import { WebConfig } from "./config";
import * as express from "express";
import { Express } from "express";

import Logging from "@hibas123/nodelogging";

import * as bodyparser from "body-parser";
import * as cookieparser from "cookie-parser";

import * as i18n from "i18n";
import * as compression from "compression";
import ApiRouter from "./api";
import ViewRouter from "./views/views";
import RequestError, { HttpStatusCode } from "./helper/request_error";

export default class Web {
   server: Express;
   private port: number;

   constructor(config: WebConfig) {
      this.server = express();
      this.port = Number(config.port);
      this.registerMiddleware();
      this.registerEndpoints();
      this.registerErrorHandler();
   }

   listen() {
      this.server.listen(this.port, () => {
         Logging.log(`Server listening on port ${this.port}`);
      });
   }

   private registerMiddleware() {
      this.server.use(cookieparser());
      this.server.use(
         bodyparser.json(),
         bodyparser.urlencoded({ extended: true })
      );
      this.server.use(i18n.init);

      //Logging Middleware
      this.server.use((req, res, next) => {
         let start = process.hrtime();
         let finished = false;
         let to = false;
         let listener = () => {
            if (finished) return;
            finished = true;
            let td = process.hrtime(start);
            let time = !to ? (td[0] * 1e3 + td[1] / 1e6).toFixed(2) : "--.--";
            let resColor = "";
            if (res.statusCode >= 200 && res.statusCode < 300)
               resColor = "\x1b[32m";
            //Green
            else if (res.statusCode === 304 || res.statusCode === 302)
               resColor = "\x1b[33m";
            else if (res.statusCode >= 400 && res.statusCode < 500)
               resColor = "\x1b[36m";
            //Cyan
            else if (res.statusCode >= 500 && res.statusCode < 600)
               resColor = "\x1b[31m"; //Red
            let m = req.method;
            while (m.length < 4) m += " ";
            Logging.log(
               `${m} ${req.originalUrl} ${(req as any).language || ""
               } ${resColor}${res.statusCode}\x1b[0m - ${time}ms`
            );
            res.removeListener("finish", listener);
         };
         res.on("finish", listener);
         setTimeout(() => {
            to = true;
            listener();
         }, 2000);
         next();
      });

      this.server.use(
         compression({
            filter: (req, res) => {
               if (req.headers["x-no-compression"]) {
                  return false;
               }
               return compression.filter(req, res);
            },
         })
      );
   }

   private registerEndpoints() {
      this.server.use("/api", ApiRouter);
      this.server.use("/", ViewRouter);
   }

   private registerErrorHandler() {
      this.server.use((error, req: express.Request, res, next) => {
         if (!(error instanceof RequestError)) {
            error = new RequestError(
               error.message,
               error.status || HttpStatusCode.INTERNAL_SERVER_ERROR,
               error.nolog || false
            );
         }

         if (error.status === 500 && !(<any>error).nolog) {
            Logging.error(error);
         } else {
            Logging.log("Responded with Error", error.status, error.message);
         }

         if (req.accepts(["json"])) {
            res.json_status = error.status || 500;
            res.json({
               error: error.message,
               status: error.status || 500,
               additional: error.additional,
            });
         } else res.status(error.status || 500).send(error.message);
      });
   }
}
