import type { Request, Response } from "express";
import { SaleService } from "../services/SaleService.js";
import { z } from "zod";
import { safeParseBigInt } from "../utils/parseHelper.js";
import { WholeSalesListPdf } from "../utils/pdf/WholeSalesListPdf.js";

const saleService = new SaleService();

const SaleItemSchema = z.object({
  purchaseId: z.coerce.number().int().positive(),
  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 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" || item.saleUnit === "PIECES") {
    if (item.quantity === undefined) {
      ctx.addIssue({
        code: z.ZodIssueCode.custom,
        path: ["quantity"],
        message: item.saleUnit === "PIECES" ? "Pieces count is required when selling in PIECES" : "Quantity is required when selling in KG",
      });
    }
    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 is required when selling in BOX/BAG",
    });
  }
});

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

const CreateSaleSchema = z.object({
  customerId: z.coerce.number().int().positive().nullable().optional(),
  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" }),
  subTotal: z.coerce.number().positive(),
  discount: z.coerce.number().nonnegative().optional().default(0),
  freightCharge: z.coerce.number().nonnegative().optional().default(0),
  grandTotal: z.coerce.number().positive(),
  receivedAmount: z.coerce.number().nonnegative().optional().default(0),
  cashAmount: z.coerce.number().nonnegative().optional().default(0),
  upiAmount: z.coerce.number().nonnegative().optional().default(0),
  bankAmount: z.coerce.number().nonnegative().optional().default(0),
  balanceAmount: z.coerce.number().nonnegative().optional().default(0),
  paymentStatus: z.enum(["UNPAID", "PARTIALLY_PAID", "PAID"]).default("UNPAID"),
  remarks: z.string().optional(),
  items: z.array(SaleItemSchema).min(1, { message: "At least one sale item is required" }),
}).superRefine((val, ctx) => {
  const isWalkIn = !val.customerId;
  if (isWalkIn && !val.walkInName?.trim()) {
    ctx.addIssue({
      code: z.ZodIssueCode.custom,
      path: ["walkInName"],
      message: "Customer Name is required for walk-in customer",
    });
  }
});

const parseSaleRequestBody = (req: Request) => {
  const body = { ...req.body };
  if (typeof body.items === "string") {
    try {
      body.items = JSON.parse(body.items);
    } catch {
      body.items = null;
    }
  }
  return body;
};

export const createSale = async (req: Request, res: Response): Promise<void> => {
  const body = parseSaleRequestBody(req);
  const parsed = CreateSaleSchema.safeParse(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 files = (req.files as Express.Multer.File[]) || [];
    const uploadedImages = files.map((file) => file.filename);

    const data = {
      ...parsed.data,
      customerId: parsed.data.customerId ? BigInt(parsed.data.customerId) : null,
      createdBy: req.user?.id ? safeParseBigInt(req.user.id) : null,
      images: uploadedImages,
      items: parsed.data.items.map((item) => ({
        ...item,
        purchaseId: BigInt(item.purchaseId),
      })),
    };
    const sale = await saleService.createSale(data);
    res.status(201).json({ message: "Sale created successfully", result: sale });
  } catch (err: any) {
    res.status(400).json({ message: err.message });
  }
};

export const updateSale = async (req: Request, res: Response): Promise<void> => {
  const { id } = req.params;
  if (!id) {
    res.status(400).json({ message: "Sale ID is required" });
    return;
  }
  const body = parseSaleRequestBody(req);
  const parsed = CreateSaleSchema.safeParse(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 files = (req.files as Express.Multer.File[]) || [];
    const newUploadedImages = files.map((file) => file.filename);

    let existingImages: string[] = [];
    if (req.body.existingImages) {
      try {
        existingImages = typeof req.body.existingImages === "string"
          ? JSON.parse(req.body.existingImages)
          : req.body.existingImages;
        if (!Array.isArray(existingImages)) existingImages = [];
      } catch {
        existingImages = [];
      }
    }

    const allImages = [...existingImages, ...newUploadedImages];

    const data = {
      ...parsed.data,
      customerId: parsed.data.customerId ? BigInt(parsed.data.customerId) : null,
      createdBy: req.user?.id ? safeParseBigInt(req.user.id) : null,
      images: allImages,
      items: parsed.data.items.map((item) => ({
        ...item,
        purchaseId: BigInt(item.purchaseId),
      })),
    };
    const saleId = BigInt(String(id));
    const sale = await saleService.updateSale(saleId, data);
    res.status(200).json({ message: "Sale updated successfully", result: sale });
  } catch (err: any) {
    res.status(400).json({ message: err.message });
  }
};

export const getSales = async (req: Request, res: Response): Promise<void> => {
  try {
    const page = Number(req.query.page) || 1;
    const limit = Number(req.query.limit) || 10;
    const customerId = safeParseBigInt(req.query.customerId);
    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 saleType = req.query.saleType ? String(req.query.saleType) : undefined;

    const result = await saleService.getSales(page, limit, customerId, dateFrom, dateTo, search, saleType);
    res.json({ message: "Sales fetched successfully", result });
  } catch (err: any) {
    res.status(500).json({ message: err.message });
  }
};

export const getSaleById = async (req: Request, res: Response): Promise<void> => {
  try {
    const id = safeParseBigInt(req.params.id);
    if (!id) {
      res.status(400).json({ message: "Invalid sale ID format" });
      return;
    }
    const sale = await saleService.getSaleById(id);
    res.json({ message: "Sale fetched successfully", result: sale });
  } catch (err: any) {
    res.status(404).json({ message: err.message });
  }
};

export const cancelSale = async (req: Request, res: Response): Promise<void> => {
  try {
    const id = safeParseBigInt(req.params.id);
    if (!id) {
      res.status(400).json({ message: "Invalid sale ID format" });
      return;
    }
    const sale = await saleService.cancelSale(id);
    res.json({ message: "Sale cancelled successfully", result: sale });
  } catch (err: any) {
    res.status(400).json({ message: err.message });
  }
};

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

export const getAvailableLots = async (req: Request, res: Response): Promise<void> => {
  try {
    const vegetableId = safeParseBigInt(req.query.vegetableId);
    const lots = await saleService.getAvailableLots(vegetableId);
    res.json({ message: "Available lots fetched", result: lots });
  } catch (err: any) {
    res.status(500).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 saleType = req.query.saleType ? String(req.query.saleType) : undefined;

    const sales = await saleService.getSalesForExport(dateFrom, dateTo, search, saleType);
    if (!sales || sales.length === 0) {
      res.status(400).json({ message: "No data available to export" });
      return;
    }
    await WholeSalesListPdf.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 printInvoice = async (
  req: Request,
  res: Response
): Promise<void> => {
  try {
    const id = safeParseBigInt(req.params.id);
    if (!id) {
      res.status(400).json({ message: "Invalid sale ID format" });
      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

    await saleService.generateInvoice(id, res, { lang, printSign });
  } catch (error: any) {
    res.status(500).json({
      message: error.message,
    });
  }
};

export const sendWhatsappInvoice = async (
  req: Request,
  res: Response
): Promise<void> => {
  try {
    const id = safeParseBigInt(req.params.id);
    if (!id) {
      res.status(400).json({ message: "Invalid sale ID format" });
      return;
    }
    
    await saleService.sendWhatsappInvoice(id);
    res.json({ message: "WhatsApp message sent successfully" });
  } catch (error: any) {
    res.status(400).json({ message: error.message });
  }
};
