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

export class ArrivalRepository {
  async create(data: Prisma.PurchaseUncheckedCreateInput, tx: Prisma.TransactionClient = prisma) {
    return tx.purchase.create({ data });
  }

  async findAll(
    skip?: number,
    take?: number,
    search?: string,
    dateFrom?: Date,
    dateTo?: Date,
    supplierId?: bigint,
    vegetableId?: bigint,
    settlementStatus?: "UNSETTLED" | "PARTIALLY_SETTLED" | "SETTLED" | "ALL"
  ) {
    const where: Prisma.PurchaseWhereInput = {
      purchaseType: "COMMISSION_ARRIVAL",
      deletedAt: null,
    };

    if (search) {
      where.OR = [
        { lotNo: { contains: search } },
        { billNo: { contains: search } },
        { truckNo: { contains: search } },
        { agentName: { contains: search } },
        { placeOfDespatch: { contains: search } },
        { placeOfArrival: { contains: search } },
        { supplier: { name: { contains: search } } },
        { cashSupplierName: { contains: search } },
        { vegetable: { name: { contains: search } } },
      ];
    }

    if (dateFrom || dateTo) {
      where.purchaseDate = {};
      if (dateFrom) where.purchaseDate.gte = dateFrom;
      if (dateTo) where.purchaseDate.lte = dateTo;
    }

    if (supplierId !== undefined) {
      where.supplierId = supplierId;
    }

    if (vegetableId !== undefined) {
      where.vegetableId = vegetableId;
    }

    if (settlementStatus && settlementStatus !== "ALL") {
      if (settlementStatus === "UNSETTLED") {
        const unsettledOR: Prisma.PurchaseWhereInput[] = [
          { arrivalSettlement: null },
          { arrivalSettlement: { status: "UNSETTLED" } },
        ];
        if (where.OR) {
          // Both search and settlement filter active — combine with AND to preserve both
          where.AND = [
            { OR: where.OR as Prisma.PurchaseWhereInput[] },
            { OR: unsettledOR },
          ];
          delete where.OR;
        } else {
          where.OR = unsettledOR;
        }
      } else {
        where.arrivalSettlement = { status: settlementStatus };
      }
    }

    const findOptions: any = {
      where,
      include: {
        supplier: {
          select: { id: true, name: true, mobileNo: true },
        },
        vegetable: {
          select: { id: true, name: true, tamilName: true },
        },
        arrivalSettlement: true,
        arrivalCustomPatti: true,
        retailStocks: {
          where: { deletedAt: null },
          select: {
            id: true,
            quantity: true,
            remainingQuantity: true,
            bags: true,
            remainingBags: true,
          },
        },
        _count: {
          select: { saleItems: true },
        },
      },
      orderBy: { id: 'desc' },
    };

    if (skip !== undefined) findOptions.skip = skip;
    if (take !== undefined) findOptions.take = take;

    const [items, total] = await Promise.all([
      prisma.purchase.findMany(findOptions),
      prisma.purchase.count({ where }),
    ]);

    return { items, total };
  }

  async findById(id: bigint, tx: Prisma.TransactionClient = prisma) {
    return tx.purchase.findFirst({
      where: {
        id,
        purchaseType: "COMMISSION_ARRIVAL",
        deletedAt: null,
      },
      include: {
        supplier: true,
        vegetable: true,
        arrivalSettlement: true,
        arrivalCustomPatti: true,
        saleItems: {
          where: { sale: { deletedAt: null } },
          include: {
            sale: {
              select: {
                id: true,
                invoiceNo: true,
                invoiceDate: true,
                walkInName: true,
                customer: { select: { id: true, name: true } },
              },
            },
          },
          orderBy: { createdAt: "asc" },
        },
        retailStocks: {
          where: { deletedAt: null },
          include: {
            retailSaleItems: {
              where: { retailSale: { deletedAt: null } },
              include: {
                retailSale: {
                  select: {
                    id: true,
                    invoiceNo: true,
                    invoiceDate: true,
                    walkInName: true,
                  },
                },
              },
              orderBy: { createdAt: "asc" },
            },
            retailStockSpoilages: {
              orderBy: { spoilageDate: "desc" },
            },
          },
        },
        stockSpoilages: {
          orderBy: { spoilageDate: "desc" },
        },
      },
    });
  }

  async update(id: bigint, data: Prisma.PurchaseUncheckedUpdateInput, tx: Prisma.TransactionClient = prisma) {
    return tx.purchase.update({ where: { id }, data });
  }

  async softDelete(id: bigint, tx: Prisma.TransactionClient = prisma) {
    return tx.purchase.update({
      where: { id },
      data: { deletedAt: new Date() },
    });
  }

  async findActiveByLotNo(lotNo: string, excludeId?: bigint, vegetableId?: bigint, tx: Prisma.TransactionClient = prisma) {
    return tx.purchase.findFirst({
      where: {
        lotNo,
        status: { not: "CLOSED" },
        ...(vegetableId ? { vegetableId } : {}),
        OR: [
          { remainingQuantity: { gt: 0 } },
          { remainingBags: { gt: 0 } },
          { vegetable: { isFlexible: true } },
        ],
        deletedAt: null,
        ...(excludeId ? { id: { not: excludeId } } : {}),
      },
      include: {
        vegetable: { select: { id: true, name: true, isFlexible: true } },
      },
    });
  }

  async findAnyByLotNo(lotNo: string, tx: Prisma.TransactionClient = prisma) {
    return tx.purchase.findFirst({
      where: { 
        lotNo, 
        deletedAt: null 
      },
      orderBy: { id: 'desc' },
      select: { vegetableId: true, lotNo: true }
    });
  }

  async getArrivalSalesSummary(purchaseId: bigint, tx: Prisma.TransactionClient = prisma) {
    // 1. Fetch Wholesale sale items (include saleReturns so we can compute net values)
    const wholesaleSaleItems = await tx.saleItem.findMany({
      where: {
        purchaseId,
        sale: { deletedAt: null },
      },
      include: {
        sale: {
          select: {
            invoiceNo: true,
            invoiceDate: true,
            walkInName: true,
            customer: { select: { name: true } },
          },
        },
        saleReturns: true,
      },
      orderBy: { createdAt: "asc" },
    });

    // 2. Fetch Retail sale items for this purchase lot
    const retailSaleItems = await tx.retailSaleItem.findMany({
      where: {
        retailStock: { purchaseId, deletedAt: null },
        retailSale: { deletedAt: null },
      },
      include: {
        retailSale: {
          select: {
            invoiceNo: true,
            invoiceDate: true,
            walkInName: true,
          },
        },
        retailStock: {
          select: {
            lotNo: true,
            allocatedUnit: true,
          },
        },
      },
      orderBy: { createdAt: "asc" },
    });

    // 3. Fetch Retail stocks summary (allocated and remaining)
    const retailStocks = await tx.retailStock.findMany({
      where: {
        purchaseId,
        deletedAt: null,
      },
      select: {
        id: true,
        quantity: true,
        bags: true,
        remainingQuantity: true,
        remainingBags: true,
      },
    });

    const retailRemainingQty = retailStocks.reduce(
      (sum, rs) => sum.plus(rs.remainingQuantity),
      new Prisma.Decimal(0)
    );
    const retailRemainingBags = retailStocks.reduce(
      (sum, rs) => sum.plus(rs.remainingBags ?? 0),
      new Prisma.Decimal(0)
    );
    const retailAllocatedQty = retailStocks.reduce(
      (sum, rs) => sum.plus(rs.quantity),
      new Prisma.Decimal(0)
    );
    const retailAllocatedBags = retailStocks.reduce(
      (sum, rs) => sum.plus(rs.bags ?? 0),
      new Prisma.Decimal(0)
    );

    // Format wholesale items (with sale return adjustments)
    const formattedWholesaleItems = wholesaleSaleItems.map((item) => {
      const returnedQuantity = (item.saleReturns || []).reduce(
        (acc, ret) => acc.add(new Prisma.Decimal(ret.quantity)),
        new Prisma.Decimal(0)
      );
      const returnedAmount = (item.saleReturns || []).reduce(
        (acc, ret) => acc.add(new Prisma.Decimal(ret.returnAmount)),
        new Prisma.Decimal(0)
      );

      const netQuantity = new Prisma.Decimal(item.quantity).sub(returnedQuantity);
      const netTotal = new Prisma.Decimal(item.total).sub(returnedAmount);

      return {
        id: `WS-${item.id}`,
        channel: "WHOLESALE" as const,
        source: "Wholesale",
        saleId: item.saleId,
        invoiceNo: item.sale.invoiceNo,
        invoiceDate: item.sale.invoiceDate,
        buyerName: item.sale.customer?.name || item.sale.walkInName || "Wholesale Buyer",
        quantity: netQuantity,
        bags: item.bags,
        sellingRate: item.sellingRate,
        total: netTotal,
        returnedQuantity,
        returnedAmount,
        originalQuantity: item.quantity,
        originalTotal: item.total,
        netQuantity,
        netTotal,
        createdAt: item.createdAt,
        sale: item.sale,
        saleReturns: item.saleReturns,
      };
    });

    // Format retail items
    const formattedRetailItems = retailSaleItems.map((item) => ({
      id: `RET-${item.id}`,
      channel: "RETAIL" as const,
      source: "Retail",
      saleId: item.retailSaleId,
      invoiceNo: item.retailSale.invoiceNo,
      invoiceDate: item.retailSale.invoiceDate,
      buyerName: item.retailSale.walkInName || "Retail Walk-in",
      quantity: item.quantity,
      bags: item.bags,
      sellingRate: item.sellingRate,
      total: item.total,
      returnedQuantity: new Prisma.Decimal(0),
      returnedAmount: new Prisma.Decimal(0),
      netQuantity: item.quantity,
      netTotal: item.total,
      createdAt: item.createdAt,
      sale: {
        invoiceNo: item.retailSale.invoiceNo,
        invoiceDate: item.retailSale.invoiceDate,
        walkInName: item.retailSale.walkInName || "Retail Walk-in",
        customer: null,
      },
      saleReturns: [],
    }));

    const allSaleItems = [...formattedWholesaleItems, ...formattedRetailItems].sort(
      (a, b) => new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime()
    );

    const totalWeightSold = allSaleItems.reduce(
      (sum, item) => sum.plus(item.netQuantity),
      new Prisma.Decimal(0)
    );

    const grossSalesTotal = allSaleItems.reduce(
      (sum, item) => sum.plus(item.netTotal),
      new Prisma.Decimal(0)
    );

    const totalBagsSold = allSaleItems.reduce(
      (sum, item) => sum.plus(item.bags ?? 0),
      new Prisma.Decimal(0)
    );

    const totalReturnedQuantity = allSaleItems.reduce(
      (sum, item) => sum.plus(item.returnedQuantity),
      new Prisma.Decimal(0)
    );

    const totalReturnedAmount = allSaleItems.reduce(
      (sum, item) => sum.plus(item.returnedAmount),
      new Prisma.Decimal(0)
    );

    const wholesaleWeightSold = formattedWholesaleItems.reduce(
      (sum, item) => sum.plus(item.netQuantity),
      new Prisma.Decimal(0)
    );
    const wholesaleGrossTotal = formattedWholesaleItems.reduce(
      (sum, item) => sum.plus(item.netTotal),
      new Prisma.Decimal(0)
    );

    const retailWeightSold = formattedRetailItems.reduce(
      (sum, item) => sum.plus(item.netQuantity),
      new Prisma.Decimal(0)
    );
    const retailGrossTotal = formattedRetailItems.reduce(
      (sum, item) => sum.plus(item.netTotal),
      new Prisma.Decimal(0)
    );

    return {
      saleItems: allSaleItems,
      wholesaleSaleItems: formattedWholesaleItems,
      retailSaleItems: formattedRetailItems,
      totalWeightSold,
      totalBagsSold,
      grossSalesTotal,
      totalReturnedQuantity,
      totalReturnedAmount,
      wholesaleWeightSold,
      wholesaleGrossTotal,
      retailWeightSold,
      retailGrossTotal,
      retailAllocatedQty,
      retailAllocatedBags,
      retailRemainingQty,
      retailRemainingBags,
    };
  }
}
