import express from "express";
import log4js from "log4js";
import { notPrompting } from "../utils.js";
import { getConfigPath, loadConfig } from "../config.js";
import { applyLogLevel } from "../logging.js";
import { getServerVersionInfo } from "../serverVersion.js";
import { startWaiting, stopWaiting } from "../spinner.js";

export const LOCAL_SERVER_PORT = 47291;

const config = loadConfig();

const logger = log4js.getLogger("Local Server");
logger.level = config.logLevel;

export class LocalServer {
  constructor(socketClient) {
    this.socketClient = socketClient;
    this.server = null;
    this.app = express();
    this.app.use(express.json());
    this.setupRoutes();
  }

  setupRoutes() {
    this.app.get("/info", (_req, res) => {
      logger.debug("Received request to check if server is running");
      res.json({
        running: true,
        authenticated: this.socketClient.authenticated,
        connected: this.socketClient.connected,
        host: this.socketClient.host,
        configPath: getConfigPath(),
        dataDir: config.dataDir,
        ...getServerVersionInfo(),
      });
    });

    this.app.post("/otpAuth", async (req, res) => {
      logger.debug("Received request to authenticate with OTP");
      const { otp } = req.body;

      if (!otp) {
        res.status(400).json({ error: "otp is required" });
        return;
      }

      try {
        startWaiting("Authenticating with OTP via local server...", logger);
        let result;
        try {
          notPrompting();
          result = await this.socketClient.authenticateWithOtp(otp, {
            retryOnFailure: false,
            logs: false,
          });
          await stopWaiting();
        } catch (err) {
          await stopWaiting();
          throw err;
        }

        if (!result.valid) {
          logger.error("OTP authentication failed:", result.error);
          res.status(401).json(result);
          return;
        }

        res.json(result);
      } catch (err) {
        logger.error("OTP authentication failed:", err);
        res.status(500).json({ error: err.message });
      }
    });

    this.app.post("/logLevel", async (req, res) => {
      logger.debug("Received request to set log level");
      const { logLevel } = req.body;

      if (!logLevel) {
        res.status(400).json({ valid: false, error: "logLevel is required" });
        return;
      }

      try {
        const result = await applyLogLevel(logLevel);

        if (!result.valid) {
          res.status(400).json(result);
          return;
        }

        logger.level = result.logLevel;
        res.json(result);
      } catch (err) {
        logger.error("Failed to set log level:", err);
        res.status(500).json({ valid: false, error: err.message });
      }
    });
  }

  start() {
    return new Promise((resolve, reject) => {
      this.server = this.app.listen(LOCAL_SERVER_PORT, "127.0.0.1", () => {
        logger.info(
          `Local server listening on http://127.0.0.1:${LOCAL_SERVER_PORT}`,
        );
        resolve(this.server);
      });

      this.server.on("error", reject);
    });
  }

  stop() {
    if (this.server) {
      this.server.close();
      this.server = null;
    }
  }
}
