import prisma from "../config/prisma.js";
import { Prisma } from "../generated/prisma/client.js";
import { PaymentMode } from "../constants/payment.mode.js";
import { ARRIVAL_COMMISSION_PERCENT } from "../config/constants.js";


// â”€â”€â”€ Calculation helper (mirrors saleCalculations but works on RetailStock) â”€â”€

interface RetailSaleItemCalcInput {
  saleUnit: "KG" | "BAG" | "BOX" | "THAAR" | "PIECES";
  quantity?: number | string; // required when saleUnit === KG
  bags?: number | string;     // required when saleUnit !== KG
  sellingRate: number | string;
  retailStock: {
    remainingQuantity: Prisma.Decimal;
    remainingBags: Prisma.Decimal | null;
    weightPerBag: Prisma.Decimal | null; // from parent purchase
    costPerUnit: Prisma.Decimal;
  };
}

function calculateRetailSaleItem(input: RetailSaleItemCalcInput) {
  const sellingRate = new Prisma.Decimal(input.sellingRate);
  if (sellingRate.lt(0)) throw new Error("Selling rate cannot be negative");

  let kgQty: Prisma.Decimal;
  let bagQty: Prisma.Decimal | null = null;
  let total: Prisma.Decimal;

  if (input.saleUnit === "KG") {
    if (input.quantity === undefined) throw new Error("Quantity is required for KG sale");
    kgQty = new Prisma.Decimal(input.quantity);
    if (kgQty.lte(0)) throw new Error("Quantity must be greater than 0");
    if (kgQty.gt(input.retailStock.remainingQuantity)) {
      throw new Error(
        `Insufficient retail stock: only ${input.retailStock.remainingQuantity.toFixed(3)} kg remaining`
      );
    }
    total = kgQty.mul(sellingRate).toDecimalPlaces(2, Prisma.Decimal.ROUND_HALF_UP);
    // Track bag equivalent for lot balance purposes
    if (input.retailStock.weightPerBag && input.retailStock.weightPerBag.gt(0)) {
      bagQty = kgQty
        .div(input.retailStock.weightPerBag)
        .toDecimalPlaces(3, Prisma.Decimal.ROUND_HALF_UP);
    } else if (input.retailStock.remainingQuantity.gt(0) && input.retailStock.remainingBags && input.retailStock.remainingBags.gt(0)) {
      bagQty = kgQty
        .mul(input.retailStock.remainingBags)
        .div(input.retailStock.remainingQuantity)
        .toDecimalPlaces(3, Prisma.Decimal.ROUND_HALF_UP);
    }
  } else {
    // THAAR, PIECES, BAG, BOX
    const rawCount = (input.bags !== undefined && input.bags !== null && input.bags !== "")
      ? input.bags
      : input.quantity;
    if (rawCount === undefined || rawCount === null || rawCount === "") {
      throw new Error(`${input.saleUnit} count is required`);
    }
    bagQty = new Prisma.Decimal(rawCount);
    if (bagQty.lte(0)) throw new Error(`${input.saleUnit} count must be greater than 0`);
    if (input.saleUnit !== "PIECES" && input.retailStock.remainingBags && bagQty.gt(input.retailStock.remainingBags)) {
      throw new Error(
        `Insufficient retail stock: only ${input.retailStock.remainingBags.toFixed(3)} ${input.saleUnit.toLowerCase()} remaining`
      );
    }

    if (input.saleUnit === "PIECES") {
      kgQty = bagQty;
    } else if (input.retailStock.weightPerBag && input.retailStock.weightPerBag.gt(0)) {
      kgQty = bagQty
        .mul(input.retailStock.weightPerBag)
        .toDecimalPlaces(3, Prisma.Decimal.ROUND_HALF_UP);
    } else if (input.retailStock.remainingBags && input.retailStock.remainingBags.gt(0) && input.retailStock.remainingQuantity.gt(0)) {
      kgQty = bagQty
        .mul(input.retailStock.remainingQuantity)
        .div(input.retailStock.remainingBags)
        .toDecimalPlaces(3, Prisma.Decimal.ROUND_HALF_UP);
    } else {
      kgQty = bagQty;
    }

    total = bagQty.mul(sellingRate).toDecimalPlaces(2, Prisma.Decimal.ROUND_HALF_UP);
  }

  return {
    quantity: kgQty,
    bags: bagQty,
    costPerUnit: input.retailStock.costPerUnit,
    sellingRate,
    total,
  };
}

// â”€â”€â”€ Invoice number generator â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€

function generateRetailInvoiceNo(): string {
  const today = new Date();
  const datePart = today.toISOString().slice(0, 10).replace(/-/g, ""); // YYYYMMDD
  const rand = Math.floor(10000 + Math.random() * 90000);
  return `RET-${datePart}-${rand}`;
}

// â”€â”€â”€ Serializer helpers â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€

function serializeSale(sale: any): any {
  return {
    ...sale,
    id: sale.id?.toString(),
    createdBy: sale.createdBy?.toString() ?? null,
    items: (sale.items ?? []).map((item: any) => ({
      ...item,
      id: item.id?.toString(),
      retailSaleId: item.retailSaleId?.toString(),
      retailStockId: item.retailStockId?.toString(),
      createdBy: item.createdBy?.toString() ?? null,
    })),
  };
}

// â”€â”€â”€ RetailSaleService â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€

export class RetailSaleService {
  // â”€â”€ Create Retail Sale â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€

  async createRetailSale(data: any) {
    const { items, ...saleFields } = data;

    if (!items || items.length === 0) {
      throw new Error("At least one sale item is required");
    }

    return await prisma.$transaction(async (tx) => {
      // Unique invoice no
      let invoiceNo: string;
      do {
        invoiceNo = generateRetailInvoiceNo();
        const existing = await tx.retailSale.findUnique({ where: { invoiceNo } });
        if (!existing) break;
      } while (true);

      const validatedItems: any[] = [];

      for (const item of items) {
        // Fetch the retail stock row (aggregate: sum remaining across lot rows for this lotNo)
        // But we sell FROM individual RetailStock rows in FIFO order for the given lotNo
        const retailStockRows = await tx.retailStock.findMany({
          where: {
            lotNo: item.lotNo,
            deletedAt: null,
            OR: [
              { remainingQuantity: { gt: 0 } },
              { remainingBags: { gt: 0 } },
            ],
          },
          include: {
            purchase: { select: { weightPerBag: true, costPerUnit: true } },
          },
          orderBy: { id: "asc" }, // FIFO: oldest allocation first
        });

        if (retailStockRows.length === 0) {
          throw new Error(`No available retail stock for lot: ${item.lotNo}`);
        }

        // Compute total remaining for this lotNo
        const totalRemainingKg = retailStockRows.reduce(
          (acc, r) => acc.add(r.remainingQuantity),
          new Prisma.Decimal(0)
        );
        const totalRemainingBags = retailStockRows.reduce(
          (acc, r) => acc.add(r.remainingBags ?? 0),
          new Prisma.Decimal(0)
        );

        // Use the first row's weightPerBag and costPerUnit (all rows under same lotNo share the same purchase)
        const firstRow = retailStockRows[0]!;
        const weightPerBag = firstRow.purchase?.weightPerBag ?? null;

        // Validate + calculate
        const calc = calculateRetailSaleItem({
          saleUnit: item.saleUnit ?? "KG",
          quantity: item.quantity,
          bags: item.bags,
          sellingRate: item.sellingRate,
          retailStock: {
            remainingQuantity: totalRemainingKg,
            remainingBags: totalRemainingBags,
            weightPerBag: weightPerBag,
            costPerUnit: firstRow.costPerUnit,
          },
        });

        // Deduct from RetailStock rows FIFO
        let kgToDeduct = calc.quantity;
        let bagsToDeduct = calc.bags ?? new Prisma.Decimal(0);

        if (item.saleUnit !== "PIECES") {
          for (const row of retailStockRows) {
            if (kgToDeduct.lte(0)) break;

            const rowKgDeduct = Prisma.Decimal.min(kgToDeduct, row.remainingQuantity);

            let rowBagsDeduct = new Prisma.Decimal(0);
            if (row.remainingBags && row.remainingBags.gt(0)) {
              if (item.saleUnit !== "KG") {
                rowBagsDeduct = Prisma.Decimal.min(bagsToDeduct, row.remainingBags);
              } else if (row.purchase?.weightPerBag && new Prisma.Decimal(row.purchase.weightPerBag).gt(0)) {
                const wpb = new Prisma.Decimal(row.purchase.weightPerBag);
                rowBagsDeduct = rowKgDeduct.div(wpb).toDecimalPlaces(3, Prisma.Decimal.ROUND_HALF_UP);
                rowBagsDeduct = Prisma.Decimal.min(rowBagsDeduct, row.remainingBags);
              } else if (row.remainingQuantity.gt(0)) {
                rowBagsDeduct = rowKgDeduct.mul(row.remainingBags).div(row.remainingQuantity).toDecimalPlaces(3, Prisma.Decimal.ROUND_HALF_UP);
                rowBagsDeduct = Prisma.Decimal.min(rowBagsDeduct, row.remainingBags);
              }
            }

            const result = await tx.retailStock.updateMany({
              where: { 
                id: row.id,
                remainingQuantity: { gte: rowKgDeduct },
                ...(rowBagsDeduct.gt(0) ? { remainingBags: { gte: rowBagsDeduct } } : {})
              },
              data: {
                remainingQuantity: { decrement: rowKgDeduct },
                ...(rowBagsDeduct.gt(0) ? { remainingBags: { decrement: rowBagsDeduct } } : {}),
              },
            });

            if (result.count === 0) {
              throw new Error('Retail stock changed concurrently or insufficient stock. Please retry.');
            }

            kgToDeduct = kgToDeduct.sub(rowKgDeduct);
            if (bagsToDeduct.gt(0)) {
              bagsToDeduct = bagsToDeduct.sub(rowBagsDeduct);
            }
          }
        }


        validatedItems.push({
          retailStockId: firstRow.id, // primary row reference for the item record
          saleUnit: item.saleUnit ?? "KG",
          quantity: calc.quantity,
          bags: calc.bags,
          costPerUnit: calc.costPerUnit,
          sellingRate: calc.sellingRate,
          total: calc.total,
          lotNo: item.lotNo,
        });

      }

      // Totals
      const subTotal = validatedItems
        .reduce((acc, i) => acc.add(i.total), new Prisma.Decimal(0))
        .toDecimalPlaces(2, Prisma.Decimal.ROUND_HALF_UP);

      const discount = new Prisma.Decimal(saleFields.discount ?? 0).toDecimalPlaces(2);
      const grandTotal = subTotal
        .sub(discount)
        .toDecimalPlaces(2, Prisma.Decimal.ROUND_HALF_UP);

      // Payment
      let receivedAmount: Prisma.Decimal;
      if (
        saleFields.receivedAmount !== undefined &&
        !new Prisma.Decimal(saleFields.receivedAmount).eq(grandTotal)
      ) {
        throw new Error("Retail sales must be paid in full at the time of sale");
      }
      receivedAmount = grandTotal;

      const balanceAmount = grandTotal
        .sub(receivedAmount)
        .toDecimalPlaces(2, Prisma.Decimal.ROUND_HALF_UP);

      let paymentStatus: "UNPAID" | "PARTIALLY_PAID" | "PAID";
      if (balanceAmount.lte(0)) {
        paymentStatus = "PAID";
      } else if (receivedAmount.gt(0)) {
        paymentStatus = "PARTIALLY_PAID";
      } else {
        paymentStatus = "UNPAID";
      }

      const sale = await tx.retailSale.create({
        data: {
          invoiceNo,
          walkInName: saleFields.walkInName ?? null,
          walkInMobile: saleFields.walkInMobile ?? null,
          invoiceDate: new Date(saleFields.invoiceDate),
          subTotal,
          discount,
          grandTotal,
          receivedAmount,
          balanceAmount,
          paymentStatus,
          paymentMode: saleFields.paymentMode ?? null,
          remarks: saleFields.remarks ?? null,
          createdBy: saleFields.createdBy ? BigInt(saleFields.createdBy) : null,
          items: {
            create: validatedItems.map((item) => ({
              retailStockId: item.retailStockId,
              saleUnit: item.saleUnit,
              quantity: item.quantity,
              bags: item.bags ?? null,
              costPerUnit: item.costPerUnit,
              sellingRate: item.sellingRate,
              total: item.total,
              createdBy: saleFields.createdBy ? BigInt(saleFields.createdBy) : null,
            })),
          },
        },
        include: {
          items: true,
        },
      });

      return serializeSale(sale);
    });
  }

  // â”€â”€ Get Retail Sales (paginated) â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€

  async getRetailSales(
    page: number = 1,
    limit: number = 10,
    dateFromStr?: string,
    dateToStr?: string,
    search?: string,
    paymentMode?: string
  ) {
    const skip = (page - 1) * limit;

    const where: Prisma.RetailSaleWhereInput = { deletedAt: null };

    if (paymentMode) {
      where.paymentMode = paymentMode as any;
    }

    if (search) {
      where.OR = [
        { invoiceNo: { contains: search } },
        { walkInName: { contains: search } },
        { walkInMobile: { contains: search } },
        {
          items: {
            some: {
              retailStock: {
                OR: [
                  { lotNo: { contains: search } },
                  { purchase: { vegetable: { name: { contains: search } } } },
                ],
              },
            },
          },
        },
      ];
    }

    if (dateFromStr || dateToStr) {
      where.invoiceDate = {};
      if (dateFromStr) (where.invoiceDate as any).gte = new Date(dateFromStr);
      if (dateToStr) (where.invoiceDate as any).lte = new Date(dateToStr);
    }

    const [sales, total, grandTotalAgg] = await Promise.all([
      prisma.retailSale.findMany({
        where,
        skip,
        take: limit,
        orderBy: { id: "desc" },
        include: {
          items: {
            include: {
              retailStock: {
                select: {
                  lotNo: true,
                  purchase: { 
                    select: { 
                      weightPerBag: true,
                      costPerUnit: true,
                      vegetable: { select: { name: true } } 
                    } 
                  },
                },
              },
            },
          },
        },
      }),
      prisma.retailSale.count({ where }),
      prisma.retailSale.aggregate({
        _sum: { grandTotal: true },
        where,
      }),
    ]);

    const grandTotalSum = Number(grandTotalAgg._sum.grandTotal || 0);

    return {
      data: sales.map(serializeSale),
      meta: { total, page, limit, totalPages: Math.ceil(total / limit) },
      sum: {
        grand_total: grandTotalSum,
        total_invoices: total,
      },
    };
  }

  // â”€â”€ Get Retail Sales (For Export) â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€

  async getRetailSalesForExport(
    dateFromStr?: string,
    dateToStr?: string,
    search?: string,
    paymentMode?: string
  ) {
    const where: Prisma.RetailSaleWhereInput = { deletedAt: null };

    if (paymentMode) {
      where.paymentMode = paymentMode as any;
    }

    if (search) {
      where.OR = [
        { invoiceNo: { contains: search } },
        { walkInName: { contains: search } },
        { walkInMobile: { contains: search } },
        {
          items: {
            some: {
              retailStock: {
                OR: [
                  { lotNo: { contains: search } },
                  { purchase: { vegetable: { name: { contains: search } } } },
                ],
              },
            },
          },
        },
      ];
    }

    if (dateFromStr || dateToStr) {
      where.invoiceDate = {};
      if (dateFromStr) (where.invoiceDate as any).gte = new Date(dateFromStr);
      if (dateToStr) (where.invoiceDate as any).lte = new Date(dateToStr);
    }

    const sales = await prisma.retailSale.findMany({
      where,
      orderBy: { id: "desc" },
      include: {
        items: {
          include: {
            retailStock: {
              select: {
                lotNo: true,
                purchase: { 
                  select: { 
                    weightPerBag: true,
                    costPerUnit: true,
                    vegetable: { select: { name: true } } 
                  } 
                },
              },
            },
          },
        },
      },
    });

    return sales.map(serializeSale);
  }

  // â”€â”€ Get Single Retail Sale â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€

  async getRetailSaleById(id: bigint) {
    const sale = await prisma.retailSale.findUnique({
      where: { id },
      include: {
        items: {
          include: {
            retailStock: {
              select: {
                lotNo: true,
                allocatedUnit: true,
                purchase: {
                  select: {
                    weightPerBag: true,
                    costPerUnit: true,
                    vegetable: { select: { name: true, tamilName: true, hindiName: true, kannadaName: true, malayalamName: true, teluguName: true } },
                    supplier: { select: { name: true } },
                  },
                },
              },
            },
          },
        },
      },
    });

    if (!sale) throw new Error("Retail sale not found");
    return serializeSale(sale);
  }

  // â”€â”€ Cancel Retail Sale (soft-delete, restore retail stock) â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€

  async cancelRetailSale(id: bigint) {
    const sale = await prisma.retailSale.findUnique({
      where: { id },
      include: {
        items: {
          include: {
            retailStock: {
              select: { lotNo: true, purchase: { select: { weightPerBag: true } } },
            },
          },
        },
      },
    });

    if (!sale) throw new Error("Retail sale not found");
    if (sale.deletedAt) throw new Error("Retail sale is already cancelled");
    if (sale.movedToWholesale) throw new Error("Cannot cancel a retail sale that has been moved to wholesale");

    return await prisma.$transaction(async (tx) => {
      // Restore retail stock for each item.
      // During creation, FIFO deduction may have spanned multiple RetailStock
      // rows for the same lotNo. We must restore across all rows (reverse-FIFO)
      // to avoid over-restoring a single row beyond its original allocation.
      for (const item of sale.items) {
        const lotNo = item.retailStock?.lotNo;
        if (!lotNo) continue;

        const weightPerBag = item.retailStock?.purchase?.weightPerBag
          ? new Prisma.Decimal(item.retailStock.purchase.weightPerBag)
          : null;

        // Find all RetailStock rows for this lotNo, ordered newest-first
        // (reverse of FIFO deduction) so we fill the most recently depleted first.
        const retailStockRows = await tx.retailStock.findMany({
          where: { lotNo, deletedAt: null },
          orderBy: { id: "desc" },
          select: { id: true, quantity: true, remainingQuantity: true, bags: true, remainingBags: true },
        });

        let kgToRestore = new Prisma.Decimal(item.quantity);

        for (const row of retailStockRows) {
          if (kgToRestore.lte(0)) break;

          // Cap: don't restore beyond the row's original allocated quantity
          const headroom = new Prisma.Decimal(row.quantity).sub(row.remainingQuantity);
          const rowKgRestore = Prisma.Decimal.min(kgToRestore, headroom);

          if (rowKgRestore.lte(0)) continue;

          let rowBagsRestore: Prisma.Decimal | null = null;
          if (weightPerBag && weightPerBag.gt(0) && row.bags !== null && row.remainingBags !== null) {
            rowBagsRestore = rowKgRestore.div(weightPerBag).toDecimalPlaces(3, Prisma.Decimal.ROUND_HALF_UP);
            // Cap bags restoration too
            const bagsHeadroom = new Prisma.Decimal(row.bags).sub(row.remainingBags);
            rowBagsRestore = Prisma.Decimal.min(rowBagsRestore, bagsHeadroom);
          }

          await tx.retailStock.update({
            where: { id: row.id },
            data: {
              remainingQuantity: { increment: rowKgRestore },
              ...(rowBagsRestore !== null && rowBagsRestore.gt(0)
                ? { remainingBags: { increment: rowBagsRestore } }
                : {}),
            },
          });

          kgToRestore = kgToRestore.sub(rowKgRestore);
        }
      }

      // Soft-delete the sale
      return tx.retailSale.update({
        where: { id },
        data: { deletedAt: new Date() },
      });
    });
  }

  async getTodayRetailSalesAmount(): Promise<number> {
    const today = new Date();
    const startOfDay = new Date(Date.UTC(today.getFullYear(), today.getMonth(), today.getDate(), 0, 0, 0, 0));
    const endOfDay = new Date(Date.UTC(today.getFullYear(), today.getMonth(), today.getDate(), 23, 59, 59, 999));

    const totalRetailSalesAmount = await prisma.retailSale.aggregate({
      _sum: {
        grandTotal: true,
      },
      where: {
        invoiceDate: {
          gte: startOfDay,
          lte: endOfDay,
        },
        deletedAt: null,
        movedToWholesale: false,
      },
    });
    return Number(totalRetailSalesAmount._sum.grandTotal || 0);
  }

  // â”€â”€ Meta (customers and payment modes for dropdown) â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€

  async getRetailSaleMeta() {
    const paymentModes = Object.values(PaymentMode);

    return {
      paymentModes,
    };
  }
  // â”€â”€ Move Retail Sale to Wholesale â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€

  async moveToWholesale(id: bigint) {
    const sale = await prisma.retailSale.findUnique({ where: { id } });
    if (!sale) throw new Error("Retail sale not found");
    if (sale.deletedAt) throw new Error("Cannot move a cancelled retail sale");
    if (sale.movedToWholesale) throw new Error("Retail sale is already moved to wholesale");

    const updated = await prisma.retailSale.update({
      where: { id },
      data: { movedToWholesale: true },
      include: { items: true },
    });

    return serializeSale(updated);
  }

  // â”€â”€ Moved to Wholesale Helpers â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€

  async getTodayMovedToWholesaleAmount(): Promise<number> {
    const today = new Date();
    const startOfDay = new Date(Date.UTC(today.getFullYear(), today.getMonth(), today.getDate(), 0, 0, 0, 0));
    const endOfDay = new Date(Date.UTC(today.getFullYear(), today.getMonth(), today.getDate(), 23, 59, 59, 999));

    const totalMoved = await prisma.retailSale.aggregate({
      _sum: { grandTotal: true },
      where: {
        invoiceDate: { gte: startOfDay, lte: endOfDay },
        deletedAt: null,
        movedToWholesale: true,
      },
    });
    return Number(totalMoved._sum.grandTotal || 0);
  }

  async getMovedToWholesaleProfitChart(): Promise<{ day: string; sales: number; profit: number }[]> {
    const today = new Date();
    const days: { label: string; start: Date; end: Date }[] = [];

    for (let i = 6; i >= 0; i--) {
      const date = new Date(today);
      date.setDate(today.getDate() - i);
      const start = new Date(Date.UTC(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0, 0));
      const end = new Date(Date.UTC(date.getFullYear(), date.getMonth(), date.getDate(), 23, 59, 59, 999));
      const label = date.toLocaleDateString("en-IN", { weekday: "short" });
      days.push({ label, start, end });
    }

    const rangeStart = days[0]!.start;
    const rangeEnd = days[days.length - 1]!.end;

    const allMovedSales = await prisma.retailSale.findMany({
      where: {
        invoiceDate: { gte: rangeStart, lte: rangeEnd },
        deletedAt: null,
        movedToWholesale: true,
      },
      include: {
        items: {
          include: {
            retailStock: {
              select: {
                purchase: {
                  select: {
                    purchaseType: true,
                    arrivalSettlement: { select: { status: true } },
                    vegetable: { select: { isFlexible: true } },
                  },
                },
              },
            },
          },
        },
      },
    });

    return days.map(({ label, start, end }) => {
      const daySales = allMovedSales.filter(
        (s) => s.invoiceDate && s.invoiceDate >= start && s.invoiceDate <= end
      );

      const salesTotal = daySales.reduce((sum, sale) => sum + Number(sale.grandTotal), 0);

      let dayProfit = 0;
      for (const sale of daySales) {
        let saleItemProfit = 0;
        for (const item of sale.items) {
          const isArrival = (item as any).retailStock?.purchase?.purchaseType === "COMMISSION_ARRIVAL";
          const isFlexible = (item as any).retailStock?.purchase?.vegetable?.isFlexible || item.saleUnit === "PIECES";
          if (isArrival) {
            const isSettled = (item as any).retailStock?.purchase?.arrivalSettlement?.status === "SETTLED";
            saleItemProfit += isSettled ? Number(item.total) * (ARRIVAL_COMMISSION_PERCENT / 100) : 0;
          } else if (isFlexible) {
            // Flexible produce sold in pieces or loose: do not calculate profit
            continue;
          } else {
            // item.quantity is always in KG; costPerUnit is always per-KG.
            const costOfGoodsSold = Number(item.quantity || 0) * Number(item.costPerUnit);
            saleItemProfit += Number(item.total) - costOfGoodsSold;
          }
        }
        const saleDiscount = Number(sale.discount || 0);
        dayProfit += saleItemProfit - saleDiscount;
      }

      return {
        day: label,
        sales: salesTotal,
        profit: Math.round(dayProfit * 100) / 100,
      };
    });
  }

  // Dashboard stat-card: today's moved-to-wholesale profit with arrival-aware logic
  async getTodayMovedToWholesaleProfit(): Promise<number> {
    const today = new Date();
    const startOfDay = new Date(Date.UTC(today.getFullYear(), today.getMonth(), today.getDate(), 0, 0, 0, 0));
    const endOfDay = new Date(Date.UTC(today.getFullYear(), today.getMonth(), today.getDate(), 23, 59, 59, 999));

    const sales = await prisma.retailSale.findMany({
      where: {
        invoiceDate: { gte: startOfDay, lte: endOfDay },
        deletedAt: null,
        movedToWholesale: true,
      },
      include: {
        items: {
          include: {
            retailStock: {
              select: {
                purchase: {
                  select: {
                    purchaseType: true,
                    arrivalSettlement: { select: { status: true } },
                    vegetable: { select: { isFlexible: true } },
                  },
                },
              },
            },
          },
        },
      },
    });

    let totalProfit = 0;
    for (const sale of sales) {
      let saleItemProfit = 0;
      for (const item of sale.items) {
        const isArrival = (item as any).retailStock?.purchase?.purchaseType === "COMMISSION_ARRIVAL";
        const isFlexible = (item as any).retailStock?.purchase?.vegetable?.isFlexible || item.saleUnit === "PIECES";
        if (isArrival) {
          const isSettled = (item as any).retailStock?.purchase?.arrivalSettlement?.status === "SETTLED";
          saleItemProfit += isSettled ? Number(item.total) * (ARRIVAL_COMMISSION_PERCENT / 100) : 0;
        } else if (isFlexible) {
          // Flexible produce sold in pieces or loose: do not calculate profit
          continue;
        } else {
          // item.quantity is always in KG; costPerUnit is always per-KG.
          const costOfGoodsSold = Number(item.quantity || 0) * Number(item.costPerUnit);
          saleItemProfit += Number(item.total) - costOfGoodsSold;
        }
      }
      const saleDiscount = Number(sale.discount || 0);
      totalProfit += saleItemProfit - saleDiscount;
    }

    return Math.round(totalProfit * 100) / 100;
  }

  async getRecentMovedToWholesaleSales(): Promise<{ invoiceNo: string; customer: string; amount: number; status: string; date: string; createdAt: Date }[]> {
    const today = new Date();
    const startOfDay = new Date(Date.UTC(today.getFullYear(), today.getMonth(), today.getDate(), 0, 0, 0, 0));

    const sales = await prisma.retailSale.findMany({
      where: {
        deletedAt: null,
        movedToWholesale: true,
        invoiceDate: { gte: startOfDay },
      },
      orderBy: { createdAt: "desc" },
    });

    return sales.map((s) => {
      let dateStr = "";
      if (s.invoiceDate) {
        const iso = new Date(s.invoiceDate as unknown as string).toISOString();
        dateStr = iso.substring(0, 10);
      }
      return {
        invoiceNo: s.invoiceNo,
        customer: s.walkInName ? `${s.walkInName} (Retail)` : "Retail Customer",
        amount: Number(s.grandTotal),
        status: String(s.paymentStatus),
        date: dateStr,
        createdAt: s.createdAt,
      };
    });
  }

}
