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

export interface FinancialSummaryResponse {
  date: string;
  opening: {
    outstanding: number;
    supplierOutstanding: number;
    totalOutstanding: number;
    cashInHand: number;
    cashInBank: number;
    totalCashBank: number;
    total: number;
  };
  closing: {
    outstanding: number;
    supplierOutstanding: number;
    totalOutstanding: number;
    cashInHand: number;
    cashInBank: number;
    totalCashBank: number;
    total: number;
  };
  yesterdayMovements: {
    expenses: number;
    supplierPayments: number;
    sales: number;
    wholesaleSales?: number;
    retailSales?: number;
    purchases: number;
    customerPayments: number;
    transfers: number;
  };
  movements: {
    expenses: {
      total: number;
      cash: number;
      bank: number;
    };
    supplierPayments: {
      total: number;
      cash: number;
      bank: number;
    };
    customerPayments: {
      total: number;
      cash: number;
      bank: number;
    };
    sales: {
      total: number;
      wholesale: number;
      retail: number;
      cash: number;
      bank: number;
      customerPayable: number; // Credit / balance amount owed by customers
    };
    purchases: {
      total: number;
      paid: number;
      supplierPayable: number; // Balance amount owed to suppliers
    };
    customerPayable: number; // New credit sales generated today
    transfers: {
      cashToBank: number;
      bankToCash: number;
    };
    saleReturns: number;
    purchaseReturns: number;
  };
  details: {
    sales: Array<{
      id: string;
      invoiceNo: string;
      customerName: string;
      grandTotal: number;
      receivedAmount: number;
      cashAmount: number;
      upiAmount: number;
      bankAmount: number;
      balanceAmount: number;
    }>;
    customerPayments: Array<{
      id: string;
      receiptDate: string;
      customerName: string;
      amount: number;
      paymentMode: string;
      referenceNo: string | null;
      remarks: string | null;
    }>;
    supplierPayments: Array<{
      id: string;
      paymentDate: string;
      supplierName: string;
      amount: number;
      paymentMode: string;
      referenceNo: string | null;
      remarks: string | null;
    }>;
    expenses: Array<{
      id: number;
      expenseDate: string;
      category: string;
      amount: number;
      paymentMode: string;
      remarks: string | null;
    }>;
    purchases: Array<{
      id: string;
      billNo: string | null;
      lotNo: string;
      supplierName: string;
      vegetableName: string;
      grandTotal: number;
      advanceAmount: number;
      balanceAmount: number;
    }>;
    transfers: Array<{
      id: string;
      transferDate: string;
      amount: number;
      fromAccount: string;
      toAccount: string;
      referenceNo: string | null;
      remarks: string | null;
    }>;
  };
}

export class FinancialSummaryService {
  private customerRepository = new CustomerRepository();
  private supplierRepository = new SupplierRepository();

  /**
   * Get active CashBankSetting or create default
   */
  private async getCashBankSetting() {
    let setting = await prisma.cashBankSetting.findFirst({
      orderBy: { id: "asc" },
    });

    if (!setting) {
      setting = await prisma.cashBankSetting.create({
        data: {
          openingCashInHand: new Prisma.Decimal(0),
          openingCashInBank: new Prisma.Decimal(0),
          openingDate: new Date("2020-01-01"),
          remarks: "Default initial balance configuration",
        },
      });
    }

    return setting;
  }

  async getFinancialSummary(dateStr?: string): Promise<FinancialSummaryResponse> {
    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));

    const setting = await this.getCashBankSetting();
    const settingOpeningDate = setting.openingDate;

    // ── 1. Calculate OPENING balances (strictly before startOfDay) ──
    // 1a. Customer Outstanding opening before startOfDay
    const openingCustomerOutstanding = await this.customerRepository.getCustomerOpeningBalance(
      undefined,
      startOfDay
    );

    // 1b. Supplier Outstanding opening before startOfDay
    const rawSupplierOpening = await this.supplierRepository.getSupplierOpeningBalance(
      undefined,
      startOfDay
    );
    // getSupplierOpeningBalance returns negative for payable in ledger convention,
    // so absolute value is the payable amount owed to suppliers.
    const openingSupplierOutstanding = Math.abs(rawSupplierOpening);

    // 1c. Cash in Hand & Cash in Bank opening before startOfDay
    // Prior Sales
    const priorSales = await prisma.sale.findMany({
      where: {
        deletedAt: null,
        invoiceDate: {
          gte: settingOpeningDate,
          lt: startOfDay,
        },
      },
      select: {
        cashAmount: true,
        upiAmount: true,
        bankAmount: true,
      },
    });

    let priorCashSales = new Prisma.Decimal(0);
    let priorBankSales = new Prisma.Decimal(0);
    for (const s of priorSales) {
      priorCashSales = priorCashSales.add(s.cashAmount || 0);
      priorBankSales = priorBankSales.add(s.upiAmount || 0).add(s.bankAmount || 0);
    }

    // Prior Moved Retail Sales
    const priorRetailSales = await prisma.retailSale.findMany({
      where: {
        deletedAt: null,
        movedToWholesale: true,
        invoiceDate: {
          gte: settingOpeningDate,
          lt: startOfDay,
        },
      },
      select: {
        paymentMode: true,
        receivedAmount: true,
        grandTotal: true,
      },
    });

    for (const rs of priorRetailSales) {
      const rAmt = rs.receivedAmount ?? rs.grandTotal ?? new Prisma.Decimal(0);
      if (rs.paymentMode === "CASH") {
        priorCashSales = priorCashSales.add(rAmt);
      } else if (rs.paymentMode === "UPI" || rs.paymentMode === "BANK") {
        priorBankSales = priorBankSales.add(rAmt);
      } else {
        priorCashSales = priorCashSales.add(rAmt);
      }
    }

    // Prior Customer Receipts
    const priorCashReceiptsAgg = await prisma.customerReceipt.aggregate({
      _sum: { amount: true },
      where: {
        deletedAt: null,
        paymentMode: "CASH",
        receiptDate: {
          gte: settingOpeningDate,
          lt: startOfDay,
        },
      },
    });
    const priorBankReceiptsAgg = await prisma.customerReceipt.aggregate({
      _sum: { amount: true },
      where: {
        deletedAt: null,
        paymentMode: { in: ["UPI", "BANK"] },
        receiptDate: {
          gte: settingOpeningDate,
          lt: startOfDay,
        },
      },
    });
    const priorCashReceipts = priorCashReceiptsAgg._sum.amount ?? new Prisma.Decimal(0);
    const priorBankReceipts = priorBankReceiptsAgg._sum.amount ?? new Prisma.Decimal(0);

    // Prior Supplier Payments
    const priorCashSuppPaymentsAgg = await prisma.supplierPayment.aggregate({
      _sum: { amount: true },
      where: {
        deletedAt: null,
        paymentMode: "CASH",
        paymentDate: {
          gte: settingOpeningDate,
          lt: startOfDay,
        },
      },
    });
    const priorBankSuppPaymentsAgg = await prisma.supplierPayment.aggregate({
      _sum: { amount: true },
      where: {
        deletedAt: null,
        paymentMode: { in: ["UPI", "BANK"] },
        paymentDate: {
          gte: settingOpeningDate,
          lt: startOfDay,
        },
      },
    });
    const priorCashSuppPayments = priorCashSuppPaymentsAgg._sum.amount ?? new Prisma.Decimal(0);
    const priorBankSuppPayments = priorBankSuppPaymentsAgg._sum.amount ?? new Prisma.Decimal(0);

    // Prior Expenses
    const priorCashExpensesAgg = await prisma.expense.aggregate({
      _sum: { amount: true },
      where: {
        paymentMode: "CASH",
        expenseDate: {
          gte: settingOpeningDate,
          lt: startOfDay,
        },
      },
    });
    const priorBankExpensesAgg = await prisma.expense.aggregate({
      _sum: { amount: true },
      where: {
        paymentMode: { in: ["UPI", "BANK"] },
        expenseDate: {
          gte: settingOpeningDate,
          lt: startOfDay,
        },
      },
    });
    const priorCashExpenses = priorCashExpensesAgg._sum.amount ?? new Prisma.Decimal(0);
    const priorBankExpenses = priorBankExpensesAgg._sum.amount ?? new Prisma.Decimal(0);

    // Prior Cash to Bank Transfers
    const priorCashToBankAgg = await prisma.cashBankTransfer.aggregate({
      _sum: { amount: true },
      where: {
        deletedAt: null,
        fromAccount: "CASH",
        toAccount: "BANK",
        transferDate: {
          gte: settingOpeningDate,
          lt: startOfDay,
        },
      },
    });
    const priorCashToBank = priorCashToBankAgg._sum.amount ?? new Prisma.Decimal(0);

    // Prior Bank to Cash Transfers (if any)
    const priorBankToCashAgg = await prisma.cashBankTransfer.aggregate({
      _sum: { amount: true },
      where: {
        deletedAt: null,
        fromAccount: "BANK",
        toAccount: "CASH",
        transferDate: {
          gte: settingOpeningDate,
          lt: startOfDay,
        },
      },
    });
    const priorBankToCash = priorBankToCashAgg._sum.amount ?? new Prisma.Decimal(0);

    // Compute Opening Cash in Hand
    const openingCashInHand = setting.openingCashInHand
      .add(priorCashSales)
      .add(priorCashReceipts)
      .add(priorBankToCash)
      .sub(priorCashSuppPayments)
      .sub(priorCashExpenses)
      .sub(priorCashToBank);

    // Compute Opening Cash in Bank
    const openingCashInBank = setting.openingCashInBank
      .add(priorBankSales)
      .add(priorBankReceipts)
      .add(priorCashToBank)
      .sub(priorBankSuppPayments)
      .sub(priorBankExpenses)
      .sub(priorBankToCash);

    const openingTotal =
      openingCustomerOutstanding + openingCashInHand.toNumber() + openingCashInBank.toNumber();

    // ── 1d. Calculate YESTERDAY'S Movements (day before selected date) ──
    const yesterdayStart = new Date(Date.UTC(year, month, day - 1, 0, 0, 0, 0));
    const yesterdayEnd = startOfDay;

    const [
      yesterdayExpensesAgg,
      yesterdaySuppPaymentsAgg,
      yesterdaySalesAgg,
      yesterdayRetailSalesAgg,
      yesterdayPurchasesAgg,
      yesterdayReceiptsAgg,
      yesterdayTransfersAgg,
    ] = await Promise.all([
      prisma.expense.aggregate({
        where: { expenseDate: { gte: yesterdayStart, lt: yesterdayEnd } },
        _sum: { amount: true },
      }),
      prisma.supplierPayment.aggregate({
        where: { deletedAt: null, paymentDate: { gte: yesterdayStart, lt: yesterdayEnd } },
        _sum: { amount: true },
      }),
      prisma.sale.aggregate({
        where: { deletedAt: null, invoiceDate: { gte: yesterdayStart, lt: yesterdayEnd } },
        _sum: { grandTotal: true },
      }),
      prisma.retailSale.aggregate({
        where: {
          deletedAt: null,
          movedToWholesale: true,
          invoiceDate: { gte: yesterdayStart, lt: yesterdayEnd },
        },
        _sum: { grandTotal: true },
      }),
      prisma.purchase.aggregate({
        where: { deletedAt: null, purchaseDate: { gte: yesterdayStart, lt: yesterdayEnd } },
        _sum: { grandTotal: true },
      }),
      prisma.customerReceipt.aggregate({
        where: { deletedAt: null, receiptDate: { gte: yesterdayStart, lt: yesterdayEnd } },
        _sum: { amount: true },
      }),
      prisma.cashBankTransfer.aggregate({
        where: {
          deletedAt: null,
          transferDate: { gte: yesterdayStart, lt: yesterdayEnd },
          fromAccount: "CASH",
          toAccount: "BANK",
        },
        _sum: { amount: true },
      }),
    ]);

    const yesterdayExpensesTotal = Number(yesterdayExpensesAgg._sum.amount ?? 0);
    const yesterdaySuppPaymentsTotal = Number(yesterdaySuppPaymentsAgg._sum.amount ?? 0);
    const yesterdayWholesaleSales = Number(yesterdaySalesAgg._sum.grandTotal ?? 0);
    const yesterdayRetailSales = Number(yesterdayRetailSalesAgg._sum.grandTotal ?? 0);
    const yesterdaySalesTotal = yesterdayWholesaleSales + yesterdayRetailSales;
    const yesterdayPurchasesTotal = Number(yesterdayPurchasesAgg._sum.grandTotal ?? 0);
    const yesterdayReceiptsTotal = Number(yesterdayReceiptsAgg._sum.amount ?? 0);
    const yesterdayCashToBankTotal = Number(yesterdayTransfersAgg._sum.amount ?? 0);

    // ── 2. Query TODAY'S Transactions (strictly on the selected date: >= startOfDay and < nextDay) ──
    const [
      todaySales,
      todayRetailSales,
      todayReceipts,
      todaySuppPayments,
      todayExpenses,
      todayPurchases,
      todayTransfers,
      todaySaleReturns,
      todayPurchaseReturns,
    ] = await Promise.all([
      prisma.sale.findMany({
        where: {
          deletedAt: null,
          invoiceDate: { gte: startOfDay, lt: nextDay },
        },
        include: {
          customer: { select: { name: true } },
        },
        orderBy: { invoiceDate: "asc" },
      }),
      prisma.retailSale.findMany({
        where: {
          deletedAt: null,
          movedToWholesale: true,
          invoiceDate: { gte: startOfDay, lt: nextDay },
        },
        orderBy: { invoiceDate: "asc" },
      }),
      prisma.customerReceipt.findMany({
        where: {
          deletedAt: null,
          receiptDate: { gte: startOfDay, lt: nextDay },
        },
        include: {
          customer: { select: { name: true } },
        },
        orderBy: { receiptDate: "asc" },
      }),
      prisma.supplierPayment.findMany({
        where: {
          deletedAt: null,
          paymentDate: { gte: startOfDay, lt: nextDay },
        },
        include: {
          supplier: { select: { name: true } },
        },
        orderBy: { paymentDate: "asc" },
      }),
      prisma.expense.findMany({
        where: {
          expenseDate: { gte: startOfDay, lt: nextDay },
        },
        orderBy: { expenseDate: "asc" },
      }),
      prisma.purchase.findMany({
        where: {
          deletedAt: null,
          purchaseDate: { gte: startOfDay, lt: nextDay },
        },
        include: {
          supplier: { select: { name: true } },
          vegetable: { select: { name: true } },
        },
        orderBy: { purchaseDate: "asc" },
      }),
      prisma.cashBankTransfer.findMany({
        where: {
          deletedAt: null,
          transferDate: { gte: startOfDay, lt: nextDay },
        },
        orderBy: { transferDate: "asc" },
      }),
      prisma.saleReturn.findMany({
        where: {
          returnDate: { gte: startOfDay, lt: nextDay },
        },
        select: { returnAmount: true },
      }),
      prisma.purchaseReturn.findMany({
        where: {
          returnDate: { gte: startOfDay, lt: nextDay },
        },
        select: { returnAmount: true },
      }),
    ]);

    // ── 3. Calculate TODAY'S Movements ──
    // Sales breakdown
    let todaySalesTotal = new Prisma.Decimal(0);
    let todayWholesaleSalesTotal = new Prisma.Decimal(0);
    let todayRetailSalesTotal = new Prisma.Decimal(0);
    let todayCashSales = new Prisma.Decimal(0);
    let todayBankSales = new Prisma.Decimal(0);
    let todayCustomerPayable = new Prisma.Decimal(0); // Credit / balanceAmount from sales

    const formattedSales: Array<{
      id: string;
      invoiceNo: string;
      type: "wholesale" | "retail";
      customerName: string;
      grandTotal: number;
      receivedAmount: number;
      cashAmount: number;
      upiAmount: number;
      bankAmount: number;
      balanceAmount: number;
    }> = todaySales.map((s) => {
      const gTotal = s.grandTotal ?? new Prisma.Decimal(0);
      const cAmt = s.cashAmount ?? new Prisma.Decimal(0);
      const uAmt = s.upiAmount ?? new Prisma.Decimal(0);
      const bAmt = s.bankAmount ?? new Prisma.Decimal(0);
      const rAmt = s.receivedAmount ?? cAmt.add(uAmt).add(bAmt);
      const balAmt = s.balanceAmount ?? gTotal.sub(rAmt);

      todaySalesTotal = todaySalesTotal.add(gTotal);
      todayWholesaleSalesTotal = todayWholesaleSalesTotal.add(gTotal);
      todayCashSales = todayCashSales.add(cAmt);
      todayBankSales = todayBankSales.add(uAmt).add(bAmt);
      if (s.customerId) {
        todayCustomerPayable = todayCustomerPayable.add(balAmt);
      }

      return {
        id: s.id.toString(),
        invoiceNo: s.invoiceNo,
        type: "wholesale",
        customerName: s.customer?.name || s.walkInName || "Walk-in Customer",
        grandTotal: gTotal.toNumber(),
        receivedAmount: rAmt.toNumber(),
        cashAmount: cAmt.toNumber(),
        upiAmount: uAmt.toNumber(),
        bankAmount: bAmt.toNumber(),
        balanceAmount: balAmt.toNumber(),
      };
    });

    // Add moved retail sales to today's sales
    for (const rs of todayRetailSales) {
      const gTotal = rs.grandTotal ?? new Prisma.Decimal(0);
      const rAmt = rs.receivedAmount ?? gTotal;
      const balAmt = rs.balanceAmount ?? new Prisma.Decimal(0);

      let cAmt = new Prisma.Decimal(0);
      let uAmt = new Prisma.Decimal(0);
      let bAmt = new Prisma.Decimal(0);

      if (rs.paymentMode === "CASH") {
        cAmt = rAmt;
      } else if (rs.paymentMode === "UPI") {
        uAmt = rAmt;
      } else if (rs.paymentMode === "BANK") {
        bAmt = rAmt;
      } else {
        cAmt = rAmt;
      }

      todaySalesTotal = todaySalesTotal.add(gTotal);
      todayRetailSalesTotal = todayRetailSalesTotal.add(gTotal);
      todayCashSales = todayCashSales.add(cAmt);
      todayBankSales = todayBankSales.add(uAmt).add(bAmt);
      if (balAmt.gt(0)) {
        todayCustomerPayable = todayCustomerPayable.add(balAmt);
      }

      formattedSales.push({
        id: `retail-${rs.id.toString()}`,
        invoiceNo: rs.invoiceNo,
        type: "retail",
        customerName: rs.walkInName ? `${rs.walkInName} (Retail)` : "Retail Walk-in Customer",
        grandTotal: gTotal.toNumber(),
        receivedAmount: rAmt.toNumber(),
        cashAmount: cAmt.toNumber(),
        upiAmount: uAmt.toNumber(),
        bankAmount: bAmt.toNumber(),
        balanceAmount: balAmt.toNumber(),
      });
    }

    // Customer Receipts breakdown
    let todayReceiptsTotal = new Prisma.Decimal(0);
    let todayCashReceipts = new Prisma.Decimal(0);
    let todayBankReceipts = new Prisma.Decimal(0);

    const formattedReceipts = todayReceipts.map((r) => {
      const amt = r.amount ?? new Prisma.Decimal(0);
      todayReceiptsTotal = todayReceiptsTotal.add(amt);
      if (r.paymentMode === "CASH") {
        todayCashReceipts = todayCashReceipts.add(amt);
      } else {
        todayBankReceipts = todayBankReceipts.add(amt);
      }

      return {
        id: r.id.toString(),
        receiptDate: r.receiptDate.toISOString().slice(0, 10),
        customerName: r.customer?.name || "Customer",
        amount: amt.toNumber(),
        paymentMode: r.paymentMode,
        referenceNo: r.referenceNo,
        remarks: r.remarks,
      };
    });

    // Supplier Payments breakdown
    let todaySuppPaymentsTotal = new Prisma.Decimal(0);
    let todayCashSuppPayments = new Prisma.Decimal(0);
    let todayBankSuppPayments = new Prisma.Decimal(0);

    const formattedSuppPayments = todaySuppPayments.map((sp) => {
      const amt = sp.amount ?? new Prisma.Decimal(0);
      todaySuppPaymentsTotal = todaySuppPaymentsTotal.add(amt);
      if (sp.paymentMode === "CASH") {
        todayCashSuppPayments = todayCashSuppPayments.add(amt);
      } else {
        todayBankSuppPayments = todayBankSuppPayments.add(amt);
      }

      return {
        id: sp.id.toString(),
        paymentDate: sp.paymentDate.toISOString().slice(0, 10),
        supplierName: sp.supplier?.name || "Supplier",
        amount: amt.toNumber(),
        paymentMode: sp.paymentMode,
        referenceNo: sp.referenceNo,
        remarks: sp.remarks,
      };
    });

    // Expenses breakdown
    let todayExpensesTotal = new Prisma.Decimal(0);
    let todayCashExpenses = new Prisma.Decimal(0);
    let todayBankExpenses = new Prisma.Decimal(0);

    const formattedExpenses = todayExpenses.map((e) => {
      const amt = e.amount ?? new Prisma.Decimal(0);
      todayExpensesTotal = todayExpensesTotal.add(amt);
      if (e.paymentMode === "CASH") {
        todayCashExpenses = todayCashExpenses.add(amt);
      } else {
        todayBankExpenses = todayBankExpenses.add(amt);
      }

      return {
        id: e.id,
        expenseDate: e.expenseDate.toISOString().slice(0, 10),
        category: e.category,
        amount: amt.toNumber(),
        paymentMode: e.paymentMode,
        remarks: e.remarks,
      };
    });

    // Purchases breakdown
    let todayPurchasesTotal = new Prisma.Decimal(0);
    let todayPurchasesPaid = new Prisma.Decimal(0);
    let todaySupplierPayable = new Prisma.Decimal(0);

    const formattedPurchases = todayPurchases.map((p) => {
      const gTotal = p.grandTotal ?? new Prisma.Decimal(0);
      const adv = p.advanceAmount ?? new Prisma.Decimal(0);
      const bal = p.balanceAmount ?? gTotal.sub(adv);

      todayPurchasesTotal = todayPurchasesTotal.add(gTotal);
      todayPurchasesPaid = todayPurchasesPaid.add(adv);
      if (p.supplierId && p.purchaseType !== 'COMMISSION_ARRIVAL') {
        todaySupplierPayable = todaySupplierPayable.add(bal);
      }

      return {
        id: p.id.toString(),
        billNo: p.billNo,
        lotNo: p.lotNo,
        supplierName: p.supplier?.name || p.cashSupplierName || "Supplier",
        vegetableName: p.vegetable?.name || "-",
        grandTotal: gTotal.toNumber(),
        advanceAmount: adv.toNumber(),
        balanceAmount: bal.toNumber(),
      };
    });

    // Transfers breakdown
    let todayCashToBank = new Prisma.Decimal(0);
    let todayBankToCash = new Prisma.Decimal(0);

    const formattedTransfers = todayTransfers.map((t) => {
      const amt = t.amount ?? new Prisma.Decimal(0);
      if (t.fromAccount === "CASH" && t.toAccount === "BANK") {
        todayCashToBank = todayCashToBank.add(amt);
      } else if (t.fromAccount === "BANK" && t.toAccount === "CASH") {
        todayBankToCash = todayBankToCash.add(amt);
      }

      return {
        id: t.id.toString(),
        transferDate: t.transferDate.toISOString().slice(0, 10),
        amount: amt.toNumber(),
        fromAccount: t.fromAccount,
        toAccount: t.toAccount,
        referenceNo: t.referenceNo,
        remarks: t.remarks,
      };
    });

    // Returns
    const todaySaleReturnsTotal = todaySaleReturns.reduce(
      (sum, r) => sum.add(r.returnAmount || 0),
      new Prisma.Decimal(0)
    );
    const todayPurchaseReturnsTotal = todayPurchaseReturns.reduce(
      (sum, r) => sum.add(r.returnAmount || 0),
      new Prisma.Decimal(0)
    );

    // ── 4. Calculate CLOSING balances (at end of day) ──
    // Closing Customer Outstanding = Opening + Today's Customer Credit (Payable) - Today's Receipts - Today's Returns
    const closingCustomerOutstanding = new Prisma.Decimal(openingCustomerOutstanding)
      .add(todayCustomerPayable)
      .sub(todayReceiptsTotal)
      .sub(todaySaleReturnsTotal)
      .toNumber();

    // Closing Cash in Hand = Opening + Today's Cash In - Today's Cash Out
    const closingCashInHand = openingCashInHand
      .add(todayCashSales)
      .add(todayCashReceipts)
      .add(todayBankToCash)
      .sub(todayCashSuppPayments)
      .sub(todayCashExpenses)
      .sub(todayCashToBank)
      .toNumber();

    // Closing Cash in Bank = Opening + Today's Bank In - Today's Bank Out
    const closingCashInBank = openingCashInBank
      .add(todayBankSales)
      .add(todayBankReceipts)
      .add(todayCashToBank)
      .sub(todayBankSuppPayments)
      .sub(todayBankExpenses)
      .sub(todayBankToCash)
      .toNumber();

    const closingTotal = closingCustomerOutstanding + closingCashInHand + closingCashInBank;

    // Closing Supplier Outstanding = Opening + Today's Credit Purchases (Supplier Payable) - Today's Supplier Payments - Today's Purchase Returns
    const closingSupplierOutstanding = new Prisma.Decimal(openingSupplierOutstanding)
      .add(todaySupplierPayable)
      .sub(todaySuppPaymentsTotal)
      .sub(todayPurchaseReturnsTotal)
      .toNumber();

    const openingTotalOutstanding = openingCustomerOutstanding + openingSupplierOutstanding;
    const openingTotalCashBank = openingCashInHand.add(openingCashInBank).toNumber();
    const closingTotalOutstanding = closingCustomerOutstanding + closingSupplierOutstanding;
    const closingTotalCashBank = closingCashInHand + closingCashInBank;

    return {
      date: formattedDateStr,
      opening: {
        outstanding: openingCustomerOutstanding,
        supplierOutstanding: openingSupplierOutstanding,
        totalOutstanding: openingTotalOutstanding,
        cashInHand: openingCashInHand.toNumber(),
        cashInBank: openingCashInBank.toNumber(),
        totalCashBank: openingTotalCashBank,
        total: openingTotal,
      },
      closing: {
        outstanding: closingCustomerOutstanding,
        supplierOutstanding: closingSupplierOutstanding,
        totalOutstanding: closingTotalOutstanding,
        cashInHand: closingCashInHand,
        cashInBank: closingCashInBank,
        totalCashBank: closingTotalCashBank,
        total: closingTotal,
      },
      yesterdayMovements: {
        expenses: yesterdayExpensesTotal,
        supplierPayments: yesterdaySuppPaymentsTotal,
        sales: yesterdaySalesTotal,
        wholesaleSales: yesterdayWholesaleSales,
        retailSales: yesterdayRetailSales,
        purchases: yesterdayPurchasesTotal,
        customerPayments: yesterdayReceiptsTotal,
        transfers: yesterdayCashToBankTotal,
      },
      movements: {
        expenses: {
          total: todayExpensesTotal.toNumber(),
          cash: todayCashExpenses.toNumber(),
          bank: todayBankExpenses.toNumber(),
        },
        supplierPayments: {
          total: todaySuppPaymentsTotal.toNumber(),
          cash: todayCashSuppPayments.toNumber(),
          bank: todayBankSuppPayments.toNumber(),
        },
        customerPayments: {
          total: todayReceiptsTotal.toNumber(),
          cash: todayCashReceipts.toNumber(),
          bank: todayBankReceipts.toNumber(),
        },
        sales: {
          total: todaySalesTotal.toNumber(),
          wholesale: todayWholesaleSalesTotal.toNumber(),
          retail: todayRetailSalesTotal.toNumber(),
          cash: todayCashSales.toNumber(),
          bank: todayBankSales.toNumber(),
          customerPayable: todayCustomerPayable.toNumber(),
        },
        purchases: {
          total: todayPurchasesTotal.toNumber(),
          paid: todayPurchasesPaid.toNumber(),
          supplierPayable: todaySupplierPayable.toNumber(),
        },
        customerPayable: todayCustomerPayable.toNumber(),
        transfers: {
          cashToBank: todayCashToBank.toNumber(),
          bankToCash: todayBankToCash.toNumber(),
        },
        saleReturns: todaySaleReturnsTotal.toNumber(),
        purchaseReturns: todayPurchaseReturnsTotal.toNumber(),
      },
      details: {
        sales: formattedSales,
        customerPayments: formattedReceipts,
        supplierPayments: formattedSuppPayments,
        expenses: formattedExpenses,
        purchases: formattedPurchases,
        transfers: formattedTransfers,
      },
    };
  }
}
export default new FinancialSummaryService();
