import { FastifyInstance } from "fastify";
import prisma from "../../config/database";
import { requirePermission } from "../../middleware/auth";
import { logAudit } from "../../services/audit";
import { success, error, parseId, paginated } from "../../utils/response";
import { parsePagination, buildPaginationMeta } from "../../utils/pagination";
import { parseBody } from "../../schemas/common";
import {
  CreateInvoiceSchema,
  UpdateInvoiceSchema,
} from "../../schemas/invoices.schema";
import {
  markOverdueInvoices,
  listInvoices,
  getInvoiceListTotals,
  getNextInvoiceNumberFormatted,
  getNextInvoiceNumberPreview,
  getInvoiceStats,
  getOrderDataForInvoice,
  getInvoice,
  createInvoice,
  updateInvoice,
  deleteInvoice,
} from "../../services/invoices.service";
import { nasInvoicesManager } from "../../services/nas-financials-manager";

export default async function invoicesRoutes(
  fastify: FastifyInstance,
): Promise<void> {
  // Auto-update overdue invoices on GET requests, throttled to once per hour
  let lastOverdueCheck = 0;
  const OVERDUE_CHECK_INTERVAL = 60 * 60 * 1000; // 1 hour

  fastify.addHook("onRequest", async (request) => {
    if (request.method !== "GET") return;
    if (Date.now() - lastOverdueCheck > OVERDUE_CHECK_INTERVAL) {
      lastOverdueCheck = Date.now();
      await markOverdueInvoices();
    }
  });

  // GET /api/admin/invoices
  fastify.get(
    "/",
    { preHandler: requirePermission("invoices.view") },
    async (request, reply) => {
      const query = request.query as Record<string, unknown>;
      const { page, limit, skip, order, search } = parsePagination(query);

      const result = await listInvoices({
        page,
        limit,
        skip,
        sort: String(query.sort || ""),
        order,
        search,
        status: query.status ? String(query.status) : undefined,
        customer_id: query.customer_id ? Number(query.customer_id) : undefined,
        month: query.month ? Number(query.month) : undefined,
        year: query.year ? Number(query.year) : undefined,
      });

      return paginated(
        reply,
        result.data,
        buildPaginationMeta(result.total, page, limit),
      );
    },
  );

  // GET /api/admin/invoices/next-number
  fastify.get(
    "/next-number",
    { preHandler: requirePermission("invoices.create") },
    async (_request, reply) => {
      const result = await getNextInvoiceNumberPreview();
      return success(reply, result);
    },
  );

  // GET /api/admin/invoices/stats — monthly KPI cards (paid/awaiting/overdue/VAT).
  fastify.get(
    "/stats",
    { preHandler: requirePermission("invoices.view") },
    async (request, reply) => {
      const query = request.query as Record<string, unknown>;
      const month = query.month ? Number(query.month) : undefined;
      const year = query.year ? Number(query.year) : undefined;
      const stats = await getInvoiceStats(month, year);
      return success(reply, stats);
    },
  );

  // GET /api/admin/invoices/list-totals — per-currency total (incl. VAT) summed
  // over the WHOLE filtered set (not one page), matching the issued-invoice
  // list's filters (search/status/month/year). Distinct path from "/stats" (the
  // KPI cards) to avoid colliding with that endpoint. Registered BEFORE "/:id"
  // so the literal path isn't captured as an invoice id.
  fastify.get(
    "/list-totals",
    { preHandler: requirePermission("invoices.view") },
    async (request, reply) => {
      const query = request.query as Record<string, unknown>;
      const result = await getInvoiceListTotals({
        search: query.search ? String(query.search) : undefined,
        status: query.status ? String(query.status) : undefined,
        customer_id: query.customer_id ? Number(query.customer_id) : undefined,
        month: query.month ? Number(query.month) : undefined,
        year: query.year ? Number(query.year) : undefined,
      });
      return success(reply, { totals: result.totals });
    },
  );

  // GET /api/admin/invoices/order-data/:id
  fastify.get<{ Params: { id: string } }>(
    "/order-data/:id",
    { preHandler: requirePermission("invoices.create") },
    async (request, reply) => {
      const orderId = parseId(request.params.id, reply);
      if (orderId === null) return;
      const result = await getOrderDataForInvoice(orderId);
      if (!result) return error(reply, "Objednávka nenalezena", 404);
      return success(reply, result);
    },
  );

  // GET /api/admin/invoices/:id
  fastify.get<{ Params: { id: string } }>(
    "/:id",
    { preHandler: requirePermission("invoices.view") },
    async (request, reply) => {
      const id = parseId(request.params.id, reply);
      if (id === null) return;
      const invoice = await getInvoice(id);
      if (!invoice) return error(reply, "Faktura nenalezena", 404);
      return success(reply, invoice);
    },
  );

  // POST /api/admin/invoices
  fastify.post(
    "/",
    { preHandler: requirePermission("invoices.create") },
    async (request, reply) => {
      const parsed = parseBody(CreateInvoiceSchema, request.body);
      if ("error" in parsed) return error(reply, parsed.error, 400);
      const body = parsed.data;

      const invoice = await createInvoice(body);

      // A draft has no number yet — show "koncept" instead of "null".
      const isDraft = invoice.status === "draft";
      await logAudit({
        request,
        authData: request.authData,
        action: "create",
        entityType: "invoice",
        entityId: invoice.id,
        description: `Vytvořena faktura ${invoice.invoice_number ?? "koncept"}`,
      });
      // Return both invoice_id and id for frontend compatibility
      return success(
        reply,
        {
          id: invoice.id,
          invoice_id: invoice.id,
          invoice_number: invoice.invoice_number,
        },
        201,
        isDraft
          ? "Faktura byla uložena jako koncept"
          : "Faktura byla vystavena",
      );
    },
  );

  // PUT /api/admin/invoices/:id
  fastify.put<{ Params: { id: string } }>(
    "/:id",
    { preHandler: requirePermission("invoices.edit") },
    async (request, reply) => {
      const id = parseId(request.params.id, reply);
      if (id === null) return;
      const parsed = parseBody(UpdateInvoiceSchema, request.body);
      if ("error" in parsed) return error(reply, parsed.error, 400);
      const body = parsed.data;

      const result = await updateInvoice(id, body);

      if ("error" in result) {
        if (result.error === "not_found")
          return error(reply, "Faktura nenalezena", 404);
        if (result.error === "invalid_transition")
          return error(
            reply,
            `Neplatný přechod stavu z "${result.currentStatus}" na "${result.newStatus}"`,
            400,
          );
        return error(reply, "Neznámá chyba", 500);
      }

      await logAudit({
        request,
        authData: request.authData,
        action: "update",
        entityType: "invoice",
        entityId: id,
        description: `Upravena faktura ${result.invoice_number}`,
      });
      return success(reply, { id }, 200, "Faktura byla aktualizována");
    },
  );

  // DELETE /api/admin/invoices/:id
  fastify.delete<{ Params: { id: string } }>(
    "/:id",
    { preHandler: requirePermission("invoices.delete") },
    async (request, reply) => {
      const id = parseId(request.params.id, reply);
      if (id === null) return;
      const existing = await deleteInvoice(id);
      if (!existing) return error(reply, "Faktura nenalezena", 404);

      // Delete PDF from NAS
      if (existing.invoice_number) {
        await nasInvoicesManager.cleanIssued(existing.invoice_number);
      }

      await logAudit({
        request,
        authData: request.authData,
        action: "delete",
        entityType: "invoice",
        entityId: id,
        description: `Smazána faktura ${existing.invoice_number}`,
      });
      return success(reply, null, 200, "Faktura smazána");
    },
  );

  // GET /api/admin/invoices/:id/file — serve PDF from NAS
  fastify.get<{ Params: { id: string } }>(
    "/:id/file",
    { preHandler: requirePermission("invoices.view") },
    async (request, reply) => {
      const id = parseId(request.params.id, reply);
      if (id === null) return;
      const invoice = await prisma.invoices.findUnique({
        where: { id },
        select: { invoice_number: true, issue_date: true },
      });
      if (!invoice?.invoice_number || !invoice.issue_date)
        return error(reply, "Faktura nenalezena", 404);

      const d = new Date(invoice.issue_date);
      const relPath = `Vydané/${d.getFullYear()}/${String(d.getMonth() + 1).padStart(2, "0")}/${invoice.invoice_number}.pdf`;
      const file = nasInvoicesManager.readIssued(relPath);
      if (!file) return error(reply, "PDF soubor nenalezen", 404);

      const safeName = invoice.invoice_number.replace(/[\r\n"]/g, "");
      return reply
        .type("application/pdf")
        .header("Content-Disposition", `inline; filename="${safeName}.pdf"`)
        .send(file.data);
    },
  );
}
