// Imports
const express = require("express");
const fs = require("fs");
const Eta = require("eta");
const winston = require("winston");
const mysql = require("mysql");
const bodyParser = require("body-parser");
const session = require("express-session");
const nodemailer = require("nodemailer");

// Inting the logger
const logger = winston.createLogger({
  level: "debug",
  format: winston.format.json(),
  defaultMeta: { service: "user-service" },
  transports: [
    //
    // - Write all logs with level `error` and below to `error.log`
    // - Write all logs with level `info` and below to `combined.log`
    //
    new winston.transports.File({ filename: "error.log", level: "error" }),
    new winston.transports.File({ filename: "combined.log" }),
  ],
});

logger.add(
  new winston.transports.Console({
    format: winston.format.simple(),
  })
);

const app = express();

const startUpTime = Math.floor(new Date().getTime() / 1000);

// Skeleton Variables
let fontawesomeKey = "";
let mapboxAccessToken = "";
let mysqlData = { "user": "", "password": "", "database": "", "allowCreation": false };
let mailConf = { "host": "", "port": 0, "username": "", "password": "" };
let serverAdress = "";
let cookieSecret = ""
let jsonConfigGlobal = {};
let port = 3000;

// Load config
try {
  const data = fs.readFileSync("config/default.json", "utf8");
  const jsonContent = JSON.parse(data);
  let jsonConfig = jsonContent;
  if (jsonContent.redirectConfig) {
    const data = fs.readFileSync(
      "config/" + jsonContent.redirectConfig,
      "utf8"
    );
    jsonConfig = JSON.parse(data);
  }
  fontawesomeKey = jsonConfig.fontAwesome;
  mapboxAccessToken = jsonConfig.mapboxAccessToken;
  mysqlData = jsonConfig.mysql;

  mailConf = jsonConfig.mail;
  serverAdress = jsonConfig.serverAdress;
  port = jsonConfig.port;
  cookieSecret =
    jsonConfig.cookieSecret || "saF0DSF65AS4DF0S4D6F0S54DF0Fad";
  jsonConfigGlobal = jsonConfig;
} catch (error) {
  logger.error(
    "While reading the config an error occured. The error was: " + error
  );
}

// Express (server) preperation
app.use(express.static("static"));

app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
app.use(session({ secret: cookieSecret }));
app.use(function (req, res, next) {
  // PreShow Errorpage handler
  const pathesWhichRequireDB = ["map", "login", "register"];
  const pathesWhichRequireLogin = ["createElevator"];
  const path = req.path
  const pathesDes = path.split("/")
  let requiresDB = false;
  let requiresLogin = false;
  let allowContinue = true;
  console.log(pathesDes)

  if (pathesWhichRequireLogin.indexOf(pathesDes[1]) > -1) {
    requiresLogin = true;
  }

  if (pathesDes[1] == "api") {
    requiresDB = true;
  }
  if (pathesWhichRequireDB.indexOf(pathesDes[1]) > -1) {
    requiresDB = true;
  }

  if (requiresDB) {
    if (!mysqlIsUpAndOkay) {
      allowContinue = false;
      const data = fs.readFileSync("templates/dbError.html", "utf8");
      let displayText =
        "This might be an artifact of a recent restart. Maybe wait a few minutes and reload this page.";
      if (startUpTime + 60 <= Math.floor(new Date().getTime() / 1000)) {
        displayText =
          "The server failed to connect to the MySQL server. This means it was unable to load any data.";
      }
      if (mySQLstate == 1) {
        displayText =
          "There is a problem with the database servers setup. Please check the log for more info.";
      }

      res.send(
        Eta.render(data, {
          author: metainfo.author,
          desc: metainfo.desc,
          siteTitel: metainfo.sitePrefix + "Error",
          fontawesomeKey: fontawesomeKey,
          displayText: displayText,
        })
      );
    }
  }

  if (requiresLogin) {
    if (req.session.username == undefined) {
      allowContinue = false;
      const data = fs.readFileSync("templates/redirect.html", "utf8");
      res.send(
        Eta.render(data, {
          author: metainfo.author,
          desc: metainfo.desc,
          siteTitel: metainfo.sitePrefix + "Redirect",
          fontawesomeKey: fontawesomeKey,
          url: "/login?r=" + path,
        })
      );
    }
  }

  console.log('Time:', Date.now())
  if (allowContinue) {
    next()
  } else {
    console.log("Stopped further exec of route")
  }
})

// Mail preperation
const transport = nodemailer.createTransport({
  host: mailConf.host,
  port: mailConf.port,
  requireTLS: true,
  secure: false,
  debug: true,
  disableFileAccess: true,
  auth: {
    user: mailConf.username,
    pass: mailConf.password,
  },
});

logger.info("Testing SMTP connection");
transport.verify(function (error) {
  if (error) {
    logger.error(error);
  } else {
    logger.info("SMPT server is ready to accept messages");
  }
});

// Basic defines for html
const metainfo = {
  author: "TheGreydiamond",
  desc: "The Elevatormap. A map for elevator spotters!",
  sitePrefix: "Elevatormap - "
}

let mysqlIsUpAndOkay = false;
let mySQLstate = 0; // 0 -> Default failure   1 -> Missing strucutre



// Prepare MYSQL
let con = mysql.createConnection({
  host: "localhost",
  user: mysqlData.user,
  password: mysqlData.password,
  database: mysqlData.database,
});

function checkIfMySQLStructureIsReady() {
  if (mysqlIsUpAndOkay) {
    // Only if MySQL is ready
    logger.debug("Checking MySQL strucutre");
    con.query("SHOW TABLES;", function (err, result, fields) {
      if (err) throw err;
      if (result.length == 0) {
        // There are no tables. Not good.
        logger.warn("There are no tables found");
        if (mysqlData.allowCreation) {
          // Lets create it then
          logger.warn("Creating a new table");
          const sql =
            "CREATE TABLE `" +
            mysqlData.database +
            "`.`elevators` ( `id` INT NOT NULL AUTO_INCREMENT , `lat` FLOAT NOT NULL , `lng` FLOAT NOT NULL , `manufacturer` VARCHAR(512) NOT NULL , `modell` VARCHAR(512) NOT NULL , `info` VARCHAR(512) NOT NULL , `visitabilty` INT NOT NULL , `technology` INT NOT NULL , `images` JSON NOT NULL , `amountOfFloors` INT NOT NULL , `maxPassangers` INT NOT NULL , `maxWeight` INT NOT NULL , `creator` INT NOT NULL, PRIMARY KEY (`id`)) ENGINE = InnoDB;";
          const newSql =
            "CREATE TABLE `" +
            mysqlData.database +
            "`.`users` ( `id` INT NOT NULL AUTO_INCREMENT , `email` VARCHAR(255) NOT NULL , `username` VARCHAR(255) NOT NULL , `passwordHash` VARCHAR(512) NOT NULL , `permLevel` INT NOT NULL DEFAULT '0' , `verificationState` INT NOT NULL DEFAULT '0' , PRIMARY KEY (`id`), UNIQUE KEY (`email`)) ENGINE = InnoDB;";
          const newSqlMailVeri =
            "CREATE TABLE `" +
            mysqlData.database +
            "`.`mailverification` ( `id` INT NOT NULL AUTO_INCREMENT , `targetMail` VARCHAR(512) NOT NULL , `userID` INT NOT NULL , `token` VARCHAR(255) NOT NULL , PRIMARY KEY (`id`)) ENGINE = InnoDB;";
          con.query(sql, function (err, result) {
            if (err) throw err;
            logger.info("Table created");
          });

          con.query(newSql, function (err, result) {
            if (err) throw err;
            logger.info("Usertable created");
          });

          con.query(newSqlMailVeri, function (err, result) {
            if (err) throw err;
            logger.info("Email verification table created");
          });
        } else {
          // We cannot do that. Welp.
          logger.warn(
            "MySQL tables are missing and the config denies creation of new ones."
          );
          mysqlIsUpAndOkay = false;
          mySQLstate = 1;
        }
      }
    });
  } else {
    logger.warn("Tried checking the tables even though MySQL wasn't ready.");
  }
}

con.connect(function (err) {
  if (err) {
    mysqlIsUpAndOkay = false;
    logger.error("Connction to MySQL failed");
    console.log(err);
  } else {
    logger.info("Mysql is ready.");
    mysqlIsUpAndOkay = true;
    checkIfMySQLStructureIsReady();
  }
});

// Routes
app.get("/", function (req, res) { // Index page
  const data = fs.readFileSync("templates/index.html", "utf8");
  res.send(
    Eta.render(data, {
      author: metainfo.author,
      desc: metainfo.desc,
      siteTitel: metainfo.sitePrefix + "Start",
      fontawesomeKey: fontawesomeKey,
    })
  );
});

app.get("/map", function (req, res) { // Map page showing all elevators
  const data = fs.readFileSync("templates/map.html", "utf8");
  res.send(
    Eta.render(data, {
      author: metainfo.author,
      desc: metainfo.desc,
      siteTitel: metainfo.sitePrefix + "Map",
      fontawesomeKey: fontawesomeKey,
      mapboxAccessToken: mapboxAccessToken,
    })
  )

});

app.get("/createElevator", function (req, res) { // Page to create a new elvator
  const data = fs.readFileSync("templates/createElevator.html", "utf8");
  res.send(
    Eta.render(data, {
      author: metainfo.author,
      desc: metainfo.desc,
      siteTitel: metainfo.sitePrefix + "New elevator",
      fontawesomeKey: fontawesomeKey,
      mapboxAccessToken: mapboxAccessToken,
    })
  );
});

require('./routes/api.route.ts')(app, con, mysqlIsUpAndOkay, logger, metainfo);
require('./routes/debug.route.ts')(app, con, logger, metainfo);
require('./routes/auth.route.ts')(app, con, logger, metainfo, jsonConfigGlobal);

// Some loops for handeling stuff, 
setInterval(() => {
  if (mysqlIsUpAndOkay == false) { // SQL reconnect
    logger.warn("Retrying to connect to MySQL");
    con = mysql.createConnection({
      host: "localhost",
      user: mysqlData.user,
      password: mysqlData.password,
      database: mysqlData.database,
    });

    con.connect(function (err) {
      if (err) {
        mysqlIsUpAndOkay = false;
        logger.error("Connction to MySQL failed");
        console.log(err);
      } else {
        logger.info("Mysql is ready.");
        mysqlIsUpAndOkay = true;
      }
    });
  }
}, 60*1000); // Every minute

// App start
app.listen(port, () => {
  logger.info(`Elevator map ready at http://localhost:${port}`);
});
