import express from "express";
import http from "node:http";
import path from "node:path";
import fs from "node:fs/promises";
import cookieParser from "cookie-parser";
import cors from "cors";
import { config, isDev } from "./config"
import { ensureDataPaths } from "./storage/paths"
import { initAuth } from "./auth/auth.service"
import authRoutes from "./auth/auth.routes"
import { requireAuth } from "./auth/auth.middleware"
import qbitRoutes from "./qbit/qbit.routes"
import torrentRoutes from "./torrent/torrent.routes"
import loopRoutes from "./loop/loop.routes"
import profilesRoutes from "./loop/profiles.routes"
import statusRoutes from "./status/status.routes"
import timerRoutes from "./timer/timer.routes"
import watcherRoutes from "./watcher/watcher.routes"
import { QbitClient } from "./qbit/qbit.client"
import { detectCapabilities } from "./qbit/qbit.capabilities"
import { setQbitClient, setQbitCapabilities } from "./qbit/qbit.context"
import { setQbitStatus } from "./status/status.service"
import { createSocketServer } from "./realtime/socket"
import { initEmitter, emitQbitHealth } from "./realtime/emitter"
import { startLoopScheduler } from "./loop/loop.scheduler"
import { startEnforcementWorker } from "./enforcement/enforcement.worker"
import { startTimerWorker } from "./timer/timer.worker"
import { startWatcherWorker } from "./watcher/watcher.worker"
import { logger } from "./utils/logger"

const crashLogPath = "/app/data/crash.log";

const appendCrashLog = async (label: string, detail: unknown) => {
  try {
    const payload =
      detail instanceof Error
        ? { message: detail.message, stack: detail.stack }
        : { detail };
    const line = `${new Date().toISOString()} ${label} ${JSON.stringify(payload)}\n`;
    await fs.appendFile(crashLogPath, line, "utf-8");
  } catch (error) {
    logger.error({ error }, "Failed to append crash log");
  }
};

process.on("unhandledRejection", (reason) => {
  logger.error({ reason }, "Unhandled promise rejection");
  appendCrashLog("unhandledRejection", reason);
});

process.on("uncaughtException", (error) => {
  logger.error({ error }, "Uncaught exception");
  appendCrashLog("uncaughtException", error);
});

process.on("SIGTERM", () => {
  logger.warn("Received SIGTERM, shutting down");
  appendCrashLog("SIGTERM", { pid: process.pid });
});

process.on("SIGINT", () => {
  logger.warn("Received SIGINT, shutting down");
  appendCrashLog("SIGINT", { pid: process.pid });
});

let serverStarted = false;

const bootstrap = async () => {
  await ensureDataPaths();
  await initAuth();

  const app = express();
  app.use(cookieParser());
  app.use(express.json());

  if (isDev) {
    const fallbackOrigin = `http://localhost:${config.webPort}`;
    const originList = [config.webOrigin || fallbackOrigin, fallbackOrigin];
    const allowed = config.webAllowedOrigins
      .split(",")
      .map((origin) => origin.trim())
      .filter(Boolean);
    const origins = [...originList, ...allowed];
    app.use(
      cors({
        origin: origins,
        credentials: true,
      })
    );
  }

  app.use("/api/auth", authRoutes);
  app.use("/api/qbit", requireAuth, qbitRoutes);
  app.use("/api/torrent", requireAuth, torrentRoutes);
  app.use("/api/loop", requireAuth, loopRoutes);
  app.use("/api/profiles", requireAuth, profilesRoutes);
  app.use("/api/status", requireAuth, statusRoutes);
  app.use("/api/timer", requireAuth, timerRoutes);
  app.use("/api/watchers", requireAuth, watcherRoutes);

  if (!isDev) {
    app.use(express.static(config.webPublicDir));
    app.get("*", (req, res, next) => {
      if (req.path.startsWith("/api")) {
        return next();
      }
      return res.sendFile(path.join(config.webPublicDir, "index.html"));
    });
  }

  const server = http.createServer(app);
  const io = createSocketServer(server);
  initEmitter(io);

  const qbit = new QbitClient();
  setQbitClient(qbit);

  try {
    const caps = await detectCapabilities(qbit);
    setQbitCapabilities(caps);
    setQbitStatus({ ok: true, version: caps.version, capabilities: caps });
    emitQbitHealth({ ok: true, version: caps.version, capabilities: caps });
  } catch (error) {
    logger.error({ error }, "Failed to connect to qBittorrent");
    setQbitStatus({ ok: false, lastError: (error as Error).message });
    emitQbitHealth({ ok: false, lastError: (error as Error).message });
  }

  startLoopScheduler(qbit, config.pollIntervalMs);
  startEnforcementWorker(config.enforceIntervalMs);
  startTimerWorker(qbit, config.timerPollMs);
  startWatcherWorker(config.watcherTickMs);

  server.listen(config.port, () => {
    serverStarted = true;
    logger.info(`q-buffer server listening on ${config.port}`);
  });
};

const startWithRetry = () => {
  bootstrap().catch((error) => {
    logger.error({ error }, "Failed to start server");
    if (!serverStarted) {
      setTimeout(startWithRetry, 5000);
    }
  });
};

startWithRetry();
