import { FastBillingRepository } from "../repositories/FastBillingRepository.js";
import ExcelJS from "exceljs";
import type { Response } from "express";
import { FastBillingInvoicePdf } from "../utils/pdf/FastBillingInvoicePdf.js";
import { FastBillingListPdf } from "../utils/pdf/FastBillingListPdf.js";

const getUnitCategory = (unit?: string): "kg" | "box" | "thaar" | "pieces" | "other" => {
  const u = (unit || "").toString().trim().toLowerCase();
  if (!u) return "other";
  if (u === "kg" || u.includes("kilogram")) return "kg";
  if (u.includes("box") || u.includes("bag")) return "box";
  if (u.includes("thaar") || u.includes("thar")) return "thaar";
  if (u.includes("piece") || u.includes("pcs") || u === "pc") return "pieces";
  return "other";
};

const calculateItemTotal = (item: any): number => {
  if (!item) return 0;
  const category = getUnitCategory(item.unit);
  const rate = Number(item.ratePerUnit) || 0;

  switch (category) {
    case "kg": {
      const qty = Number(item.quantity) || 0;
      const extra = Number(item.extraCharges) || 0;
      return qty * rate + extra;
    }
    case "box": {
      if (item.rateType === "perBox") {
        const boxes = Number(item.numberOfBoxes) || 0;
        return boxes * rate;
      }
      if (item.rateType === "perKg") {
        const qty = Number(item.quantity) || 0;
        return qty * rate;
      }
      return 0;
    }
    case "thaar": {
      const thaar = Number(item.numberOfThaar) || 0;
      return thaar * rate;
    }
    case "pieces": {
      const pieces = Number(item.numberOfPieces) || 0;
      return pieces * rate;
    }
    default: {
      const qty = Number(item.quantity) || 0;
      return qty * rate;
    }
  }
};

const mapItemData = (item: any) => {
  const calculatedTotal = calculateItemTotal(item);
  return {
    vegetableName: item.vegetableName,
    unit: item.unit || "Kg",
    quantity: item.quantity !== undefined && item.quantity !== null ? item.quantity : null,
    rateType: item.rateType || null,
    numberOfBoxes: item.numberOfBoxes !== undefined && item.numberOfBoxes !== null ? item.numberOfBoxes : null,
    numberOfThaar: item.numberOfThaar !== undefined && item.numberOfThaar !== null ? item.numberOfThaar : null,
    numberOfPieces: item.numberOfPieces !== undefined && item.numberOfPieces !== null ? item.numberOfPieces : null,
    extraCharges: item.extraCharges !== undefined && item.extraCharges !== null ? item.extraCharges : 0,
    ratePerUnit: item.ratePerUnit,
    totalAmount: item.totalAmount !== undefined && item.totalAmount !== null ? item.totalAmount : calculatedTotal
  };
};

export class FastBillingService {
  constructor(private readonly fastBillingRepository = new FastBillingRepository()) {}

  async createFastBilling(data: any) {
    if (!data.billNo) {
      data.billNo = await this.fastBillingRepository.generateBillNo();
    }
    
    // Construct items payload for Prisma nested write
    const itemsData = data.items?.map(mapItemData) || [];
    const calculatedBillTotal = itemsData.reduce((acc: number, item: any) => acc + Number(item.totalAmount || 0), 0);

    const createData: any = {
      billNo: data.billNo,
      billDate: data.billDate ? new Date(data.billDate) : new Date(),
      customerName: data.customerName,
      totalAmount: data.totalAmount !== undefined ? data.totalAmount : calculatedBillTotal,
      remarks: data.remarks,
      items: {
        create: itemsData
      }
    };

    if (data.createdBy) {
      createData.createdBy = data.createdBy;
    }

    return this.fastBillingRepository.create(createData);
  }

  async getFastBillings(page: number = 1, limit: number = 10, startDateStr?: string, endDateStr?: string, search?: string) {
    const skip = (page - 1) * limit;
    
    let startDate: Date | undefined;
    let endDate: Date | undefined;

    if (startDateStr) startDate = new Date(startDateStr);
    if (endDateStr) endDate = new Date(endDateStr);

    const { items, total } = await this.fastBillingRepository.findAll(skip, limit, startDate, endDate, search);
    
    return {
      data: items,
      meta: {
        total,
        page,
        limit,
        totalPages: Math.ceil(total / limit),
      },
    };
  }

  async getFastBillingById(id: bigint) {
    const fastBilling = await this.fastBillingRepository.findById(id);
    if (!fastBilling) {
      throw new Error("Fast Billing entry not found");
    }
    return fastBilling;
  }

  async updateFastBilling(id: bigint, data: any) {
    const existing = await this.fastBillingRepository.findById(id);
    if (!existing) {
      throw new Error("Fast Billing entry not found");
    }

    const updateData: any = {};
    if (data.billDate) updateData.billDate = new Date(data.billDate);
    if (data.customerName !== undefined) updateData.customerName = data.customerName;
    if (data.totalAmount !== undefined) updateData.totalAmount = data.totalAmount;
    if (data.remarks !== undefined) updateData.remarks = data.remarks;

    // To handle items update, delete old items and recreate new ones
    if (data.items) {
      const itemsData = data.items.map(mapItemData);
      updateData.items = {
        deleteMany: {},
        create: itemsData
      };
      if (data.totalAmount === undefined) {
        updateData.totalAmount = itemsData.reduce((acc: number, item: any) => acc + Number(item.totalAmount || 0), 0);
      }
    }

    return this.fastBillingRepository.update(id, updateData);
  }

  async deleteFastBilling(id: bigint) {
    const existing = await this.fastBillingRepository.findById(id);
    if (!existing) {
      throw new Error("Fast Billing entry not found");
    }
    return this.fastBillingRepository.delete(id);
  }

  async exportToExcel(startDateStr?: string, endDateStr?: string, search?: string): Promise<Buffer> {
    let startDate: Date | undefined;
    let endDate: Date | undefined;

    if (startDateStr) startDate = new Date(startDateStr);
    if (endDateStr) endDate = new Date(endDateStr);

    // Fetch all items without pagination
    const { items } = await this.fastBillingRepository.findAll(0, Number.MAX_SAFE_INTEGER, startDate, endDate, search);

    const workbook = new ExcelJS.Workbook();
    const worksheet = workbook.addWorksheet('Fast Billing');

    worksheet.columns = [
      { header: 'Bill No', key: 'billNo', width: 15 },
      { header: 'Date', key: 'billDate', width: 15 },
      { header: 'Customer Name', key: 'customerName', width: 30 },
      { header: 'Bill Total', key: 'totalAmount', width: 15 },
      { header: 'Remarks', key: 'remarks', width: 30 },
      { header: 'Vegetable Name', key: 'vegetableName', width: 20 },
      { header: 'Unit', key: 'unit', width: 10 },
      { header: 'Quantity (Kg)', key: 'quantity', width: 15 },
      { header: 'No. of Boxes/Bags', key: 'numberOfBoxes', width: 18 },
      { header: 'No. of Thaar', key: 'numberOfThaar', width: 15 },
      { header: 'No. of Pieces', key: 'numberOfPieces', width: 15 },
      { header: 'Rate Type', key: 'rateType', width: 15 },
      { header: 'Rate', key: 'ratePerUnit', width: 15 },
      { header: 'Extra Charges', key: 'extraCharges', width: 15 },
      { header: 'Item Total', key: 'itemTotal', width: 15 },
    ];

    // Add rows
    items.forEach((bill: any) => {
      const billDateStr = new Date(bill.billDate).toLocaleDateString('en-GB');
      
      if (bill.items && bill.items.length > 0) {
        bill.items.forEach((item: any) => {
          worksheet.addRow({
            billNo: bill.billNo,
            billDate: billDateStr,
            customerName: bill.customerName,
            totalAmount: Number(bill.totalAmount),
            remarks: bill.remarks || '',
            vegetableName: item.vegetableName,
            unit: item.unit,
            quantity: item.quantity !== null && item.quantity !== undefined ? Number(item.quantity) : '-',
            numberOfBoxes: item.numberOfBoxes !== null && item.numberOfBoxes !== undefined ? Number(item.numberOfBoxes) : '-',
            numberOfThaar: item.numberOfThaar !== null && item.numberOfThaar !== undefined ? Number(item.numberOfThaar) : '-',
            numberOfPieces: item.numberOfPieces !== null && item.numberOfPieces !== undefined ? Number(item.numberOfPieces) : '-',
            rateType: item.rateType === 'perBox' ? 'Rate / Box' : (item.rateType === 'perKg' ? 'Rate / Kg' : '-'),
            ratePerUnit: Number(item.ratePerUnit),
            extraCharges: item.extraCharges !== null && item.extraCharges !== undefined ? Number(item.extraCharges) : 0,
            itemTotal: Number(item.totalAmount)
          });
        });
      } else {
        worksheet.addRow({
          billNo: bill.billNo,
          billDate: billDateStr,
          customerName: bill.customerName,
          totalAmount: Number(bill.totalAmount),
          remarks: bill.remarks || '',
          vegetableName: '-',
          unit: '-',
          quantity: '-',
          numberOfBoxes: '-',
          numberOfThaar: '-',
          numberOfPieces: '-',
          rateType: '-',
          ratePerUnit: 0,
          extraCharges: 0,
          itemTotal: 0
        });
      }
    });

    // Format headers
    worksheet.getRow(1).font = { bold: true };
    
    // Auto-fit columns is handled by widths above

    const buffer = await workbook.xlsx.writeBuffer();
    return buffer as any;
  }

  async exportInvoicePdf(id: bigint, res: Response, options?: { lang?: string; printSign?: boolean }) {
    const fastBilling = await this.fastBillingRepository.findById(id);
    if (!fastBilling) {
      throw new Error("Fast Billing entry not found");
    }
    await FastBillingInvoicePdf.generate(fastBilling, res, options);
  }

  async exportListPdf(res: Response, startDateStr?: string, endDateStr?: string, search?: string) {
    let startDate: Date | undefined;
    let endDate: Date | undefined;

    if (startDateStr) startDate = new Date(startDateStr);
    if (endDateStr) endDate = new Date(endDateStr);

    const { items } = await this.fastBillingRepository.findAll(0, Number.MAX_SAFE_INTEGER, startDate, endDate, search);
    await FastBillingListPdf.generate(items, res, { startDate: startDateStr, endDate: endDateStr, search });
  }
}
