const glob = require("glob");
const path = require("path");
const dayjs = require("dayjs");
const fs = require("fs-extra");
const axios = require("axios");
const openpgp = require("openpgp");
const Notifications = require("../models/Notifications.js");
const Odmdb = require("../models/Odmdb.js");

/*if (fs.existsSync("../../nationchains/tribes/conf.json")) {
  conf = require("../../nationchains/tribes/conf.json");
}*/
const conf = require(`${process.env.dirtown}/conf.json`);

/**
 * Pagan Management numeric Identity and Person  (Person = Pagan Id + tribe)
 *
 *
 *
 */
const Pagans = {};

/**
 *  Remove authentification token after a logout
 * @param {string} alias
 * @param {string} tribe
 * @param {integer} xdays
 * @param {string} xhash
 * @returns {status:200, ref:"Pagans",msg:"logout"}
 * tmpfs name file has to be on line with the tmpfs create by isAuthenticated
 *  tmpfs contain profils name for a tribe/
 */
Pagans.logout = (alias, tribe, xdays, xhash) => {
  //console.log(alias, tribe, xdays, xhash);
  // inline with middleware isAuthenticated.js
  let tmpfs = `${process.env.dirtown}/tmp/tokens/${alias}_${tribe}_${xdays}`;
  //max filename in ext4: 255 characters
  tmpfs += `_${xhash.substring(150, 150 + tmpfs.length - 249)}.json`;
  fs.remove(tmpfs);
  return { status: 200, ref: "Pagans", msg: "logout" };
};

/**
 * @param {string} alias a alias that exist or not
 * @return {object}  { status: 200, ref:"pagans",msg:"aliasexist",data: { alias, publicKey } }
 *                   { status: 404, ref:"pagans",msg:"aliasdoesnotexist",data: { alias} }
 *
 **/
Pagans.getalias = (alias) => {
  //bypass Odmdb cause all is public
  if (fs.existsSync(`${conf.dirapi}/nationchains/pagans/itm/${alias}.json`)) {
    return {
      status: 200,
      ref: "Pagans",
      msg: "aliasexist",
      data: fs.readJSONSync(
        `${conf.dirapi}/nationchains/pagans/itm/${alias}.json`
      ),
    };
  } else {
    return {
      status: 404,
      ref: "Pagans",
      msg: "aliasdoesnotexist",
      data: { alias },
    };
  }
};

/**
 * @param {string} alias  that exist
 * @param {string} tribeId that exist with a person alias
 * @return {object}  { status: 200, ref:"pagans",msg:"personexist",data: { person } }
 *                   { status: 404, ref:"pagans",msg:"persondoesnotexist",data: { person } }
 *
 **/
Pagans.getperson = (tribeid, alias, role) => {
  const objlst = Odmdb.reads(
    `${conf.dirtown}/tribes/${tribeid}/persons`,
    [alias],
    role
  );
  if (objlst.data[alias] == "notfound") {
    return {
      status: 404,
      ref: "Pagans",
      msg: "persondoesnotexist",
      data: { alias, tribeid },
    };
  } else {
    return {
      status: 200,
      ref: "Pagans",
      msg: "personexist",
      data: objlst.data[alias],
    };
  }
};

Pagans.create = (objpagan, role) => {
  /**
   * @param {object} objpagan {alias,publickey} a unique alias/publickey that identify an identity
   * @param {array} role {xalias,xprofils} requester and list of profil
   * @return {object}  { status: 200, data: { alias, publicKey } }
   *         xhash was checked  by isauthenticated
   * @todo use Odmdb to add a pagan
   */
  return Odmdb.cud(`${conf.dirapi}/nationchains/pagans`, "C", objpagan, role);
  /*
  let apxpagans = {};
  if (fs.existsSync(`${conf.dirapi}/nationchains/pagans/idx/alias_all.json`)) {
    apxpagans = fs.readJsonSync(
      `${conf.dirapi}/nationchains/pagans/idx/alias_all.json`
    );
  }
  if (apxpagans[objpagan.alias]) {
    return { status: 409, ref: "Pagans", msg: "aliasexist", data: { alias } };
  }
  apxpagans[objpagan.alias] = { alias, publicKey };
  fs.outputJsonSync(
    `${conf.dirapi}/nationchains/pagans/idx/alias_all.json`,
    apxpagans
  );
  fs.outputJsonSync(`${conf.dirapi}/nationchains/pagans/itm/${alias}.json`, {
    alias,
    publicKey,
  });
  return {
    status: 200,
    ref: "Pagans",
    msg: "identitycreate",
    data: { alias, publicKey },
  };
  */
};

/**
 * @Param {string} alias pagan unique id
 * @Param {string} tribeid tribe id in this town
 * @Param {object} persondata that respect /nationchains/schema/person.json  + nationchains/tribe/tribeid/schema/personextented.json
 * @return create or update a person /tribe/tribeid/person/alias.json
 * todo later use Odmdb ans schema person to manage this
 */
Pagans.personupdate = (tribeid, alias, personupdate, role) => {
  const personinit = {
    alias: alias,
    dt_create: dayjs(),
    profils: ["person"],
  };
  const personfile = `${process.env.dirtown}/tribes/${tribeid}/person/itm/${alias}.json`;
  const persondata = fs.existsSync(personfile)
    ? fs.readJSONSync(personfile)
    : personinit;
  persondata.dt_update = dayjs();

  Object.keys(personupdate).forEach((d) => {
    persondata[d] = personupdate[d];
  });
  //const checkjson= Checkjson.schema.data = (fs.readJsonSync(`${conf.dirapi}/nationchains/schema/person.json`, person, false)
  // if checkjson.status==200 create /update with odmdb to update index data
  // see odmdb that did all and return standard message
  fs.outputJSONSync(personfile, persondata, { space: 2 });
  return {
    status: 200,
    ref: "Pagans",
    msg: "successfullupdate",
    data: { alias: alias, tribeid: tribeid },
  };
};
/**
 * Send email with alias's keys to email or person alias person.recovery.email
 *
 * If email or pubkey is undefined then get data from tribe/person(alias)
 * Send email with keys
 *
 * @param {string} alias
 * @param {pgpPrivate} privkey
 * @param {string} passphrase
 * @param {string} tribe
 * @param {pgpPublic} pubkey
 * @param {string} email
 */
Pagans.sendmailkey = (
  alias,
  privatekey,
  tribeid,
  passphrase,
  publickey,
  email
) => {
  const person = { alias, privatekey, tribeid };
  console.log(
    alias,
    "-",
    privatekey,
    "-",
    tribeid,
    "-",
    passphrase,
    "-",
    publickey,
    "-",
    email
  );
  if (!publickey || !email || !passphrase || !privatekey) {
    const personfile = `${process.env.dirtown}/tribes/${tribeid}/person/itm/${alias}.json`;
    const persondata = fs.existsSync(personfile)
      ? fs.readJsonSync(personfile)
      : {};
    if (persondata.length == 0) {
      return {
        status: 404,
        ref: "Pagans",
        msg: "persondoesnotexist",
        data: { alias, tribeid },
      };
    }
    person.email = persondata.recoveryauth.email;
    person.publickey = persondata.recoveryauth.publickey;
    person.privatekey = persondata.recoveryauth.privatekey;
    person.passphrase = persondata.recoveryauth.passphrase;
  } else {
    person.email = email;
    person.passphrase = passphrase;
    person.publickey = publickey;
  }
  console.log("person:", person);

  //feedback.withemail = true;
  //feedback.email = email;
  //feedback.privatekey = privatekey;
  //feedback.passphrase = passphrase;
  const mailidentity = {
    subjecttpl: "Information pour l'alias: {{alias}}",
    htmltpl:
      "<h1>Votre identité {{alias}} via {{tribeid}}</h1><p>Passphrase:</p></p><p>{{{passphrase}}</p><p>Cle public:</p><p>{{{publickey}}</p><p>Cle privée</p><p>{{{privatekey}}</p>",
    texttpl:
      "Votre identité {{alias}}\nPassphrase:\n{{{passphrase}}\nCle public:\n{{{publickey}}\nCle privée\n{{{privatekey}}",
    filelist: [],
  };

  const maildata = {
    To: person.email,
    subject: Mustache.render(mailidentity.subject, person),
    htmlpart: Mustache.render(mailidentity.htmltpl, person),
    textpart: Mustache.render(mailidentity.texttpl, person),
    filelist: [],
  };
  fs.outputFileSync(
    `${conf.dirtown}/tmp/${person.alias}_privatekey.txt`,
    person.privatekey,
    "utf8"
  );
  maildata.filelist.push({
    filename: "${person.alias}_privatekey.txt",
    pathfile: `${conf.dirtown}/tmp/${person.alias}_privatekey.txt`,
  });
  fs.outputFileSync(
    `${conf.dirtown}/tmp/${person.alias}_publickey.txt`,
    person.publickey,
    "utf8"
  );
  maildata.filelist.push({
    filename: "${person.alias}_publickey.txt",
    pathfile: `${conf.dirtown}/tmp/${person.alias}_publickey.txt`,
  });
  //fs.readJSONSync('${conf.dirapi}/api/')
  return Notifications.sendmail(maildata, tribeid);
};

Pagans.authenticatedetachedSignature = async (
  alias,
  pubK,
  detachedSignature,
  message
) => {
  /**
   *  Check that a message was signed with a privateKey from a publicKey
   *  This is not necessary if isAuthenticated, but can be usefull to double check
   *  @TODO finish it and implement it also in  /apxpagan.js for browser
   *  @alias {string} alias link to the publicKey
   *  @pubK {string} publiKey text format
   *  @detachedSignature  {string} a detachedsignatured get from apx.detachedSignature
   *  @message {string} the message signed
   *  @return {boolean} true the message was signed by alias
   *                    false the message was not signed by alias
   */
  const publicKey = await openpgp.readKey({ armoredKey: pubK });
  const msg = await openpgp.createMessage({ text: message });
  const signature = await openpgp.readSignature({
    armoredSignature: detachedSignature, // parse detached signature
  });
  const verificationResult = await openpgp.verify({
    msg, // Message object
    signature,
    verificationKeys: publicKey,
  });
  const { verified, keyID } = verificationResult.signatures[0];
  try {
    await verified; // throws on invalid signature
    console.log("Signed by key id " + keyID.toHex());
    return KeyId.toHex().alias == alias;
  } catch (e) {
    console.log("Signature could not be verified: " + e.message);
    return false;
  }
};

/**
 * todo recuperer tous les tokens et les distribuer à la town
 * @param {string} alias
 */
Pagans.deletealias = (alias) => {
  // check if alias is used in the blockchain
  // if not then delete itm pagan alias => this means that alias is now available for someone else
};
Pagans.deleteperson = (alias, tribeId) => {};
Pagans.keyrecovery = (tribeid, email) => {
  glob
    .GlobSync(`${conf.dirtown}/tribes/${tribeId}/Person/*.json`)
    .forEach((f) => {
      const person = fs.readJsonSync(f);
      if (person.recoveryauth && person.recoveryauth.email) {
        // send email (alias publickey privatekey )
      }
    });
  return { status: 200, ref: "Pagans", msg: "todo" };
};
module.exports = Pagans;
