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

const createDistrictSchema = z.object({
  name: z.string({
    message: "District name must be a string",
  }).trim().min(1, "District name cannot be empty"),
  tamilName: z.string({
    message: "Tamil name must be a string",
  }).optional(),
  status: z.boolean({
    message: "Status must be a boolean",
  }).optional(),
  stateId: z.union([z.string(), z.number()]).transform(val => BigInt(val)),
});

const updateDistrictSchema = z.object({
  name: z.string({
    message: "District name must be a string",
  }).trim().min(1, "District name cannot be empty").optional(),
  tamilName: z.string({
    message: "Tamil name must be a string",
  }).optional(),
  status: z.boolean({
    message: "Status must be a boolean",
  }).optional(),
  stateId: z.union([z.string(), z.number()]).transform(val => BigInt(val)).optional(),
});

export class DistrictController {
  constructor(private readonly districtService = new DistrictService()) { }

  create = async (req: Request, res: Response): Promise<void> => {
    try {
      const validatedData = createDistrictSchema.parse(req.body);
      const result = await this.districtService.createDistrict({
        ...validatedData,
        createdBy: req.user?.id ? BigInt(req.user.id) : undefined,
      } as any);
      res.status(201).json({ message: "District 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(400).json({ message: "District Tamil name already exists" });
          return;
        }
        res.status(400).json({ message: "District name already exists" });
        return;
      }
      const message = error instanceof Error ? error.message : "Failed to create district";
      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;

      let stateIdFilter: bigint | undefined = undefined;
      if (req.query.stateId) {
        stateIdFilter = BigInt(req.query.stateId as string);
      }

      const result = await this.districtService.getDistricts(page, limit, search, statusFilter, stateIdFilter);
      res.status(200).json({ message: "Districts fetched successfully", result });
    } catch (error) {
      const message = error instanceof Error ? error.message : "Failed to fetch districts";
      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.districtService.getDistrictById(id);
      res.status(200).json(result);
    } catch (error) {
      const message = error instanceof Error ? error.message : "District 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 = updateDistrictSchema.parse(req.body);
      const result = await this.districtService.updateDistrict(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(400).json({ message: "District Tamil name already exists" });
          return;
        }
        res.status(400).json({ message: "District name already exists" });
        return;
      }
      const message = error instanceof Error ? error.message : "Failed to update district";
      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.districtService.deleteDistrict(id);
      res.status(200).json({ message: "District 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 district";
      res.status(400).json({ message });
    }
  };

  downloadTemplate = async (_req: Request, res: Response): Promise<void> => {
    try {
      const buffer = await this.districtService.generateImportTemplate();
      res.setHeader("Content-Type", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
      res.setHeader("Content-Disposition", 'attachment; filename="district-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.districtService.importBulk(req.file.buffer, createdBy);
      res.status(200).json(result);
    } catch (error) {
      const message = error instanceof Error ? error.message : "Failed to import districts";
      res.status(400).json({ message });
    }
  };
}

