import fs from "node:fs";
import path from "node:path";
import { fileURLToPath } from "node:url";

import cookieParser from "cookie-parser";
import cors from "cors";
import express from "express";
import helmet from "helmet";
import morgan from "morgan";

import { env } from "./config/env.js";
import apiRoutes from "./routes/index.js";
import { errorHandler, notFoundHandler } from "./middleware/errorHandler.js";
import { sanitizeRequest } from "./utils/sanitize.js";

const app = express();
const currentDir = path.dirname(fileURLToPath(import.meta.url));
const clientDistCandidates = [
  path.resolve(currentDir, "../client/dist"),
  path.resolve(currentDir, "../../client/dist"),
];
const clientDistPath =
  clientDistCandidates.find((candidatePath) => fs.existsSync(path.join(candidatePath, "index.html"))) ||
  clientDistCandidates[0];
const clientIndexPath = path.join(clientDistPath, "index.html");
const uploadsPath = path.resolve(currentDir, "../uploads");
const hasClientBuild = fs.existsSync(clientIndexPath);

const isAllowedOrigin = (origin, req) => {
  if (!origin) {
    return false;
  }

  if (env.clientUrls.includes(origin)) {
    return true;
  }

  try {
    const requestOrigin = new URL(origin);
    const requestHost = req.get("host");

    return Boolean(requestHost) && requestOrigin.host === requestHost;
  } catch {
    return false;
  }
};

app.use(
  cors((req, callback) => {
    const origin = req.get("origin");

    callback(null, {
      origin: isAllowedOrigin(origin, req) ? origin : false,
      credentials: true,
    });
  }),
);
app.use(
  helmet({
    referrerPolicy: {
      policy: "strict-origin-when-cross-origin",
    },
    contentSecurityPolicy: {
      directives: {
        defaultSrc: ["'self'"],
        connectSrc: ["'self'", "ws:", "wss:", "http:", "https:"],
        imgSrc: [
          "'self'",
          "data:",
          "blob:",
          "https://*.tile.openstreetmap.org",
          "https://tile.openstreetmap.org",
          "https://server.arcgisonline.com",
          "https://unpkg.com",
        ],
        styleSrc: ["'self'", "'unsafe-inline'", "https://unpkg.com"],
        scriptSrc: ["'self'", "'unsafe-inline'"],
        fontSrc: ["'self'", "data:", "https:"],
        upgradeInsecureRequests: null,
      },
    },
    hsts: false,
  }),
);
app.use(morgan("dev"));
app.use(cookieParser());
app.use(express.json({ limit: "10mb" }));
app.use(express.urlencoded({ extended: true, limit: "10mb" }));
app.use(sanitizeRequest);

app.get("/health", (req, res) => {
  res.json({
    status: "ok",
    service: "scf-accounting-api",
    timestamp: new Date().toISOString(),
  });
});

app.use("/api", apiRoutes);
app.use("/uploads", express.static(uploadsPath));

if (hasClientBuild) {
  app.use(
    express.static(clientDistPath, {
      index: "index.html",
    }),
  );

  app.get("*", (req, res, next) => {
    if (
      req.path.startsWith("/api") ||
      req.path === "/health" ||
      req.path.startsWith("/socket.io")
    ) {
      next();
      return;
    }

    res.sendFile(clientIndexPath);
  });
}

app.use(notFoundHandler);
app.use(errorHandler);

export default app;
