import cors from "cors";
import { app } from "electron";
import log from "electron-log/main";
import express from "express";
import http from "http";
import errorTypeCheck from "../../util/errorTypeCheck";
import ImportJob from "../decoder/decoder";
import folderScan from "../decoder/folder-scan";
import { handleEMSPartsOrder } from "../ems-parts-order/ems-parts-order-handler";
import { handleShopMetaDataFetch } from "../ipc/ipcMainHandler.user";
import { handlePartsPriceChangeRequest } from "../ppc/ppc-handler";
import { handleQuickBookRequest } from "../quickbooks-desktop/quickbooks-desktop";

export default class LocalServer {
  private readonly app: express.Application;
  private server: http.Server | null;
  private PORT = 1337;

  constructor() {
    this.server = null;
    this.app = express();
    this.configureMiddleware();
    this.configureRoutes();
  }

  private configureMiddleware(): void {
    const allowedOrigins = [
      "http://localhost",
      "https://localhost",
      "http://localhost:3000",
      "https://localhost:3000",
      "https://test.imex.online",
      "https://imex.online",
      "https://test.romeonline.io",
      "https://romeonline.io",
      "https://www.test.imex.online",
      "https://www.imex.online",
      "https://www.test.romeonline.io",
      "https://www.romeonline.io",
    ];

    this.app.use(
      cors({
        origin: (origin, callback) => {
          // Allow requests with no origin (like mobile apps, curl requests)
          if (!origin) return callback(null, true);

          if (allowedOrigins.indexOf(origin) !== -1) {
            return callback(null, true);
          } else {
            return callback(null, false);
          }
        },
        credentials: true,
      }),
    );

    // Parse JSON bodies
    this.app.use(express.json());
    this.app.use(express.urlencoded());

    //Add logger Middleware
    this.app.use((req, res, next) => {
      const startTime = Date.now();
      const requestId = Math.random().toString(36).substring(2, 15);

      // Log request details
      log.info(
        `[HTTP Server] [${requestId}] Request: ${req.method} ${req.url}`,
      );
      log.info(
        `[HTTP Server] [${requestId}] Headers: ${JSON.stringify(req.headers)}`,
      );

      // Log request body if it exists
      if (req.body && Object.keys(req.body).length > 0) {
        log.info(
          `[HTTP Server] [${requestId}] Body: ${JSON.stringify(req.body)}`,
        );
      }

      // Capture the original methods
      const originalSend = res.send;
      const originalJson = res.json;

      // Override send method to log response
      res.send = function (body): express.Response {
        log.info(`[HTTP Server] [${requestId}] Response body: ${body}`);
        log.info(
          `[HTTP Server] [${requestId}] Response time: ${Date.now() - startTime}ms`,
        );
        return originalSend.call(this, body);
      };

      // Override json method to log response
      res.json = function (body): express.Response {
        log.info(
          `[HTTP Server] [${requestId}] Response body: ${JSON.stringify(body)}`,
        );
        log.info(
          `[HTTP Server] [${requestId}] Response time: ${Date.now() - startTime}ms`,
        );
        return originalJson.call(this, body);
      };

      next();
    });
  }

  private configureRoutes(): void {
    // Basic health check endpoint
    this.app.get("/health", (_req: express.Request, res: express.Response) => {
      res.status(200).json({ status: "ok" });
    });
    this.app.post("/ping", (_req, res) => {
      res.status(200).json({
        appVer: app.getVersion(),
        qbPath: app.getPath("userData"), //TODO: Resolve to actual QB file path.
      });
    });

    this.app.post("/qb", handleQuickBookRequest);
    this.app.post("/scan", async (_req, res): Promise<void> => {
      log.debug("[HTTP Server] Scan request received");
      const files = await folderScan();
      res.status(200).json(files);
      return;
    });
    this.app.post("/ppc", handlePartsPriceChangeRequest);
    this.app.post("/oec", handleEMSPartsOrder);
    this.app.post(
      "/import",
      async (req: express.Request, res: express.Response) => {
        log.debug("[HTTP Server] Import request received");
        const { filepath } = req.body;
        if (!filepath) {
          res.status(400).json({ error: "filepath is required" });
          return;
        }
        try {
          await ImportJob(filepath);
          res.status(200).json({ success: true });
        } catch (error) {
          log.error(
            "[HTTP Server] Error importing file",
            errorTypeCheck(error),
          );
          res.status(500).json({
            success: false,
            error: "Error importing file",
            ...errorTypeCheck(error),
          });
        }
      },
    );
    this.app.post(
      "/refresh",
      async (_req: express.Request, res: express.Response) => {
        log.debug("[HTTP Server] Refresh request received");
        try {
          await handleShopMetaDataFetch(true);
          res.status(200).json({ success: true });
        } catch (error) {
          log.error(
            "[HTTP Server] Error refreshing shop metadata",
            errorTypeCheck(error),
          );
          res.status(500).json({
            success: false,
            error: "Error importing file",
            ...errorTypeCheck(error),
          });
        }
      },
    );

    // Add more routes as needed
  }

  public start(): void {
    try {
      this.server = http.createServer(this.app);

      this.server.on("error", (error: NodeJS.ErrnoException) => {
        if (error.code === "EADDRINUSE") {
          log.error(
            `[HTTP Server] Port ${this.PORT} is already in use. Please use a different port.`,
          );
        } else {
          log.error(`[HTTP Server] Server error: ${error.message}`);
        }
      });

      this.server.listen(this.PORT, () => {
        log.info(
          `[HTTP Server] Local HTTP server running on port ${this.PORT}`,
        );
      });
    } catch (error: unknown) {
      log.error("[HTTP Server] Error starting server", errorTypeCheck(error));
    }
  }

  public stop(): void {
    if (this.server) {
      this.server.close();
      log.info("[HTTP Server] Local HTTP server stopped");
    }
  }
}
