import Fastify from "fastify";
import type { ScheduledTask } from "node-cron";
import cors from "@fastify/cors";
import cookie from "@fastify/cookie";
import rateLimit from "@fastify/rate-limit";
import path from "path";
import { config } from "./config/env";
import { securityHeaders } from "./middleware/security";
import prisma from "./config/database";

import authRoutes from "./routes/admin/auth";
import usersRoutes from "./routes/admin/users";
import rolesRoutes from "./routes/admin/roles";
import attendanceRoutes from "./routes/admin/attendance";
import customersRoutes from "./routes/admin/customers";
import invoicesRoutes from "./routes/admin/invoices";
import issuedOrdersRoutes from "./routes/admin/issued-orders";
import quotationsRoutes from "./routes/admin/quotations";
import ordersRoutes from "./routes/admin/orders";
import projectsRoutes from "./routes/admin/projects";
import tripsRoutes from "./routes/admin/trips";
import vehiclesRoutes from "./routes/admin/vehicles";
import leaveRequestsRoutes from "./routes/admin/leave-requests";
import bankAccountsRoutes from "./routes/admin/bank-accounts";
import companySettingsRoutes from "./routes/admin/company-settings";
import receivedInvoicesRoutes from "./routes/admin/received-invoices";
import dashboardRoutes from "./routes/admin/dashboard";
import auditLogRoutes from "./routes/admin/audit-log";
import profileRoutes from "./routes/admin/profile";
import sessionsRoutes from "./routes/admin/sessions";
import totpRoutes from "./routes/admin/totp";
import scopeTemplatesRoutes from "./routes/admin/scope-templates";
import invoicesPdfRoutes from "./routes/admin/invoices-pdf";
import issuedOrdersPdfRoutes from "./routes/admin/issued-orders-pdf";
import offersPdfRoutes from "./routes/admin/offers-pdf";
import ordersPdfRoutes from "./routes/admin/orders-pdf";
import projectFilesRoutes from "./routes/admin/project-files";
import warehouseRoutes from "./routes/admin/warehouse";
import planRoutes from "./routes/admin/plan";
import aiRoutes from "./routes/admin/ai";
import aresRoutes from "./routes/admin/ares";

const app = Fastify({
  logger: {
    level: config.isProduction ? "warn" : "info",
  },
  trustProxy:
    config.trustProxy.length > 0 ? config.trustProxy : ["127.0.0.1", "::1"],
  bodyLimit: config.nas.maxUploadSize,
});

async function start() {
  let invoiceAlertCron: ScheduledTask | null = null;

  // --- Plugins ---
  await app.register(cors, {
    origin:
      config.appEnv === "local"
        ? [
            /^http:\/\/127\.0\.0\.1:\d+$/,
            /^http:\/\/localhost:\d+$/,
            /^http:\/\/192\.168\.\d+\.\d+:\d+$/,
          ]
        : config.cors.origins,
    credentials: true,
    methods: ["GET", "POST", "PUT", "DELETE", "OPTIONS"],
    allowedHeaders: ["Content-Type", "Authorization", "X-Requested-With"],
  });

  await app.register(cookie);

  // --- Health check (before rate-limit so monitoring isn't throttled) ---
  // Pings the database so load balancers / PM2 can detect a broken instance.
  // Returns 503 with { status: "unhealthy", ... } if the DB is unreachable.
  app.get("/api/health", async (_request, reply) => {
    try {
      await prisma.$queryRaw`SELECT 1`;
    } catch (err) {
      reply.status(503);
      return {
        status: "unhealthy",
        database: "unreachable",
        error: err instanceof Error ? err.message : "unknown",
        timestamp: new Date().toISOString(),
      };
    }
    return {
      status: "ok",
      database: "reachable",
      timestamp: new Date().toISOString(),
    };
  });

  await app.register(rateLimit, {
    max: config.rateLimit.max,
    timeWindow: config.rateLimit.timeWindow,
  });

  // --- Security headers ---
  app.addHook("onRequest", securityHeaders);

  // --- Global error handler — consistent { success, error } format ---
  app.setErrorHandler(
    (err: Error & { statusCode?: number }, request, reply) => {
      const statusCode = err.statusCode ?? 500;
      if (statusCode >= 500) {
        request.log.error(err);
      }
      reply.status(statusCode).send({
        success: false,
        error:
          statusCode >= 500
            ? "Interní chyba serveru"
            : err.message || "Chyba požadavku",
      });
    },
  );

  // --- API Routes ---
  await app.register(authRoutes, { prefix: "/api/admin" });
  await app.register(usersRoutes, { prefix: "/api/admin/users" });
  await app.register(rolesRoutes, { prefix: "/api/admin/roles" });
  await app.register(attendanceRoutes, { prefix: "/api/admin/attendance" });
  await app.register(customersRoutes, { prefix: "/api/admin/customers" });
  await app.register(invoicesRoutes, { prefix: "/api/admin/invoices" });
  await app.register(issuedOrdersRoutes, {
    prefix: "/api/admin/issued-orders",
  });
  await app.register(issuedOrdersPdfRoutes, {
    prefix: "/api/admin/issued-orders-pdf",
  });
  await app.register(quotationsRoutes, { prefix: "/api/admin/offers" });
  await app.register(ordersRoutes, { prefix: "/api/admin/orders" });
  await app.register(projectsRoutes, { prefix: "/api/admin/projects" });
  await app.register(tripsRoutes, { prefix: "/api/admin/trips" });
  await app.register(vehiclesRoutes, { prefix: "/api/admin/vehicles" });
  await app.register(leaveRequestsRoutes, {
    prefix: "/api/admin/leave-requests",
  });
  await app.register(bankAccountsRoutes, {
    prefix: "/api/admin/bank-accounts",
  });
  await app.register(companySettingsRoutes, {
    prefix: "/api/admin/company-settings",
  });
  await app.register(receivedInvoicesRoutes, {
    prefix: "/api/admin/received-invoices",
  });
  await app.register(dashboardRoutes, { prefix: "/api/admin/dashboard" });
  await app.register(auditLogRoutes, { prefix: "/api/admin/audit-log" });
  await app.register(profileRoutes, { prefix: "/api/admin/profile" });
  await app.register(sessionsRoutes, { prefix: "/api/admin/sessions" });
  await app.register(totpRoutes, { prefix: "/api/admin/totp" });
  await app.register(scopeTemplatesRoutes, {
    prefix: "/api/admin/offers-templates",
  });
  await app.register(invoicesPdfRoutes, { prefix: "/api/admin/invoices-pdf" });
  await app.register(offersPdfRoutes, { prefix: "/api/admin/offers-pdf" });
  await app.register(ordersPdfRoutes, { prefix: "/api/admin/orders-pdf" });
  await app.register(projectFilesRoutes, {
    prefix: "/api/admin/project-files",
  });
  await app.register(warehouseRoutes, { prefix: "/api/admin/warehouse" });
  await app.register(planRoutes, { prefix: "/api/admin/plan" });
  await app.register(aiRoutes, { prefix: "/api/admin/ai" });
  await app.register(aresRoutes, { prefix: "/api/admin/ares" });

  // --- Frontend: Vite dev middleware (dev only) ---
  if (!config.isProduction) {
    const viteModule = await (Function(
      'return import("vite")',
    )() as Promise<any>);
    const createViteServer = viteModule.createServer as (
      opts: any,
    ) => Promise<any>;
    const vite = await createViteServer({
      server: { middlewareMode: true },
      appType: "spa",
    });

    app.addHook("onRequest", (request, reply, done) => {
      if (request.url.startsWith("/api/")) {
        done();
        return;
      }
      vite.middlewares(request.raw, reply.raw, done);
    });

    app.setNotFoundHandler((request, reply) => {
      if (request.url.startsWith("/api/")) {
        return reply.status(404).send({ success: false, error: "Not found" });
      }
      if (!reply.raw.headersSent) {
        vite.middlewares(request.raw, reply.raw, () => {
          reply.raw.statusCode = 404;
          reply.raw.end();
        });
      }
    });
  }

  // --- Frontend: static file serving (production) ---
  if (config.isProduction) {
    const fastifyStatic = (await import("@fastify/static")).default;
    await app.register(fastifyStatic, {
      root: path.join(__dirname, "..", "dist-client"),
      prefix: "/",
      wildcard: false,
      // Deploy-skew caching contract (per the Vite deploy guide):
      // - /assets/* names are content-hashed → safe to cache forever
      //   (a changed file always gets a NEW name, so "immutable" is correct).
      // - index.html (and any non-hashed file) must always be revalidated,
      //   otherwise a cached copy keeps referencing deleted old chunks.
      setHeaders: (res, filePath) => {
        if (filePath.includes(`${path.sep}assets${path.sep}`)) {
          res.setHeader("Cache-Control", "public, max-age=31536000, immutable");
        } else {
          res.setHeader("Cache-Control", "no-cache");
        }
      },
    });

    app.setNotFoundHandler((request, reply) => {
      if (request.url.startsWith("/api/")) {
        return reply.status(404).send({ success: false, error: "Not found" });
      }
      // A missing hashed asset (old chunk after a deploy) must 404, NOT get
      // the SPA index.html fallback — HTML-as-JS masks the failure and breaks
      // the client's vite:preloadError reload recovery.
      if (request.url.startsWith("/assets/")) {
        return reply.status(404).send();
      }
      return reply.sendFile("index.html");
    });
  }

  // --- Invoice alert cron (daily at 8:00 AM) ---
  if (config.email.invoiceAlert) {
    const cron = await import("node-cron");
    invoiceAlertCron = cron.default.schedule("0 8 * * *", async () => {
      try {
        const { checkInvoiceAlerts } =
          await import("./services/invoice-alerts");
        await checkInvoiceAlerts();
      } catch (err) {
        app.log.error(err, "Invoice alert cron failed");
      }
    });
    app.log.info("Invoice alert cron scheduled (daily 8:00)");
  }

  // --- Start ---
  // Dev listens on a fixed local port (prod uses config.port / the PORT env).
  // 3050 keeps it clear of other local apps that commonly grab 3000.
  const port = config.isProduction ? config.port : 3050;
  try {
    await app.listen({ port, host: config.host });
    app.log.info(`Server running on http://${config.host}:${port}`);
  } catch (err) {
    app.log.error(err);
    process.exit(1);
  }

  const shutdown = async (signal: string) => {
    app.log.info(`${signal} received, shutting down gracefully...`);
    try {
      if (invoiceAlertCron) {
        invoiceAlertCron.stop();
      }
      await app.close();
      const { default: prisma } = await import("./config/database");
      await prisma.$disconnect();
      const { closeBrowser } = await import("./utils/html-to-pdf");
      await closeBrowser();
      app.log.info("Server shut down successfully");
      process.exit(0);
    } catch (err) {
      app.log.error(err, "Error during shutdown");
      process.exit(1);
    }
  };

  process.on("SIGTERM", () => shutdown("SIGTERM"));
  process.on("SIGINT", () => shutdown("SIGINT"));
}

start();
