import type { Request, Response } from "express";
import { z } from "zod";
import { RetailSaleService } from "../services/RetailSaleService.js";
import { PaymentMode } from "../constants/payment.mode.js";
import { RetailSalesListPdf } from "../utils/pdf/RetailSalesListPdf.js";
import { RetailSaleInvoicePdf } from "../utils/pdf/RetailSaleInvoicePdf.js";
import { generateRetailSaleExcel, mapRetailSaleToExportRow } from "../utils/excel/RetailSaleExcelExport.js";

const retailSaleService = new RetailSaleService();

const paymentModeValues = Object.values(PaymentMode) as [string, ...string[]];

const RetailSaleItemSchema = z.object({
  lotNo: z.string().min(1, { message: "Lot number is required" }),
  saleUnit: z.enum(["KG", "BAG", "BOX", "THAAR", "PIECES"]).default("KG"),
  quantity: z.coerce.number().positive({ message: "Quantity must be greater than 0" }).optional(),
  bags: z.coerce.number().int({ message: "Bags count must be a whole number" }).positive({ message: "Bags must be greater than 0" }).optional(),
  sellingRate: z.coerce.number().positive({ message: "Rate (per Unit) cannot be zero!" }),
}).superRefine((item, ctx) => {
  if (item.saleUnit === "KG") {
    if (item.quantity === undefined) {
      ctx.addIssue({
        code: z.ZodIssueCode.custom,
        path: ["quantity"],
        message: "Quantity is required when selling in KG",
      });
    }
    return;
  }

  if (item.saleUnit === "PIECES") {
    if (item.quantity === undefined && item.bags === undefined) {
      ctx.addIssue({
        code: z.ZodIssueCode.custom,
        path: ["quantity"],
        message: "Pieces count is required when selling in PIECES",
      });
    }
    return;
  }

  if (item.saleUnit === "THAAR") {
    if (item.bags === undefined && item.quantity === undefined) {
      ctx.addIssue({
        code: z.ZodIssueCode.custom,
        path: ["quantity"],
        message: "Thaar count is required when selling in THAAR",
      });
    }
    return;
  }

  if (item.bags === undefined) {
    ctx.addIssue({
      code: z.ZodIssueCode.custom,
      path: ["bags"],
      message: "Bags count is required when selling in BOX/BAG",
    });
  }
});

const indianMobileRegex = /^(?:(?:\+?91)|0)?[6-9]\d{9}$/;

const CreateRetailSaleSchema = z.object({
  walkInName: z.string().max(255).optional(),
  walkInMobile: z
    .preprocess(
      (val) => (val === null || val === undefined ? "" : String(val).trim()),
      z.string().refine((val) => val === "" || indianMobileRegex.test(val), {
        message: "Please enter a valid 10-digit mobile number (e.g. 9876543210)",
      })
    )
    .optional(),
  invoiceDate: z.string().min(1, { message: "Invoice date is required" }),
  paymentMode: z.enum(paymentModeValues, {
    message: `Payment mode must be one of: ${paymentModeValues.join(", ")}`,
  }),
  discount: z.coerce.number().nonnegative().optional().default(0),
  remarks: z.string().optional(),
  items: z.array(RetailSaleItemSchema).min(1, { message: "At least one sale item is required" }),
});



export const createRetailSale = async (req: Request, res: Response): Promise<void> => {
  const parsed = CreateRetailSaleSchema.safeParse(req.body);
  if (!parsed.success) {
    const formattedErrors: Record<string, string[]> = {};
    parsed.error.issues.forEach(issue => {
      const path = issue.path.join('.');
      if (!formattedErrors[path]) {
        formattedErrors[path] = [];
      }
      formattedErrors[path].push(issue.message);
    });
    res.status(422).json({
      message: "Validation failed",
      errors: formattedErrors,
    });
    return;
  }

  try {
    const data = {
      ...parsed.data,
      createdBy: req.user?.id ? BigInt(req.user.id) : null,
    };
    const sale = await retailSaleService.createRetailSale(data);
    res.status(201).json({ message: "Retail sale created successfully", result: sale });
  } catch (err: any) {
    res.status(400).json({ message: err.message });
  }
};

export const getRetailSales = async (req: Request, res: Response): Promise<void> => {
  try {
    const page = Number(req.query.page) || 1;
    const limit = Number(req.query.limit) || 10;
    const dateFrom = req.query.dateFrom ? String(req.query.dateFrom) : undefined;
    const dateTo = req.query.dateTo ? String(req.query.dateTo) : undefined;
    const search = req.query.search ? String(req.query.search) : undefined;
    const paymentMode = req.query.paymentMode ? String(req.query.paymentMode) : undefined;

    const result = await retailSaleService.getRetailSales(page, limit, dateFrom, dateTo, search, paymentMode);
    res.json({ message: "Retail sales fetched successfully", result });
  } catch (err: any) {
    res.status(500).json({ message: err.message });
  }
};

export const getRetailSaleById = async (req: Request, res: Response): Promise<void> => {
  try {
    const sale = await retailSaleService.getRetailSaleById(BigInt(req.params.id as string));
    res.json({ message: "Retail sale fetched successfully", result: sale });
  } catch (err: any) {
    res.status(404).json({ message: err.message });
  }
};

export const exportPdf = async (req: Request, res: Response): Promise<void> => {
  try {
    const dateFrom = req.query.dateFrom ? String(req.query.dateFrom) : undefined;
    const dateTo = req.query.dateTo ? String(req.query.dateTo) : undefined;
    const search = req.query.search ? String(req.query.search) : undefined;
    const paymentMode = req.query.paymentMode ? String(req.query.paymentMode) : undefined;

    const sales = await retailSaleService.getRetailSalesForExport(dateFrom, dateTo, search, paymentMode);
    if (!sales || sales.length === 0) {
      res.status(400).json({ message: "No data available to export" });
      return;
    }
    await RetailSalesListPdf.generate(sales, res, {
      ...(dateFrom !== undefined && { startDate: dateFrom }),
      ...(dateTo !== undefined && { endDate: dateTo }),
      ...(search !== undefined && { search }),
    });
  } catch (err: any) {
    if (res.headersSent) {
      res.end();
    } else {
      res.status(500).json({ message: err.message });
    }
  }
};

export const exportExcel = async (req: Request, res: Response): Promise<void> => {
  try {
    const dateFrom = req.query.dateFrom ? String(req.query.dateFrom) : undefined;
    const dateTo = req.query.dateTo ? String(req.query.dateTo) : undefined;
    const search = req.query.search ? String(req.query.search) : undefined;
    const paymentMode = req.query.paymentMode ? String(req.query.paymentMode) : undefined;

    const sales = await retailSaleService.getRetailSalesForExport(dateFrom, dateTo, search, paymentMode);
    if (!sales || sales.length === 0) {
      res.status(400).json({ message: "No data available to export" });
      return;
    }
    const rows = sales.map(mapRetailSaleToExportRow);
    const dateTag = new Date().toISOString().slice(0, 10);
    await generateRetailSaleExcel(rows, res, `retail-sales-report-${dateTag}`);
  } catch (err: any) {
    if (res.headersSent) {
      res.end();
    } else {
      res.status(500).json({ message: err.message });
    }
  }
};

export const exportInvoicePdf = async (req: Request, res: Response): Promise<void> => {
  try {
    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 sale = await retailSaleService.getRetailSaleById(BigInt(req.params.id as string));
    await RetailSaleInvoicePdf.generate(sale, res, { lang, printSign });
  } catch (err: any) {
    if (res.headersSent) {
      res.end();
    } else {
      res.status(500).json({ message: err.message });
    }
  }
};

export const cancelRetailSale = async (req: Request, res: Response): Promise<void> => {
  try {
    const sale = await retailSaleService.cancelRetailSale(BigInt(req.params.id as string));
    res.json({ message: "Retail sale cancelled successfully", result: sale });
  } catch (err: any) {
    res.status(400).json({ message: err.message });
  }
};

export const getRetailSaleMeta = async (_req: Request, res: Response): Promise<void> => {
  try {
    const meta = await retailSaleService.getRetailSaleMeta();
    res.json({ message: "Retail sale meta fetched", result: meta });
  } catch (err: any) {
    res.status(500).json({ message: err.message });
  }
};

export const moveToWholesale = async (req: Request, res: Response): Promise<void> => {
  try {
    const sale = await retailSaleService.moveToWholesale(BigInt(req.params.id as string));
    res.json({ message: "Retail sale moved to wholesale successfully", result: sale });
  } catch (err: any) {
    res.status(400).json({ message: err.message });
  }
};
