import type { Request, Response } from "express";
import { z } from "zod";
import { ArrivalService } from "../services/ArrivalService.js";

const createArrivalSchema = z.object({
  vegetableId: z.number().int().positive().or(z.string().regex(/^\d+$/).transform(Number)),
  lotNo: z.string().trim().min(1, "Lot number cannot be empty").max(100),
  supplierId: z.number().int().positive().or(z.string().regex(/^\d+$/).transform(Number)).optional().nullable(),
  cashSupplierName: z.string().max(255).optional().nullable(),
  billNo: z.string().max(50).optional().nullable(),
  unit: z.enum(["KG", "BAG", "BOX", "THAAR", "PIECES"]).default("KG"),
  bags: z.number().nonnegative().optional().or(z.string().regex(/^\d+(\.\d+)?$/).transform(Number).optional()),
  weightPerBag: z.number().positive().optional().or(z.string().regex(/^\d+(\.\d+)?$/).transform(Number).optional()),
  quantity: z.number().min(0).optional().or(z.string().regex(/^\d+(\.\d+)?$/).transform(Number).optional()),
  freight: z.number().min(0).optional().nullable().or(z.string().regex(/^\d+(\.\d+)?$/).transform(Number).optional().nullable()),
  extraFreight: z.number().min(0).optional().nullable().or(z.string().regex(/^\d+(\.\d+)?$/).transform(Number).optional().nullable()),
  associationFee: z.number().min(0).optional().nullable().or(z.string().regex(/^\d+(\.\d+)?$/).transform(Number).optional().nullable()),
  unloadingFee: z.number().min(0).optional().nullable().or(z.string().regex(/^\d+(\.\d+)?$/).transform(Number).optional().nullable()),
  miscellaneousFee: z.number().min(0).optional().nullable().or(z.string().regex(/^\d+(\.\d+)?$/).transform(Number).optional().nullable()),
  dateOfDespatch: z.string().optional().nullable(),
  arrivalDate: z.string().optional().nullable(),
  placeOfDespatch: z.string().max(255).optional().nullable(),
  placeOfArrival: z.string().max(255).optional().nullable(),
  truckNo: z.string().max(50).optional().nullable(),
  agentName: z.string().max(255).optional().nullable(),
  advanceAmount: z.number().min(0).optional().nullable().or(z.string().regex(/^\d+(\.\d+)?$/).transform(Number).optional().nullable()),
  remarks: z.string().optional().nullable(),
});

export class ArrivalController {
  constructor(private readonly arrivalService = new ArrivalService()) {}

  checkLot = async (req: Request, res: Response): Promise<void> => {
    try {
      const lotNo = req.query.lotNo as string | undefined;
      const vegetableId = req.query.vegetableId as string | undefined;
      const excludeId = req.query.excludeId as string | undefined;

      if (!lotNo) {
        res.status(400).json({ success: false, message: "lotNo is required." });
        return;
      }

      const result = await this.arrivalService.checkLotAvailability(
        lotNo,
        vegetableId ? BigInt(vegetableId) : undefined,
        excludeId ? BigInt(excludeId) : undefined
      );

      res.status(200).json({ success: true, data: result });
    } catch (error: any) {
      res.status(500).json({ success: false, message: error.message || "Failed to check lot availability." });
    }
  };

  create = async (req: Request, res: Response): Promise<void> => {
    try {
      const validated = createArrivalSchema.parse(req.body);
      const userId = (req as any).user?.id ? BigInt((req as any).user.id) : undefined;
      const arrival = await this.arrivalService.createArrival(validated, userId);

      res.status(201).json({
        success: true,
        message: `Arrival Lot ${arrival.lotNo} created successfully.`,
        data: arrival,
      });
    } 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({ success: false, message: "Validation failed", errors });
        return;
      }
      res.status(400).json({ success: false, message: error.message || "Failed to create arrival lot." });
    }
  };

  list = async (req: Request, res: Response): Promise<void> => {
    try {
      const result = await this.arrivalService.getAllArrivals(req.query as any);
      res.status(200).json({ success: true, ...result });
    } catch (error: any) {
      res.status(500).json({ success: false, message: error.message || "Failed to list arrivals." });
    }
  };

  getById = async (req: Request, res: Response): Promise<void> => {
    try {
      const id = String(req.params.id);
      const arrival = await this.arrivalService.getArrivalById(BigInt(id));
      res.status(200).json({ success: true, data: arrival });
    } catch (error: any) {
      res.status(404).json({ success: false, message: error.message || "Arrival lot not found." });
    }
  };

  update = async (req: Request, res: Response): Promise<void> => {
    try {
      const id = String(req.params.id);
      const arrival = await this.arrivalService.updateArrival(BigInt(id), req.body);
      res.status(200).json({
        success: true,
        message: "Arrival updated successfully.",
        data: arrival,
      });
    } catch (error: any) {
      res.status(400).json({ success: false, message: error.message || "Failed to update arrival." });
    }
  };

  delete = async (req: Request, res: Response): Promise<void> => {
    try {
      const id = String(req.params.id);
      await this.arrivalService.deleteArrival(BigInt(id));
      res.status(200).json({ success: true, message: "Arrival lot deleted successfully." });
    } catch (error: any) {
      res.status(400).json({ success: false, message: error.message || "Failed to delete arrival lot." });
    }
  };
}
