/**
 * Unified server: SAT + ORAC Risk Assessment + SWI on one port.
 * - SAT APIs: /api/sat/*
 * - ORAC APIs: /api/*, static /uploads
 * - SWI APIs: /api/swi/*
 *
 * Run from repo root: npm install && npm start
 * Port: PORT or APP_PORT, default 5012 (5000–5011 are often reserved on Windows/Hyper-V).
 * Unified HSE API: SAT + ORAC + SWI + PTW + Master Dashboard on one port.
 */

import express from "express";
import http from "http";
import helmet from "helmet";
import morgan from "morgan";
import session from "express-session";
import dotenv from "dotenv";
import path from "path";
import { fileURLToPath, pathToFileURL } from "url";
import { createRequire } from "module";

import apiRouter from "./Risk_assessment_backend/src/routes/index.js";
import swiApiRouter from "./swi_backend/src/routes/index.js";
import ptwApiRouter from "./ptw_backend/src/routes/index.js";
import masterApiRouter from "./master_dashboard_backend/src/routes/index.js";
import { login as masterPasswordLogin } from "./master_dashboard_backend/src/controllers/authController.js";
import passport from "./master_dashboard_backend/src/config/passport.js";
import {
  notFound,
  errorHandler as oracErrorHandler,
} from "./Risk_assessment_backend/src/middlewares/errorHandler.js";
import { errorHandler as swiErrorHandler } from "./swi_backend/src/middlewares/errorHandler.js";
import { errorHandler as ptwErrorHandler } from "./ptw_backend/src/middlewares/errorHandler.js";
import { errorHandler as masterErrorHandler } from "./master_dashboard_backend/src/middlewares/errorHandler.js";
import { ApiError } from "./Risk_assessment_backend/src/utils/ApiError.js";
import { ApiError as SwiApiError } from "./swi_backend/src/utils/ApiError.js";
import { ApiError as PtwApiError } from "./ptw_backend/src/utils/ApiError.js";
import { ApiError as MasterApiError } from "./master_dashboard_backend/src/utils/ApiError.js";
import { createSessionExchangeHandler, JWT_AUD } from "./shared/hse-jwt/index.js";

const __dirname = path.dirname(fileURLToPath(import.meta.url));

dotenv.config({ path: path.join(__dirname, ".env") });
dotenv.config({ path: path.join(__dirname, "SAT_backend", ".env") });
dotenv.config({ path: path.join(__dirname, "Risk_assessment_backend", ".env") });
dotenv.config({ path: path.join(__dirname, "swi_backend", ".env") });
dotenv.config({ path: path.join(__dirname, "ptw_backend", ".env") });
dotenv.config({ path: path.join(__dirname, "master_dashboard_backend", ".env") });

// Resolve SAT modules (CJS) from SAT_backend tree so relative paths in those files work.
const requireSat = createRequire(
  path.join(__dirname, "SAT_backend", "package.json")
);

const PORT = Number(process.env.APP_PORT || process.env.PORT || 5012);
const API_PREFIX = "/api/sat";
const MASTER_API_PREFIX = "/api/master";
const SWI_API_PREFIX = "/api/swi";
const PTW_API_PREFIX = "/api/ptw";
const FRONTEND_URL = (
  process.env.MASTER_DASHBOARD_URL ||
  process.env.FRONTEND_URL ||
  "http://localhost:5174"
).replace(/\/+$/, "");

export function createUnifiedApp() {
  const app = express();

if (process.env.NODE_ENV === "development") {
  app.use((req, res, next) => {
    console.log("[SERVER]", req.method, req.originalUrl || req.url);
    next();
  });
}

app.use(
  helmet({
    crossOriginResourcePolicy: { policy: "cross-origin" },
  })
);

const jsonBodyParser = express.json({ limit: "50mb" });
app.use((req, res, next) => {
  const contentType = String(req.headers["content-type"] || "").toLowerCase();
  if (contentType.includes("multipart/form-data")) {
    return next();
  }
  if (req.method === "GET" || req.method === "HEAD" || req.method === "OPTIONS") {
    return next();
  }
  return jsonBodyParser(req, res, next);
});
app.use(express.urlencoded({ extended: true, limit: "50mb" }));

const masterOrigin =
  process.env.MASTER_DASHBOARD_URL ||
  process.env.FRONTEND_URL ||
  "http://localhost:5174";
const oracOrigins = (
  process.env.ALLOWED_ORIGINS ||
  [
    "http://127.0.0.1:5500",
    "http://localhost:5500",
    "http://localhost:3000",
    "http://127.0.0.1:3000",
    "http://localhost:5173",
    "http://127.0.0.1:5173",
    "http://localhost:5174",
    "http://127.0.0.1:5174",
    "http://localhost:5175",
    "http://127.0.0.1:5175",
    "http://localhost:5176",
    "http://127.0.0.1:5176",
  ].join(",")
)
  .split(",")
  .map((o) => o.trim())
  .filter(Boolean);

const corsOriginSet = new Set([masterOrigin, ...oracOrigins]);

app.use((req, res, next) => {
  const origin = req.headers.origin;
  if (origin && corsOriginSet.has(origin)) {
    res.header("Access-Control-Allow-Origin", origin);
  }
  res.header("Vary", "Origin");
  res.header("Access-Control-Allow-Methods", "GET,POST,PUT,DELETE,OPTIONS");
  res.header("Access-Control-Allow-Headers", "Content-Type, Authorization");
  res.header("Access-Control-Allow-Credentials", "true");
  if (req.method === "OPTIONS") {
    return res.sendStatus(204);
  }
  next();
});

if (process.env.NODE_ENV === "development") {
  app.use(morgan("dev"));
}

app.use(
  session({
    secret:
      process.env.SESSION_SECRET ||
      process.env.JWT_SECRET ||
      "your-session-secret",
    resave: false,
    saveUninitialized: false,
    cookie: {
      secure: process.env.NODE_ENV === "production",
      httpOnly: true,
      maxAge: 24 * 60 * 60 * 1000,
    },
  })
);
app.use(passport.initialize());
app.use(passport.session());

/** Login UI is on the Master Dashboard SPA — not this API process. */
app.get("/login", (req, res) => {
  const returnPath = req.query.next ?? req.query.returnUrl ?? req.query.redirect;
  const qs =
    returnPath != null && String(returnPath).trim() !== ""
      ? `?next=${encodeURIComponent(String(returnPath))}`
      : "";
  res.redirect(302, `${FRONTEND_URL}/login${qs}`);
});

/** Browsers often open the API port directly — redirect to the SPA or return API metadata. */
app.get("/", (req, res) => {
  const accept = String(req.headers.accept || "");
  if (accept.includes("text/html")) {
    return res.redirect(302, FRONTEND_URL);
  }
  return res.status(200).json({
    message: "EHS unified API",
    frontend: FRONTEND_URL,
    routes: {
      master: `${MASTER_API_PREFIX}/*`,
      sat: `${API_PREFIX}/*`,
      orac: "/api/*",
      swi: `${SWI_API_PREFIX}/*`,
      ptw: `${PTW_API_PREFIX}/*`,
    },
  });
});

app.get("/favicon.ico", (_req, res) => res.sendStatus(204));

function deprecatedLoginAlias(req, res, next) {
  if (process.env.NODE_ENV !== "test") {
    console.warn(
      "[DEPRECATED] Use POST /api/master/auth/login instead of",
      req.originalUrl || req.url
    );
  }
  return masterPasswordLogin(req, res, next);
}

/** Back-compat aliases — delegate to master auth. */
app.post("/login", deprecatedLoginAlias);
app.post(`${API_PREFIX}/login`, deprecatedLoginAlias);

app.post(
  `${API_PREFIX}/auth/session`,
  createSessionExchangeHandler(JWT_AUD.SAT)
);

app.use(API_PREFIX, (req, res, next) => {
  console.log("[SAT API]", req.method, req.originalUrl || req.url);
  next();
});

app.use(`${API_PREFIX}/health`, (req, res) => {
  return res.status(200).json({
    message: "Backend running successfully 🚀",
  });
});

const accountStatusRoutes = requireSat("./src/routes/accountStatusRoutes");
const rolesRoutes = requireSat("./src/routes/rolesRoutes");
const departmentsRoutes = requireSat("./src/routes/departmentsRoutes");
const countryRoutes = requireSat("./src/routes/countryRoutes");
const auditStatusRoutes = requireSat("./src/routes/auditStatusRoutes");
const ticketsStatusRoutes = requireSat("./src/routes/ticketsStatusRoutes");
const ticketsTypeRoutes = requireSat("./src/routes/ticketsTypeRoutes");
const formsTypeRoutes = requireSat("./src/routes/formsTypeRoutes");
const questionTypeRoutes = requireSat("./src/routes/questionTypeRoutes");
const rpnRoutes = requireSat("./src/routes/rpnRoutes");
const locationsRoutes = requireSat("./src/routes/locationsRoutes");
const usersRoutes = requireSat("./src/routes/usersRoutes");
const questionsRoutes = requireSat("./src/routes/questionsRoutes");
const formsRoutes = requireSat("./src/routes/formsRoutes");
const auditsRoutes = requireSat("./src/routes/auditsRoutes");
const externalAuditorsRoutes = requireSat("./src/routes/externalAuditorsRoutes");
const submissionsRoutes = requireSat("./src/routes/submissionsRoutes");
const ticketsRoutes = requireSat("./src/routes/ticketsRoutes");
const dynamicFormRoutes = requireSat("./src/routes/dynamicFormRoutes");
const dashboardRoutes = requireSat("./src/routes/dashboardRoutes");
const activityLogRoutes = requireSat("./src/routes/activityLogRoutes");
const aiRoutes = requireSat("./src/routes/aiRoutes");
const auditSchedulesRoutes = requireSat("./src/routes/auditSchedulesRoutes");

app.use(`${API_PREFIX}/account-status`, accountStatusRoutes);
app.use(`${API_PREFIX}/roles`, rolesRoutes);
app.use(`${API_PREFIX}/departments`, departmentsRoutes);
app.use(`${API_PREFIX}/countries`, countryRoutes);
app.use(`${API_PREFIX}/audit-status`, auditStatusRoutes);
app.use(`${API_PREFIX}/tickets-status`, ticketsStatusRoutes);
app.use(`${API_PREFIX}/tickets-type`, ticketsTypeRoutes);
app.use(`${API_PREFIX}/forms-type`, formsTypeRoutes);
app.use(`${API_PREFIX}/question-type`, questionTypeRoutes);
app.use(`${API_PREFIX}/rpn`, rpnRoutes);
app.use(`${API_PREFIX}/locations`, locationsRoutes);
app.use(`${API_PREFIX}/users`, usersRoutes);
app.use(`${API_PREFIX}/questions`, questionsRoutes);
app.use(`${API_PREFIX}/forms`, formsRoutes);
app.use(`${API_PREFIX}/audits`, auditsRoutes);
app.use(`${API_PREFIX}/external-auditors`, externalAuditorsRoutes);
app.use(`${API_PREFIX}/submissions`, submissionsRoutes);
app.use(`${API_PREFIX}/tickets`, ticketsRoutes);
app.use(`${API_PREFIX}/dynamic-form`, dynamicFormRoutes);
app.use(`${API_PREFIX}/dashboard`, dashboardRoutes);
app.use(`${API_PREFIX}/activity-log`, activityLogRoutes);
app.use(`${API_PREFIX}/ai`, aiRoutes);
app.use(`${API_PREFIX}/audit-schedules`, auditSchedulesRoutes);

app.use(
  "/uploads",
  express.static(path.join(__dirname, "Risk_assessment_backend", "uploads"))
);

const apiApp = express();
apiApp.set("etag", false);
apiApp.use((req, res, next) => {
  res.set("Cache-Control", "no-store, no-cache, must-revalidate");
  res.set("Pragma", "no-cache");
  next();
});
apiApp.use(apiRouter);

app.use("/api", apiApp);

const masterApiApp = express();
masterApiApp.set("etag", false);
masterApiApp.use((req, res, next) => {
  res.set("Cache-Control", "no-store, no-cache, must-revalidate");
  res.set("Pragma", "no-cache");
  next();
});
masterApiApp.use(masterApiRouter);

app.use(MASTER_API_PREFIX, masterApiApp);

const swiApiApp = express();
swiApiApp.set("etag", false);
swiApiApp.use((req, res, next) => {
  res.set("Cache-Control", "no-store, no-cache, must-revalidate");
  res.set("Pragma", "no-cache");
  next();
});
swiApiApp.use(swiApiRouter);

app.use(SWI_API_PREFIX, swiApiApp);

// PTW router self-namespaces under `/ptw`, so mount at `/api` → `/api/ptw/*`.
const ptwApiApp = express();
ptwApiApp.set("etag", false);
ptwApiApp.use((req, res, next) => {
  res.set("Cache-Control", "no-store, no-cache, must-revalidate");
  res.set("Pragma", "no-cache");
  next();
});
ptwApiApp.use(ptwApiRouter);

app.use("/api", ptwApiApp);

const satErrorHandler = requireSat("./src/middlewares/errorHandler");

app.use(notFound);

app.use((err, req, res, next) => {
  const url = req.originalUrl || req.url || "";
  if (err instanceof MasterApiError || url.startsWith(MASTER_API_PREFIX)) {
    return masterErrorHandler(err, req, res, next);
  }
  if (err instanceof PtwApiError || url.startsWith(PTW_API_PREFIX)) {
    return ptwErrorHandler(err, req, res, next);
  }
  if (err instanceof ApiError || err instanceof SwiApiError) {
    return oracErrorHandler(err, req, res, next);
  }
  if (url.startsWith(API_PREFIX)) {
    return satErrorHandler(err, req, res, next);
  }
  if (url.startsWith(SWI_API_PREFIX)) {
    return swiErrorHandler(err, req, res, next);
  }
  return oracErrorHandler(err, req, res, next);
});

  return app;
}

const isMain =
  typeof process !== "undefined" &&
  process.argv[1] &&
  import.meta.url === pathToFileURL(process.argv[1]).href;

if (isMain) {
  const app = createUnifiedApp();
  const server = http.createServer(app);

  const shutdown = () => {
    server.close(() => process.exit(0));
    setTimeout(() => process.exit(0), 2000).unref();
  };

  process.on("SIGTERM", shutdown);
  process.on("SIGINT", shutdown);

  const tryListen = (attempt = 1) => {
    server.once("listening", () => {
      console.log(`EHS unified API listening on port ${PORT}`);
      console.log(`  MASTER: ${MASTER_API_PREFIX}/*`);
      console.log(`  SAT:   ${API_PREFIX}/*`);
      console.log(`  ORAC:  /api/* (health, auth, /api/orac, …)`);
      console.log(`  SWI:   ${SWI_API_PREFIX}/*`);
      console.log(`  PTW:   ${PTW_API_PREFIX}/*`);
      try {
        const { startAuditScheduleReminderJob } = requireSat(
          "./src/services/auditScheduleReminderService"
        );
        startAuditScheduleReminderJob();
      } catch (err) {
        console.warn("[auditScheduleReminder] Could not start reminder job:", err.message);
      }
    });

    server.once("error", (err) => {
      if (err.code === "EADDRINUSE" && attempt < 10) {
        console.warn(
          `Port ${PORT} busy (watch restart?), retry ${attempt}/10 in 500ms…`
        );
        server.close(() => setTimeout(() => tryListen(attempt + 1), 500));
        return;
      }
      if (err.code === "EACCES") {
        console.error(
          `Cannot bind port ${PORT}: permission denied. On Windows, ports 4912–5011 are often excluded (Hyper-V/WSL). Try APP_PORT=5012 or run: netsh interface ipv4 show excludedportrange protocol=tcp`
        );
      } else {
        console.error(`Cannot bind port ${PORT}:`, err.message);
      }
      process.exit(1);
    });

    server.listen(PORT);
  };

  tryListen();
}
