import prisma from "../config/prisma.js";
import { Prisma } from "../generated/prisma/client.js";

export class FinalReportService {
  async getFinalReport(dateStr?: string) {
    const formattedDateStr = dateStr || new Date().toISOString().slice(0, 10);
    const dateParts = formattedDateStr.split("-");
    const year = parseInt(dateParts[0]!, 10);
    const month = parseInt(dateParts[1]!, 10) - 1;
    const day = parseInt(dateParts[2]!, 10);

    const startOfDay = new Date(Date.UTC(year, month, day, 0, 0, 0, 0));
    const nextDay = new Date(Date.UTC(year, month, day + 1, 0, 0, 0, 0));

    // Initialize report structure
    const report = {
      date: formattedDateStr,
      quickBill: {
        cash: 0,
        bank: 0,
        total: 0
      },
      creditBill: {
        cash: 0,
        bank: 0,
        totalAmount: 0,
        details: [] as any[]
      },
      supplierPayment: {
        cash: 0,
        bank: 0,
        previousTotal: 0,
        nowTotal: 0
      },
      customerPayment: {
        cash: 0,
        bank: 0,
        previousTotal: 0,
        nowTotal: 0
      },
      expenses: {
        details: [] as any[],
        total: 0
      },
      finalBalances: {
        cash: 0,
        bank: 0
      },
      purchases: {
        details: [] as any[],
        total: 0
      },
      outstanding: {
        supplier: 0,
        customer: 0
      },
      lotWiseStock: [] as any[]
    };

    // 1. Quick Bills & Credit Bills (Sales)
    const sales = await prisma.sale.findMany({
      where: {
        deletedAt: null,
        invoiceDate: {
          gte: startOfDay,
          lt: nextDay,
        },
      },
      include: {
        customer: true,
        items: true
      }
    });

    sales.forEach(sale => {
      if (!sale.customerId) {
        const cashAmt = Number(sale.cashAmount || 0);
        const bankAmt = Number(sale.upiAmount || 0) + Number(sale.bankAmount || 0);

        report.quickBill.cash += cashAmt;
        report.quickBill.bank += bankAmt;
        report.quickBill.total += (cashAmt + bankAmt);
      } else if (sale.customerId || Number(sale.balanceAmount) > 0) {
        const cashAmt = Number(sale.cashAmount || 0);
        const bankAmt = Number(sale.upiAmount || 0) + Number(sale.bankAmount || 0);
        const amount = Number(sale.grandTotal);
        report.creditBill.cash += cashAmt;
        report.creditBill.bank += bankAmt;
        report.creditBill.totalAmount += amount;
        report.creditBill.details.push({
          name: sale.customer?.name || 'Unknown',
          date: sale.invoiceDate,
          amount: amount,
          cash: cashAmt,
          bank: bankAmt,
          invoiceNo: sale.invoiceNo
        });
      }
    });
    // 2. Purchases
    const dailyPurchases = await prisma.purchase.findMany({
      where: {
        deletedAt: null,
        purchaseDate: {
          gte: startOfDay,
          lt: nextDay,
        },
      },
      include: {
        vegetable: true
      }
    });

    dailyPurchases.forEach(p => {
      const amount = Number(p.grandTotal);
      report.purchases.total += amount;
      
      let qtyStr = '';
      const qtyNum = Number(p.quantity);
      const bagsNum = Number(p.bags);

      if (qtyNum > 0 && bagsNum > 0) {
        qtyStr = `${qtyNum} KG / ${bagsNum} ${p.unit !== 'KG' ? p.unit : 'BAG'}`;
      } else if (qtyNum > 0) {
        qtyStr = `${qtyNum} ${p.unit}`;
      } else if (bagsNum > 0) {
        qtyStr = `${bagsNum} ${p.unit}`;
      } else {
        qtyStr = `0 ${p.unit}`;
      }

      report.purchases.details.push({
        id: p.id,
        vegetableName: p.vegetable?.name || 'Unknown',
        qty: qtyStr,
        rate: Number(p.ratePerUnit),
        total: amount
      });
    });

    // 3. Lot-wise Stock
    const activeLots = await prisma.purchase.findMany({
      where: {
        deletedAt: null,
        status: 'ACTIVE',
      },
      include: {
        vegetable: true,
        supplier: true
      }
    });

    activeLots.forEach(lot => {
      let qtyStr = '';
      const remQty = Number(lot.remainingQuantity);
      const remBags = Number(lot.remainingBags);

      if (remQty > 0 && remBags > 0) {
        qtyStr = `${remQty} KG / ${remBags} ${lot.unit !== 'KG' ? lot.unit : 'BAG'}`;
      } else if (remQty > 0) {
        qtyStr = `${remQty} ${lot.unit}`;
      } else if (remBags > 0) {
        qtyStr = `${remBags} ${lot.unit}`;
      } else {
        qtyStr = `0 ${lot.unit}`;
      }

      report.lotWiseStock.push({
        id: lot.id,
        vegetable: lot.vegetable,
        unit: lot.unit,
        quantity: lot.quantity,
        bags: lot.bags,
        remainingQuantity: lot.remainingQuantity,
        remainingBags: lot.remainingBags,
        lotNo: lot.lotNo,
        purchaseDate: lot.purchaseDate,
        costPerUnit: lot.costPerUnit,
        supplier: lot.supplier,
        cashSupplierName: lot.cashSupplierName,
      });
    });

    // 4. Supplier Payments
    const supplierPayments = await prisma.supplierPayment.findMany({
      where: {
        deletedAt: null,
        paymentDate: { gte: startOfDay, lt: nextDay }
      }
    });

    supplierPayments.forEach(sp => {
      const amt = Number(sp.amount);
      if (sp.paymentMode === 'CASH') {
        report.supplierPayment.cash += amt;
      } else {
        report.supplierPayment.bank += amt;
      }
    });
    report.supplierPayment.nowTotal = report.supplierPayment.cash + report.supplierPayment.bank + report.supplierPayment.previousTotal;

    // 5. Customer Payments (Receipts)
    const customerReceipts = await prisma.customerReceipt.findMany({
      where: {
        deletedAt: null,
        receiptDate: { gte: startOfDay, lt: nextDay }
      }
    });

    customerReceipts.forEach(cr => {
      const amt = Number(cr.amount);
      if (cr.paymentMode === 'CASH') {
        report.customerPayment.cash += amt;
      } else {
        report.customerPayment.bank += amt;
      }
    });
    report.customerPayment.nowTotal = report.customerPayment.cash + report.customerPayment.bank + report.customerPayment.previousTotal;

    // 6. Expenses
    const expenses = await prisma.expense.findMany({
      where: {
        expenseDate: { gte: startOfDay, lt: nextDay }
      }
    });

    let expenseCash = 0;
    let expenseBank = 0;

    expenses.forEach(ex => {
      const amt = Number(ex.amount);
      report.expenses.total += amt;
      if (ex.paymentMode === 'CASH') {
        expenseCash += amt;
      } else {
        expenseBank += amt;
      }
      report.expenses.details.push({
        id: ex.id,
        category: ex.category,
        amount: amt,
        paymentMode: ex.paymentMode
      });
    });

    // 7. Opening / Final Balances (Use CashBankService for perfectly synchronized historical ledger tallies)
    const { CashBankService } = await import("./CashBankService.js");
    const cashBankService = new CashBankService();
    
    const cashLedger = await cashBankService.getLedger({ account: "CASH", date: formattedDateStr });
    const bankLedger = await cashBankService.getLedger({ account: "BANK", date: formattedDateStr });

    report.finalBalances.cash = cashLedger.closingBalance;
    report.finalBalances.bank = bankLedger.closingBalance;

    // 8. Outstanding Summary (Date-wise)
    // The outstanding balance shown should be the balance at the end of the selected day.
    // We achieve this by taking the current live outstanding balance and reversing any transactions that occurred strictly AFTER the selected day.
    
    // Live balances
    const supplierLive = await prisma.supplier.aggregate({
      _sum: { outstandingBalance: true },
      where: { deletedAt: null }
    });
    const customerLive = await prisma.customer.aggregate({
      _sum: { outstandingBalance: true },
      where: { deletedAt: null }
    });

    const currentSupplierOutstanding = Number(supplierLive._sum.outstandingBalance || 0);
    const currentCustomerOutstanding = Number(customerLive._sum.outstandingBalance || 0);

    // Reversals for future Supplier transactions (Purchase, SupplierPayment)
    const futurePurchases = await prisma.purchase.aggregate({
      _sum: { balanceAmount: true },
      where: { deletedAt: null, purchaseDate: { gte: nextDay } }
    });
    const futureSupplierPayments = await prisma.supplierPayment.aggregate({
      _sum: { amount: true },
      where: { deletedAt: null, paymentDate: { gte: nextDay } }
    });

    const futureSupplierDelta = Number(futurePurchases._sum.balanceAmount || 0) - Number(futureSupplierPayments._sum.amount || 0);

    // Reversals for future Customer transactions (Sale, CustomerReceipt)
    const futureSales = await prisma.sale.aggregate({
      _sum: { balanceAmount: true },
      where: { deletedAt: null, invoiceDate: { gte: nextDay } }
    });
    const futureCustomerReceipts = await prisma.customerReceipt.aggregate({
      _sum: { amount: true },
      where: { deletedAt: null, receiptDate: { gte: nextDay } }
    });

    const futureCustomerDelta = Number(futureSales._sum.balanceAmount || 0) - Number(futureCustomerReceipts._sum.amount || 0);

    report.outstanding.supplier = currentSupplierOutstanding - futureSupplierDelta;
    report.outstanding.customer = currentCustomerOutstanding - futureCustomerDelta;

    return report;
  }
}

export default new FinalReportService();
