import type { Request, Response } from "express";
import prisma from "../config/prisma.js";
import { InvoicePdf } from "../utils/pdf/InvoicePdf.js";
import { ShopRepository } from "../repositories/ShopRepository.js";
import { UpiRepository } from "../repositories/UpiRepository.js";
import { Prisma } from "../generated/prisma/client.js";

const shopRepository = new ShopRepository();
const upiRepository = new UpiRepository();

// ─── Token validation helper ────────────────────────────────────────────────
const UUID_REGEX =
  /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;

async function findSaleByToken(token: string) {
  if (!UUID_REGEX.test(token)) return null;

  return prisma.sale.findFirst({
    where: {
      invoiceDownloadToken: token,
      deletedAt: null, // cancelled sales are inaccessible
    },
    include: {
      customer: {
        select: {
          name: true,
          mobileNo: true,
          address: true,
          outstandingBalance: true,
        },
      },
      items: {
        include: {
          purchase: {
            select: {
              lotNo: true,
              unit: true,
              weightPerBag: true,
              vegetable: { select: { name: true } },
            },
          },
          saleReturns: true,
        },
      },
    },
  });
}

// ─── GET /api/public/invoices/:token ────────────────────────────────────────
/**
 * Returns invoice data as JSON.
 * No authentication required — the UUID token is the credential.
 */
export const getPublicInvoice = async (
  req: Request,
  res: Response
): Promise<void> => {
  try {
    const token = String(req.params.token ?? "");
    const sale = await findSaleByToken(token);

    if (!sale) {
      res.status(404).json({ message: "Invoice not found" });
      return;
    }

    // Compute net totals accounting for any sale returns
    let totalReturnedAmount = new Prisma.Decimal(0);
    const enrichedItems = sale.items.map((item) => {
      const returnedQuantity = item.saleReturns.reduce(
        (acc, ret) => acc.add(new Prisma.Decimal(ret.quantity)),
        new Prisma.Decimal(0)
      );
      const returnedAmount = item.saleReturns.reduce(
        (acc, ret) => acc.add(new Prisma.Decimal(ret.returnAmount)),
        new Prisma.Decimal(0)
      );
      totalReturnedAmount = totalReturnedAmount.add(returnedAmount);

      return {
        id: item.id,
        vegetableName: item.purchase?.vegetable?.name ?? "—",
        lotNo: item.purchase?.lotNo ?? "—",
        saleUnit: item.saleUnit,
        quantity: new Prisma.Decimal(item.quantity).sub(returnedQuantity),
        bags: item.bags,
        sellingRate: item.sellingRate,
        total: new Prisma.Decimal(item.total).sub(returnedAmount),
        returnedQuantity,
        returnedAmount,
      };
    });

    res.json({
      invoiceNo: sale.invoiceNo,
      invoiceDate: sale.invoiceDate,
      customerName: sale.customer?.name ?? sale.walkInName ?? "Walk-in Customer",
      subTotal: sale.subTotal,
      discount: sale.discount,
      freightCharge: sale.freightCharge,
      grandTotal: sale.grandTotal,
      receivedAmount: sale.receivedAmount,
      balanceAmount: sale.balanceAmount,
      paymentStatus: sale.paymentStatus,
      remarks: sale.remarks,
      customerOutstanding: sale.customer?.outstandingBalance ?? 0,
      totalReturnedAmount,
      items: enrichedItems,
    });
  } catch (err: any) {
    res.status(500).json({ message: err.message });
  }
};

// ─── GET /api/public/invoices/:token/pdf ────────────────────────────────────
/**
 * Streams the invoice PDF.
 * No authentication required — the UUID token is the credential.
 */
export const downloadPublicInvoicePdf = async (
  req: Request,
  res: Response
): Promise<void> => {
  try {
    const token = String(req.params.token ?? "");

    const sale = await findSaleByToken(token);

    if (!sale) {
      res.status(404).json({ message: "Invoice not found" });
      return;
    }

    const lang = req.query.lang ? String(req.query.lang) : "en";
    const printSign = false; // TEMP: Digital signature disabled — change to: req.query.printSign !== "false"  to re-enable

    const shop = await shopRepository.findFirst();
    const upis = await upiRepository.findAll();

    InvoicePdf.generate({ sale, shop, upis }, res, { lang, printSign });
  } catch (err: any) {
    if (res.headersSent) {
      res.end();
    } else {
      res.status(500).json({ message: err.message });
    }
  }
};
