const bcrypt = require("bcrypt");
const fs = require("fs-extra");
const glob = require("glob");
const jwt = require("jwt-simple");
const axios = require("axios");
const path=require('path');
const conf=require(`${process.env.dirtown}/conf.json`);
const Odmdb = require('./Odmdb.js');
// lowercase 1st letter is normal
const towns = require('./Towns.js');
const pagans = require('./Pagans.js');
/*
Blockchain manager
* Manage network directory of nations and towns
* read Blockchain and search,
* submit a transaction (now) or contract (futur) to store from userA.pubkey to userB.pubkey a number of AXESS
* mine to be able to register a block and create AXESS
* manage APIXP rules 20 M APIXP 1AXESS = 1 block validation
* manage contract = action if something appened validate by a proof of work
*/
const Nations = {};
Nations.init = () => {
  console.group("init Nations");
};

Nations.updateChains = async (newtown) =>{
  /**
   * @newtown {object} optional to request a registration in the nationchain network
   * if newtown exist then it send a request to update itself else it just refresh from existing town.
   * Check public nationchains are up to date from the existing list of towns
   * Each object to sync have a /idx/conf.json with key lastupdate = timestamp last update 
   * tribes is not synchonized and contain private information
   * A town is a network node of the nationchains and allow to synchronize new 
   */
  const res= {status:400};
  const ref2update={}
  glob.sync(`${__dirapi}nationchains/**/idx/conf.json`).forEach(f=>{
    const ref=fs.readJsonSync(f)
    ref2update[path.basename(ref.schema,'.json')]=ref.lastupdate;
  })
  console.log(ref2update)
  // Get list of town to check n of them have fresh update 
  const knowntowns =fs.readJsonSync(`${__dirapi}nationchains/towns/idx/towns_townId_all.json`);
  let promiselistblock=[]
  let towidlist=[]
  Object.keys(knowntowns).forEach(townid=>{
    // identify the town with the highest block to update town
      promiselistblock.push(axios.get(`https://${knowntowns[townid].url}/blocks/idx/conf.json`));
      townidlistblock.push(townid)
  });    
  let selectedtown=""
  let blocnum=0
  await Promise.all(promiselistblock)      
      .then(rep=>{
         for (let pos=0;pos<townidlist.length;pos++){
            if (rep[pos].blocnum > blocnum) {
              selectedtown=townidlist[pos]
              blocnum=rep[pos].blocnum
            }
         }
      })
      .catch(err=>{
         console.log(err)
      })
  let promiselistref=[]
  Object.keys(ref2update).forEach(ob=>{
    promiselistref.push(axios.get(`${knowntowns[selectedtown].url}/${obj}/idx/conf.json`));
  })
  await Promise.all(promiselistref)
    .then(rep=>{
      for (let pos=0;pos<townidlist.length;pos++){
        //si axios lastupdate > local lastupate => recupe _all et regenere tous les objets par ecrasement 
      }
    })
    .catch(err=>{
      console.log(err)
    })
 return res
}

Nations.update=(nationsource)=>{
/**
 * Update object nation with last update
 */
}

Nations.synchronize = () => {
  /*
	    Run process to communicate with a list of towns to update network and transaction
	*/
  //update himself then send to other information
  if (process.env.NODE_ENV != "prod") {
    // Not concerned
    return {};
  }
  const initcurrentinstance = {
    fixedIP: "",
    lastblocknumber: 0,
    firsttimeupdate: 0,
    lastimeupdate: 0,
    positifupdate: 0,
    negatifupdate: 0,
    pubkeyadmin: "",
    tribeids: [],
    logins: [],
    knowninstance: [],
  };
  let currentinstance = initcurrentinstance;
  try {
    currentinstance = fs.readFileSync(
      `${conf.tribes}/${conf.mayorId}/nationchains/nodes/${conf.rootURL}`,
      "utf-8"
    );
  } catch (err) {
    console.log("first init");
  }
  const loginsglob = fs.readJsonSync(`${conf.tmp}/loginsglob.json`, "utf-8");
  currentinstance.logins = Object.keys(loginsglob);
  currentinstance.tribeids = [...new Set(Object.values(loginsglob))];
  currentinstance.instanceknown = glob.Sync(
    `${conf.tribes}/${conf.mayorId}/nationchains/nodes/*`
  );
  //Save it
  fs.outputJsonSync(
    `${conf.tribes}/${conf.mayorId}/nationchains/nodes/${conf.rootURL}`,
    currentinstance
  );
  // proof of work
  // try to find a key based on last block with difficulty
  // if find then send to all for update and try to get token
  // in any case rerun Nations.synchronize()
  currentinstance.instanceknown.forEach((u) => {
    if (u != conf.rootURL) {
      //send currentinstance info and get back state of
      axios
        .post(`https://${u}/nationchains/push`, currentinstance)
        .then((rep) => {
          newdata = rep.payload.moreinfo;
          //Available update info
          fs.readJson(
            `${conf.tribes}/${conf.mayorId}/nationchains/nodes/${u}`,
            (err, data) => {
              if (err) {
                data.negatifupdate += 1;
                data.lasttimeupdate = Date.now();
              } else {
                data.positifupdate += 1;
                data.lastimeupdate = Date.now();
                data.tribeids = newdata.tribeids;
                data.logins = newdata.logins;
                data.lastblocknumber = newdata.lastblocknumber;
                newdata.knowninstance.forEach((k) => {
                  if (!data.knowninstance.includes(k)) {
                    data.knowninstance.push(k);
                    //init the domain for next update
                    initcurrentinstance.firsttimeupdate = Date.now();
                    fs.outputJson(
                      `${conf.tribes}/${conf.mayorId}/nationchains/nodes/${k}`,
                      initcurrentinstance,
                      "utf-8"
                    );
                  }
                });
              }
              //save with info
              fs.outputJson(
                `${conf.tribes}/${conf.mayorId}/nationchains/nodes/${u}`,
                data
              );
            }
          );
        })
        .catch((err) => {
          //Not available
          data.negatifupdate += 1;
          data.lasttimeupdate = Date.now();
          fs.outputJson(
            `${conf.tribes}/${conf.mayorId}/nationchains/nodes/${u}`,
            data
          );
        });
    }
  });
};

Nations.create = (conf) => {
  /*
	@conf from a nationchains/socialworld/setup/townSetup {object, nationId, townId, dns}
    @return 
	*/
  const res = {};
  if (conf.object=="towns"){
    Odmdb.create("nationchains/socialworld/objects","towns",conf);
  }
  const nations = fs.readJsonSync(
    "./nationchains/nations/idx/nationId_all.json"
  );
  if (!ObjectKeys(nations).includes(conf.nationId)) {
    res.status = 404;
    res.info = `your nationId ${conf.nationId} does not exist you have to choose an existing one`;
    return res;
  }
  const towns = fs.readJsonSync( "./nationchains/towns/idx/townId_all.json")
  if (towns[conf.nationId].includes(conf.townId)) {
    res.status = 409;
    res.info = `This conf.townId already exist you have to find a unique town name`;
    return res;
  }
  const towndata = {
    uuid: conf.townId,
    nationid: conf.nationId,
    url: `${conf.townId}.${conf.nationId}.${conf.dns}`,
    status: (conf.dns=="unchain")? "unchain" : "tochain",
  };
  const metatown=fs.readJsonSync('./nationchains/socialworld/metaobject/towns.json');
  Odmdb.add(objectpath, towns, metatown,towndata) 
  
  fs.outputJsonSync(
    `./nationchains/socialworld/objects/towns/${townId}.json`,
    towndata
  );
  res.status=200
  res.info=`${townId} create for ${nationId} nation`;
  return res
};

module.exports = Nations;
