import type { NextFunction, Request, Response } from "express";
import { z } from "zod";
import { SupplierPaymentService } from "../services/SupplierPaymentService.js";
import { generateSupplierPaymentExcel } from "../utils/excel/SupplierPaymentExcelExport.js";
import { SupplierPaymentPdf } from "../utils/pdf/SupplierPaymentPdf.js";
import { safeParseBigInt } from "../utils/parseHelper.js";

const createPaymentSchema = z.object({
  supplierId: z.number().int().positive().or(z.string().regex(/^\d+$/).transform(Number)),
  paymentDate: z.string().datetime().or(z.string().regex(/^\d{4}-\d{2}-\d{2}$/)).transform(str => new Date(str)),
  amount: z.number().positive("Amount must be greater than 0").or(z.string().regex(/^\d+(\.\d+)?$/).transform(Number).refine(n => n > 0, "Amount must be greater than 0")),
  paymentMode: z.enum(["CASH", "BANK", "UPI"]),
  referenceNo: z.string().max(100).optional(),
  remarks: z.string().optional()
});

const updatePaymentSchema = z.object({
  paymentDate: z.string().datetime().or(z.string().regex(/^\d{4}-\d{2}-\d{2}$/)).transform(str => new Date(str)).optional(),
  amount: z.number().positive("Amount must be greater than 0").or(z.string().regex(/^\d+(\.\d+)?$/).transform(Number).refine(n => n > 0, "Amount must be greater than 0")).optional(),
  paymentMode: z.enum(["CASH", "BANK", "UPI"]).optional(),
  referenceNo: z.string().max(100).optional(),
  remarks: z.string().optional()
});

export class SupplierPaymentController {
  constructor(private readonly supplierPaymentService = new SupplierPaymentService()) {}

  create = async (req: Request, res: Response): Promise<void> => {
    try {
      const validatedData = createPaymentSchema.parse(req.body);
      const result = await this.supplierPaymentService.createPayment({
        ...validatedData,
        supplierId: BigInt(validatedData.supplierId),
        createdBy: req.user?.id ? safeParseBigInt(req.user.id) : undefined,
      });
      res.status(201).json({ message: "Supplier payment recorded successfully", result });
    } catch (error: any) {
      if (error instanceof z.ZodError) {
        const errors: Record<string, string> = {};
        error.issues.forEach((err) => {
          if (err.path[0]) {
            errors[err.path[0].toString()] = err.message;
          }
        });
        res.status(422).json({ message: "Validation failed", errors });
        return;
      }
      const message = error instanceof Error ? error.message : "Failed to record supplier payment";
      res.status(400).json({ message });
    }
  };

  getAll = async (req: Request, res: Response): Promise<void> => {
    try {
      const page = parseInt(req.query.page as string) || 1;
      const limit = parseInt(req.query.limit as string) || 10;
      const supplierId = safeParseBigInt(req.query.supplierId);
      const dateFrom = req.query.dateFrom as string;
      const dateTo = req.query.dateTo as string;
      const search = req.query.search ? String(req.query.search) : undefined;

      const result = await this.supplierPaymentService.getPayments(
        page, limit, supplierId, dateFrom, dateTo, search
      );
      res.status(200).json({ message: "Supplier payments fetched successfully", result });
    } catch (error) {
      const message = error instanceof Error ? error.message : "Failed to fetch supplier payments";
      res.status(500).json({ message });
    }
  };

  getById = async (req: Request, res: Response): Promise<void> => {
    try {
      const id = safeParseBigInt(req.params.id);
      if (!id) {
        res.status(400).json({ message: "Invalid payment ID format" });
        return;
      }
      const result = await this.supplierPaymentService.getPaymentById(id);
      res.status(200).json(result);
    } catch (error) {
      const message = error instanceof Error ? error.message : "Supplier payment not found";
      res.status(404).json({ message });
    }
  };

  update = async (req: Request, res: Response): Promise<void> => {
    try {
      const id = safeParseBigInt(req.params.id);
      if (!id) {
        res.status(400).json({ message: "Invalid payment ID format" });
        return;
      }
      const validatedData = updatePaymentSchema.parse(req.body);
      const userId = req.user?.id ? safeParseBigInt(req.user.id) : undefined;
      const result = await this.supplierPaymentService.updatePayment(id, validatedData, userId);
      res.status(200).json({ message: "Supplier payment updated successfully", result });
    } catch (error: any) {
      if (error instanceof z.ZodError) {
        const errors: Record<string, string> = {};
        error.issues.forEach((err) => {
          if (err.path[0]) {
            errors[err.path[0].toString()] = err.message;
          }
        });
        res.status(422).json({ message: "Validation failed", errors });
        return;
      }
      const message = error instanceof Error ? error.message : "Failed to update supplier payment";
      res.status(400).json({ message });
    }
  };

  delete = async (req: Request, res: Response): Promise<void> => {
    try {
      const id = safeParseBigInt(req.params.id);
      if (!id) {
        res.status(400).json({ message: "Invalid payment ID format" });
        return;
      }
      const userId = req.user?.id ? safeParseBigInt(req.user.id) : undefined;
      await this.supplierPaymentService.deletePayment(id, userId);
      res.status(200).json({ message: "Supplier payment deleted successfully" });
    } catch (error: any) {
      const message = error instanceof Error ? error.message : "Failed to delete supplier payment";
      res.status(400).json({ message });
    }
  };

  exportExcel = async (
    req: Request,
    res: Response,
    next: NextFunction
  ): Promise<void> => {
    try {
      const supplierId = safeParseBigInt(req.query.supplierId);
      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 payments = await this.supplierPaymentService.getPaymentsForExport(
        supplierId,
        dateFrom,
        dateTo,
        search
      );

      if (!payments || payments.length === 0) {
        res.status(400).json({ message: "No data available to export" });
        return;
      }

      const workbook = await generateSupplierPaymentExcel(payments);

      res.setHeader(
        "Content-Type",
        "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
      );
      res.setHeader(
        "Content-Disposition",
        `attachment; filename=supplier-payments-${Date.now()}.xlsx`
      );

      await workbook.xlsx.write(res);
      res.end();
    } catch (error) {
      next(error);
    }
  };

  exportPdf = async (
    req: Request,
    res: Response,
    next: NextFunction
  ): Promise<void> => {
    try {
      const supplierId = safeParseBigInt(req.query.supplierId);
      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 payments = await this.supplierPaymentService.getPaymentsForExport(
        supplierId,
        dateFrom,
        dateTo,
        search
      );

      if (!payments || payments.length === 0) {
        res.status(400).json({ message: "No data available to export" });
        return;
      }

      await SupplierPaymentPdf.generate(payments, res, {
        ...(search ? { search } : {}),
        ...(dateFrom ? { dateFrom } : {}),
        ...(dateTo ? { dateTo } : {})
      });
    } catch (error) {
      next(error);
    }
  };

  getMeta = async (_req: Request, res: Response): Promise<void> => {
    try {
      const meta = await this.supplierPaymentService.getPaymentMeta();
      res.status(200).json({ message: "Supplier payment meta fetched successfully", result: meta });
    } catch (error) {
      const message = error instanceof Error ? error.message : "Failed to fetch supplier payment meta";
      res.status(500).json({ message });
    }
  };

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


