const { argv } = require("process");
const fs = require("fs-extra");
const mustache = require("mustache");
const bodyParser = require("body-parser");
const glob = require("glob");
const path = require("path");
const cors = require("cors");
const express = require("express");
const process = require("process");
/*******************************************

SEE https://gitea.ndda.fr/apxtrib/apxtrib/wiki/Devrules
To have a quick understanding and convention before doing deeply in source code

To share configuration :
 process.env.dirtown  is folder where town folder name /townId-nationId is accessible
 const conf = require(`${process.env.dirtown}/conf.json`); 
 app.locals.tribeids is defined later in apixtrib.js and allow express app to always have in memory a dynamic of tribeId available in req.app.locals.tribeids
*/
/**
 * 1st install for dev
 *  run $ node apxtrib.js nationId:ants townId:devfarm dns:devfarm-ants
 *  then just yarn dev
 *   it create a folder outside ../townId-nationId/
 * To convert a dev into a chain town run again with relevant param:
 *  run $ node apxtrib.js nationId:ants townId:devfarm dns:devfarm-ants
 *  check the web interface http://dns
 *  then just yarn startpm2 your town is under PM2 control
 *
 *
 * @param {args} args key:value  example node apxtrib nationId:ants townId:devfarm dns:devfarm-ants
 *                  if no parammeter from adminapi/www/adminapx/conf/setup_xx.json
 *
 * Keyword townId = "devfarm" then this is unchain town to dev
 *                            else this is a production town ready to chain to the nationId
 *
 * @returns listen onto http:/dns (80 and 443) for admin webapp and http://localhost:initconf.api.port
 * by setting the nginx parameter
 * A folder for town data is created at the same level than apxtrib as /townId-nationId/conf.json ...
 */

// check nginx exist
if (!fs.existsSync("/etc/nginx/nginx.conf")) {
  console.log(
    "\x1b[31m Check documentation, nginx have to be installed on this server first, no /etc/nginx/nginx.conf available, install then rerun yarn command."
  );
  process.exit();
}
const param = {};
argv.slice(2).forEach((arg) => {
  const kv = arg.split(":");
  if (kv.length == 2) {
    param[kv[0]] = kv[1];
  }
});

// setup_xx.json is gitignore so at first install we are in dev configuration
let infotown = {
  nationId: "ants",
  townId: "devfarm",
  dns: "devfarm-ants",
  comment:
    "Auto generate setup from apxtrib after node apxtrib nationId:value townId:value dns:domaine_to_access",
};
if (fs.existsSync(`${__dirname}/adminapi/www/adminapx/conf/setup_xx.json`)) {
  infotown = fs.readJsonSync(
    `${__dirname}/adminapi/www/adminapx/conf/setup_xx.json`
  );
};
if (
  Object.keys(param).length > 0 &&
  param.nationId &&
  param.townId &&
  param.dns
) {
  infotown.nationId = param.nationId;
  infotown.townId = param.townId;
  infotown.dns = param.dns;
} 
fs.outputJsonSync(
      `${__dirname}/adminapi/www/adminapx/conf/setup_xx.json`,
      infotown
    );
infotown.dirapi=__dirname;
infotown.dirtown=path.resolve(`${__dirname}/../${infotown.townId}-${infotown.nationId}`);
process.env.dirtown=infotown.dirtown;

if (
  !fs.existsSync(`${infotown.dirtown}/conf.json`) ||
  !fs.existsSync(`${__dirname}/adminapi/www/nginx_adminapx.conf`)
) {
  // Case of new town or request a reset of dns to access adminapx
  // genere a minimum conf with nationId, townId, dns, dirapi, dirtown
  fs.outputJsonSync(`${infotown.dirtown}/conf.json`,infotown,{space:2});
  const Towns = require('./api/models/Towns')
  const rescreate = Towns.create();
  if (rescreate.status!=200){
    console.log('Sorry error ')
    process.exit();
  }
}
const conf = require(`${infotown.dirtown}/conf.json`);

// Create and update ./nationchains
const { updateobjectsfromfreshesttown } = require("./api/models/Nations.js");
updateobjectsfromfreshesttown(conf.towns, {
  pagans: "alias_all.json",
  towns: "townId_all.json",
  nations: "nationId_all.json",
});

// Run main express process for a /towId-nationId/tribes

let tribelist = {};
if (fs.existsSync(`${conf.dirtown}/tribes/idx/tribeId_all.json`)) {
  tribelist = fs.readJsonSync(`${conf.dirtown}/tribes/idx/tribeId_all.json`);
}
let doms = conf.dns; // only dns of town during the init process
let tribeIds = [];
let routes = glob.sync(`${conf.dirapi}/api/routes/*.js`).map((f) => {
  return { url: `/${path.basename(f, ".js")}`, route: f };
});
//routes={url,route} check how to add plugin tribe route later
// keep only the 2 last part (.) of domain name to validate cors with it (generic domain)
Object.keys(tribelist).forEach((t) => {
  tribelist[t].dns.forEach((d) => {
    const dm = d.split(".").slice(-2).join(".");
    if (!doms.includes(dm)) doms.push(dm);
  });
  tribeIds.push(t);
});
console.log("Allowed DOMs to access to this apxtrib server: ", doms);

const app = express();
// load express parameter from conf
Object.keys(conf.api.appset).forEach((p) => {
  app.set(p, conf.api.appset[p]);
});
// To set depending of data form or get size to send
app.use(bodyParser.urlencoded(conf.api.bodyparse.urlencoded));
// To set depending of post put json data size to send
app.use(express.json());
app.use(bodyParser.json(conf.api.bodyparse.json));
app.locals.tribeids = tribeIds;
console.log("app.locals.tribeids", app.locals.tribeids);
// Cors management
const corsOptions = {
  origin: (origin, callback) => {
    if (origin === undefined) {
      callback(null, true);
    } else if (origin.indexOf("chrome-extension") > -1) {
      callback(null, true);
    } else {
      //console.log( 'origin', origin )
      //marchais avant modif eslint const rematch = ( /^https?\:\/\/(.*)\:.*/g ).exec( origin )
      const rematch = /^https?:\/\/(.*):.*/g.exec(origin);
      //console.log( rematch )
      let tmp = origin.replace(/http.?:\/\//g, "").split(".");

      if (rematch && rematch.length > 1) tmp = rematch[1].split(".");
      //console.log( 'tmp', tmp )
      let dom = tmp[tmp.length - 1];
      if (tmp.length > 1) {
        dom = `${tmp[tmp.length - 2]}.${tmp[tmp.length - 1]}`;
      }
      console.log(
        `origin: ${origin}, dom:${dom}, CORS allowed? : ${doms.includes(dom)}`
      );
      if (doms.includes(dom)) {
        callback(null, true);
      } else {
        console.log(`Origin is not allowed by CORS`);
        callback(new Error("Not allowed by CORS"));
      }
    }
  },
  exposedHeaders: Object.keys(conf.api.exposedHeaders),
};
// CORS
app.use(cors(corsOptions));
// Static Routes // try to use nginx route instead in comments
/*app.use( express.static( `${__dirname}/nationchains/tribes/${conf.mayorId}/www/cdn/public`, {
  dotfiles: 'allow'
} ) );
*/
// Routers add any routes from /routes and /plugins
let logroute = "Routes available on this apxtrib instance: ";

routes.forEach((r) => {
  try {
    logroute += r.url + "|" + r.route;
    app.use(r.url, require(r.route));
  } catch (err) {
    logroute += " (err check it)";
    console.log("raise err-:", err);
  }
});
console.log(logroute);
if (infotown.townId == "devfarm") {
  console.log(
    `\x1b[42m############################################################################################\x1b[0m\n\x1b[42mThis is dev conf to switch this as production, you must run:\n 1 - 'yarn dev nationId:ants townId:usbfarm dns:usbfarm-ants ' to conf your town and check it.\n 2 - 'yarn startpm2'\n Where:\n\x1b[42m * nationId have to exist in the nationchains\n * townId new or if exist must have the smae current dns,\n * dns domaine that has to redirect 80/443 into this server (example wall-ants.ndda.fr redirect to 213.32.65.213 ).\n Check README's project to learn more.\x1b[0m\n\x1b[42m############################################################################################\x1b[0m`
  );
}
app.listen(conf.api.port, () => {
  let webaccess = `check in your browser that api works`;
  conf.dns.forEach((u) => {
    webaccess += `http://${u}:${conf.api.port}`;
  });
  console.log(webaccess);
});
console.log(
  "\x1b[42m\x1b[37m",
  "Made with love for people's freedom, enjoy !!!",
  "\x1b[0m"
);
