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

// const allocateStockSchema = z.object({
//   purchaseId: z.number().int().positive().or(z.string().regex(/^\d+$/).transform(Number)),
//   allocatedUnit: z.enum(["KG", "BAG", "BOX"]),
//   allocatedQty: z.number().positive("Quantity must be greater than 0").or(z.string().regex(/^\d+(\.\d+)?$/).transform(Number).refine(n => n > 0, "Quantity must be greater than 0")),
//   allocationDate: z.string().datetime().or(z.string().regex(/^\d{4}-\d{2}-\d{2}$/)).transform(str => new Date(str)),
//   remarks: z.string().optional()
// }).strip();

const allocateStockSchema = z.object({
  purchaseId: z.number().int().positive().or(z.string().regex(/^\d+$/).transform(Number)),
  allocatedUnit: z.enum(["KG", "BAG", "BOX", "THAAR", "PIECES"]),
  allocatedQty: z.number().positive("Quantity must be greater than 0").or(z.string().regex(/^\d+(\.\d+)?$/).transform(Number).refine(n => n > 0, "Quantity must be greater than 0")),
  allocatedKg: z.number().positive("KG must be greater than 0").or(z.string().regex(/^\d+(\.\d+)?$/).transform(Number).refine(n => n > 0, "KG must be greater than 0")).optional(),
  allocationDate: z.string().datetime().or(z.string().regex(/^\d{4}-\d{2}-\d{2}$/)).transform(str => new Date(str)),
  remarks: z.string().optional()
}).strip().superRefine((data, ctx) => {
  if (data.allocatedUnit !== 'KG') {
    if (!Number.isInteger(data.allocatedQty)) {
      ctx.addIssue({
        code: z.ZodIssueCode.custom,
        message: `${data.allocatedUnit} must be a whole number`,
        path: ['allocatedQty']
      });
    }
  }
});

export class RetailStockController {
  constructor(private readonly retailStockService = new RetailStockService()) { }

  allocateStock = async (req: Request, res: Response): Promise<void> => {
    try {
      const validatedData = allocateStockSchema.parse(req.body);
      const { allocatedKg, ...restValidatedData } = validatedData;
      const result = await this.retailStockService.allocateStock({
        ...restValidatedData,
        purchaseId: BigInt(validatedData.purchaseId),
        createdBy: BigInt(req.user!.id),
        ...(allocatedKg !== undefined ? { allocatedKg } : {}),
      });

      // Convert BigInts to strings for JSON serialization
      const serializedResult = {
        ...result,
        id: result.id.toString(),
        purchaseId: result.purchaseId.toString(),
        createdBy: result.createdBy?.toString(),
      };

      res.status(201).json({ message: "Stock allocated successfully", result: serializedResult });
    } 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 allocate stock";
      res.status(400).json({ message });
    }
  };

  getAllocations = 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 search = req.query.search as string;

      const result = await this.retailStockService.getAllocations(page, limit, search);
      res.status(200).json({ message: "Allocations fetched successfully", result });
    } catch (error) {
      const message = error instanceof Error ? error.message : "Failed to fetch allocations";
      res.status(500).json({ message });
    }
  };

  getAvailableLots = async (req: Request, res: Response): Promise<void> => {
    try {
      const vegetableIdRaw = req.query.vegetableId;
      const vegetableId = vegetableIdRaw ? BigInt(String(vegetableIdRaw)) : undefined;
      const lots = await this.retailStockService.getAvailableLots(vegetableId);
      res.json({ message: "Available lots fetched", result: lots });
    } catch (error) {
      const message = error instanceof Error ? error.message : "Failed to fetch available lots";
      res.status(500).json({ message });
    }
  };

  getRetailStockSummary = async (_req: Request, res: Response): Promise<void> => {
    try {
      const result = await this.retailStockService.getRetailStockSummary();
      res.json({ message: "Retail stock summary fetched", result });
    } catch (error) {
      const message = error instanceof Error ? error.message : "Failed to fetch retail stock summary";
      res.status(500).json({ message });
    }
  };

  getStockMovements = 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 lotNo = req.query.lotNo as string | undefined;

      const result = await this.retailStockService.getStockMovements(page, limit, lotNo);
      res.json({ message: "Stock movements fetched", result });
    } catch (error) {
      const message = error instanceof Error ? error.message : "Failed to fetch stock movements";
      res.status(500).json({ message });
    }
  };

  getAvailableRetailLots = async (req: Request, res: Response): Promise<void> => {
    try {
      const vegetableIdRaw = req.query.vegetableId;
      const vegetableId = vegetableIdRaw ? BigInt(String(vegetableIdRaw)) : undefined;
      const lots = await this.retailStockService.getAvailableRetailLots(vegetableId);
      res.json({ message: "Available retail lots fetched", result: lots });
    } catch (error) {
      const message = error instanceof Error ? error.message : "Failed to fetch available retail lots";
      res.status(500).json({ message });
    }
  };

  recordSpoilage = async (req: Request, res: Response): Promise<void> => {
    try {
      const { retailAllocationId, lotNo, quantity, spoilageDate, reason, remarks } = req.body;
      const payload: {
        retailAllocationId?: bigint;
        lotNo?: string;
        quantity: number;
        spoilageDate: Date;
        reason?: string;
        remarks?: string;
        createdBy: bigint;
      } = {
        quantity: Number(quantity),
        spoilageDate: new Date(spoilageDate),
        createdBy: BigInt(req.user!.id),
      };
      if (retailAllocationId) payload.retailAllocationId = BigInt(retailAllocationId);
      if (lotNo) payload.lotNo = String(lotNo);
      if (reason) payload.reason = String(reason);
      if (remarks) payload.remarks = String(remarks);

      const result = await this.retailStockService.recordSpoilage(payload);
      res.status(201).json({ message: "Retail stock spoilage recorded successfully", result });
    } catch (error: any) {
      const message = error instanceof Error ? error.message : "Failed to record retail spoilage";
      res.status(400).json({ message });
    }
  };

  returnStock = async (req: Request, res: Response): Promise<void> => {
    try {
      const { retailAllocationId, purchaseId, quantity, returnDate, reason, remarks, returnUnit, returnedBags } = req.body;
      const payload: {
        retailAllocationId?: bigint;
        purchaseId?: bigint;
        quantity: number;
        returnDate: Date;
        reason?: string;
        remarks?: string;
        returnUnit?: string;
        returnedBags?: number;
        createdBy: bigint;
      } = {
        quantity: Number(quantity),
        returnDate: new Date(returnDate),
        createdBy: BigInt(req.user!.id),
      };
      if (retailAllocationId) payload.retailAllocationId = BigInt(retailAllocationId);
      if (purchaseId) payload.purchaseId = BigInt(purchaseId);
      if (reason) payload.reason = String(reason);
      if (remarks) payload.remarks = String(remarks);
      if (returnUnit) payload.returnUnit = String(returnUnit);
      if (returnedBags !== undefined) payload.returnedBags = Number(returnedBags);

      const result = await this.retailStockService.returnStock(payload);
      res.status(201).json({ message: "Stock returned to wholesale lot successfully", result });
    } catch (error: any) {
      const message = error instanceof Error ? error.message : "Failed to return stock";
      res.status(400).json({ message });
    }
  };

  getRetailSpoilages = 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 vegetableIdRaw = req.query.vegetableId;
      const vegetableId = vegetableIdRaw ? BigInt(String(vegetableIdRaw)) : undefined;
      const dateFrom = req.query.from ? new Date(req.query.from as string) : undefined;
      const dateTo = req.query.to ? new Date(req.query.to as string) : undefined;

      const result = await this.retailStockService.getRetailStockSpoilages(page, limit, vegetableId, dateFrom, dateTo);
      res.json({ message: "Retail stock spoilages fetched successfully", result });
    } catch (error) {
      const message = error instanceof Error ? error.message : "Failed to fetch retail stock spoilages";
      res.status(500).json({ message });
    }
  };

  closeRetailLot = async (req: Request, res: Response): Promise<void> => {
    try {
      const { lotNo, purchaseId, id: bodyId } = req.body;
      const paramId = typeof req.params.id === "string" ? req.params.id : undefined;
      const id = paramId ? BigInt(paramId) : bodyId ? BigInt(bodyId) : undefined;
      const result = await this.retailStockService.closeRetailLot({
        id,
        lotNo: lotNo ? String(lotNo) : undefined,
        purchaseId: purchaseId ? BigInt(purchaseId) : undefined,
      });
      res.status(200).json({ message: "Retail lot closed successfully", result });
    } catch (error: any) {
      const message = error instanceof Error ? error.message : "Failed to close retail lot";
      res.status(400).json({ message });
    }
  };
}

