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

// All string fields allow up to 150 characters. HSN code must be a number (max 15 digits).
const createHsnCodeSchema = z
  .union([z.number(), z.string()], {
    message: "HSN code is required",
  })
  .transform((val) => String(val).trim())
  .refine((val) => val.length > 0, {
    message: "HSN code is required",
  })
  .refine((val) => /^\d+$/.test(val), {
    message: "HSN code must be a positive number",
  })
  .refine((val) => !/^0+$/.test(val), {
    message: "HSN code cannot be 0",
  })
  .refine((val) => val.length <= 15, {
    message: "HSN code cannot exceed 15 characters",
  });

const updateHsnCodeSchema = z.preprocess(
  (val) => (val === "" || val === null ? undefined : val),
  z
    .union([z.number(), z.string()], {
      message: "HSN code must be a number",
    })
    .transform((val) => String(val).trim())
    .refine((val) => /^\d+$/.test(val), {
      message: "HSN code must be a positive number",
    })
    .refine((val) => !/^0+$/.test(val), {
      message: "HSN code cannot be 0",
    })
    .refine((val) => val.length <= 15, {
      message: "HSN code cannot exceed 15 characters",
    })
    .optional()
);

const createVegetableSchema = z.object({
  name: z.string({
    message: "Vegetable name must be a string",
  }).trim().min(1, "Vegetable name cannot be empty").max(150, "Vegetable name cannot exceed 150 characters"),
  tamilName: z.string({
    message: "Tamil name must be a string",
  }).max(150, "Tamil name cannot exceed 150 characters").optional(),
  status: z.boolean({
    message: "Status must be a boolean",
  }).optional(),
  hindiName: z.string({
    message: "Hindi name must be a string",
  }).max(150, "Hindi name cannot exceed 150 characters").optional(),
  hsnCode: createHsnCodeSchema,
  isFlexible: z.boolean().optional(),
});

const updateVegetableSchema = z.object({
  name: z.string({
    message: "Vegetable name must be a string",
  }).trim().min(1, "Vegetable name cannot be empty").max(150, "Vegetable name cannot exceed 150 characters").optional(),
  tamilName: z.string({
    message: "Tamil name must be a string",
  }).max(150, "Tamil name cannot exceed 150 characters").optional(),
  status: z.boolean({
    message: "Status must be a boolean",
  }).optional(),
  hindiName: z.string({
    message: "Hindi name must be a string",
  }).max(150, "Hindi name cannot exceed 150 characters").optional(),
  hsnCode: updateHsnCodeSchema,
  isFlexible: z.boolean().optional(),
});

export class VegetableController {
  constructor(private readonly vegetableService = new VegetableService()) { }

  create = async (req: Request, res: Response): Promise<void> => {
    try {
      const validatedData = createVegetableSchema.parse(req.body);
      const result = await this.vegetableService.createVegetable({
        ...validatedData,
        createdBy: req.user?.id ? BigInt(req.user.id) : undefined,
      } as any);
      res.status(201).json({ message: "Vegetable created successfully", result });
    } catch (error) {
      if (error instanceof z.ZodError) {
        const errors: Record<string, string> = {};
        error?.issues?.forEach((err: any) => {
          if (err.path[0]) {
            errors[err.path[0].toString()] = err.message;
          }
        });
        res.status(422).json({ message: "Validation failed", errors });
        return;
      }
      if ((error as any)?.code === "P2002") {
        const target = (error as any)?.meta?.target;
        const targetStr = Array.isArray(target) ? target.join(",") : String(target || "");
        if (targetStr.includes("tamil_name") || targetStr.includes("tamilName")) {
          res.status(409).json({ message: "Vegetable Tamil name already exists" });
          return;
        }
        if (targetStr.includes("hindi_name") || targetStr.includes("hindiName")) {
          res.status(409).json({ message: "Vegetable Hindi name already exists" });
          return;
        }
        res.status(409).json({ message: "Vegetable name already exists" });
        return;
      }
      const message = error instanceof Error ? error.message : "Failed to create vegetable";
      if (
        message === "Vegetable name already exists" ||
        message === "Vegetable Tamil name already exists" ||
        message === "Vegetable Hindi name already exists"
      ) {
        res.status(409).json({ message });
        return;
      }
      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 search = req.query.search as string;

      let statusFilter: boolean | undefined = undefined;
      if (req.query.status === 'true') statusFilter = true;
      else if (req.query.status === 'false') statusFilter = false;

      const result = await this.vegetableService.getVegetables(page, limit, search, statusFilter);
      res.status(200).json({ message: "Vegetables fetched successfully", result });
    } catch (error) {
      const message = error instanceof Error ? error.message : "Failed to fetch vegetables";
      res.status(500).json({ message });
    }
  };

  getById = async (req: Request, res: Response): Promise<void> => {
    try {
      const id = BigInt(req.params.id as string | number);
      const result = await this.vegetableService.getVegetableById(id);
      res.status(200).json(result);
    } catch (error) {
      const message = error instanceof Error ? error.message : "Vegetable not found";
      res.status(404).json({ message });
    }
  };

  update = async (req: Request, res: Response): Promise<void> => {
    try {
      const id = BigInt(req.params.id as string | number);
      const validatedData = updateVegetableSchema.parse(req.body);
      const result = await this.vegetableService.updateVegetable(id, validatedData as any);
      res.status(200).json(result);
    } catch (error) {
      if (error instanceof z.ZodError) {
        const errors: Record<string, string> = {};
        error?.issues?.forEach((err: any) => {
          if (err.path[0]) {
            errors[err.path[0].toString()] = err.message;
          }
        });
        res.status(422).json({ message: "Validation failed", errors });
        return;
      }
      if ((error as any)?.code === "P2002") {
        const target = (error as any)?.meta?.target;
        const targetStr = Array.isArray(target) ? target.join(",") : String(target || "");
        if (targetStr.includes("tamil_name") || targetStr.includes("tamilName")) {
          res.status(409).json({ message: "Vegetable Tamil name already exists" });
          return;
        }
        if (targetStr.includes("hindi_name") || targetStr.includes("hindiName")) {
          res.status(409).json({ message: "Vegetable Hindi name already exists" });
          return;
        }
        res.status(409).json({ message: "Vegetable name already exists" });
        return;
      }
      const message = error instanceof Error ? error.message : "Failed to update vegetable";
      if (
        message === "Vegetable name already exists" ||
        message === "Vegetable Tamil name already exists" ||
        message === "Vegetable Hindi name already exists"
      ) {
        res.status(409).json({ message });
        return;
      }
      res.status(400).json({ message });
    }
  };

  delete = async (req: Request, res: Response): Promise<void> => {
    try {
      const id = BigInt(req.params.id as string | number);
      await this.vegetableService.deleteVegetable(id);
      res.status(200).json({ message: "Vegetable deleted successfully" });
    } catch (error: any) {
      if (error?.code === "P2003") {
        res.status(409).json({ message: "Cannot be deleted as this is used in another module." });
        return;
      }
      const message = error instanceof Error ? error.message : "Failed to delete vegetable";
      res.status(400).json({ message });
    }
  };

  downloadTemplate = async (_req: Request, res: Response): Promise<void> => {
    try {
      const buffer = await this.vegetableService.generateImportTemplate();
      res.setHeader("Content-Type", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
      res.setHeader("Content-Disposition", 'attachment; filename="vegetable-import-template.xlsx"');
      res.setHeader("Access-Control-Expose-Headers", "Content-Disposition");
      res.send(buffer);
    } catch (error) {
      const message = error instanceof Error ? error.message : "Failed to download import template";
      res.status(500).json({ message });
    }
  };

  importBulk = async (req: Request, res: Response): Promise<void> => {
    try {
      if (!req.file) {
        res.status(400).json({ message: "No file uploaded." });
        return;
      }
      const createdBy = req.user?.id ? BigInt(req.user.id) : undefined;
      const result = await this.vegetableService.importBulk(req.file.buffer, createdBy);
      res.status(200).json(result);
    } catch (error) {
      const message = error instanceof Error ? error.message : "Failed to import vegetables";
      res.status(400).json({ message });
    }
  };
}
