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.chaintown = (nationId, townId) => {
  /**
   * if not already exist Add a requested town into conf.towns.push({ "townId": "wall", "nationId": "ants", "dns": "wall-ants.ndda.fr" })
   */
};

Nations.updateobjectsfromfreshesttown = (dnstownlist, objectidx) => {
  /**
   * Get lasttime update per apxtrib object then choose the latest source and update local town
   * if an item exist localy and does not from the town requested
   * @Param {array} dnstownlist list of dns to get latest data
   * @Param {object} objectidx  objectnme:idxfile {agans:"alias_all.json",...}
   * @return create/update nationchains/pagans town nation
   */
  const localversion = {};
  const objlist = Object.keys(objectidx);
  objlist.forEach((o) => {
    let objconf = {
      name: o,
      schema: `adminapi/schema/${o}.jsons`,
      lastupdate: -1,
    };
    if (fs.existsSync(`${conf.dirapi}/nationchains/${o}/conf.json`)) {
      objconf = fs.readJsonSync(`${conf.dirapi}/nationchains/${o}/conf.json`);
    } else {
      fs.outputJsonSync(`${conf.dirapi}/nationchains/${o}/conf.json`, objconf);
    }
    localversion[o] = [conf.dns[0], objconf.lastupdate];
  });
  //console.log(localversion);
  for (let t = 0; t < dnstownlist.length; t++) {
    if (conf.townId != dnstownlist[t].townId) { // to avoid update itself
      let promiseconf = [];
      let objecttotest = [];
      objlist.forEach((o) => {
        //console.log(`https://${dnstownlist[t].dns}/nationchains/${o}/conf.json`);
        objecttotest.push(o);
        promiseconf.push(
          axios.get(`https://${dnstownlist[t].dns}/nationchains/${o}/conf.json`)
        );
      });
      Promise.all(promiseconf)
        .then((reps) => {
          let promiseidx = [];
          let objecttoupdate = [];
          let objlastupdate = [];
          for (let i = 0; i < objecttotest.length; i++) {
            if (
              parseInt(reps[i].data.lastupdate) >
              parseInt(localversion[reps[i].data.name][1])
            ) {
              // add promise to get data
              /*console.log(
              `https://${dnstownlist[t].dns}/nationchains/${
                reps[i].data.name
              }/idx/${objectidx[reps[i].data.name]}`
            );*/
              objecttoupdate.push(objecttotest[i]);
              objlastupdate.push(reps[i].data.lastupdate);
              promiseidx.push(
                axios.get(
                  `https://${dnstownlist[t].dns}/nationchains/${
                    reps[i].data.name
                  }/idx/${objectidx[reps[i].data.name]}`
                )
              );
            }
          }
          Promise.all(promiseidx)
            .then((rets) => {
              for (let j = 0; j < objecttoupdate.length; j++) {
                Odmdb.updatefromidxall(
                  objecttoupdate[j],
                  objectidx[objecttoupdate[j]],
                  rets[j].data,
                  objlastupdate[j]
                );
              }
            })
            .catch((err) => {
              console.log("ERR get idx data");
              console.log(err);
            });
        })
        .catch((err) => {
          console.log("ERR get conf lastupdate");
          console.log(err);
        });
    }
  }
};
Nations.synchronizeold = () => {
  /*
	    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;
