import { PrismaClient } from "@prisma/client";

const prisma = new PrismaClient();

const OPENING_EVENT_TYPES = new Set(["OPENING_COST"]);
const SALE_EVENT_TYPES = new Set(["SALE_REVENUE", "CONSIGNMENT_REVENUE"]);
const RETURN_EVENT_TYPES = new Set(["SALE_RETURN", "CONSIGNMENT_RETURN"]);
const COST_ADDITIONAL_EVENT_TYPES = new Set([
  "WASHING_COST",
  "WASHING_SHRINKAGE",
  "TRANSFORMATION_SHRINKAGE",
  "SALE_SHRINKAGE",
  "CONSIGNMENT_SHRINKAGE",
  "STOCK_ADJUSTMENT_LOSS",
  "MANUAL_ADJUSTMENT"
]);

const roundQty = (value) => Number(value.toFixed(3));
const roundAmount = (value) => Number(value.toFixed(2));

async function recalcSummary(purchaseId, agentSharePercent) {
  const entries = await prisma.purchaseRealizationEntry.findMany({
    where: { purchaseId }
  });

  const aggregate = entries.reduce(
    (state, entry) => {
      const qtyIn = entry.qtyIn.toNumber();
      const qtyOut = entry.qtyOut.toNumber();
      const qtyShrinkage = entry.qtyShrinkage.toNumber();
      const amountCost = entry.amountCost.toNumber();
      const amountRevenue = entry.amountRevenue.toNumber();
      const amountExpense = entry.amountExpense.toNumber();
      const agentAmount = entry.agentAmount.toNumber();

      state.qtyRemaining += qtyIn - qtyOut - qtyShrinkage;
      state.profitLossTotal += amountRevenue - amountCost - amountExpense;
      state.agentProfitTotal += agentAmount;

      if (OPENING_EVENT_TYPES.has(entry.eventType)) {
        state.qtyOpening += qtyIn;
        state.costOpeningTotal += amountCost;
      }
      if (SALE_EVENT_TYPES.has(entry.eventType)) {
        state.qtySold += qtyOut;
        state.revenueTotal += amountRevenue;
      }
      if (RETURN_EVENT_TYPES.has(entry.eventType)) {
        state.qtyReturned += qtyIn;
      }
      if (COST_ADDITIONAL_EVENT_TYPES.has(entry.eventType)) {
        state.costAdditionalTotal += amountCost + amountExpense;
      }
      state.qtyShrinkage += qtyShrinkage;
      return state;
    },
    {
      qtyOpening: 0,
      qtyRemaining: 0,
      qtySold: 0,
      qtyReturned: 0,
      qtyShrinkage: 0,
      costOpeningTotal: 0,
      costAdditionalTotal: 0,
      revenueTotal: 0,
      profitLossTotal: 0,
      agentProfitTotal: 0
    }
  );

  const status =
    aggregate.qtyOpening <= 0
      ? "OPEN"
      : aggregate.qtyRemaining <= 0
      ? "READY_TO_CLOSE"
      : aggregate.qtySold > 0 || aggregate.qtyShrinkage > 0
      ? "PARTIAL"
      : "OPEN";

  await prisma.purchaseRealizationSummary.upsert({
    where: { purchaseId },
    update: {
      status,
      qtyOpening: roundQty(aggregate.qtyOpening),
      qtyRemaining: roundQty(aggregate.qtyRemaining),
      qtySold: roundQty(aggregate.qtySold),
      qtyReturned: roundQty(aggregate.qtyReturned),
      qtyShrinkage: roundQty(aggregate.qtyShrinkage),
      costOpeningTotal: roundAmount(aggregate.costOpeningTotal),
      costAdditionalTotal: roundAmount(aggregate.costAdditionalTotal),
      revenueTotal: roundAmount(aggregate.revenueTotal),
      profitLossTotal: roundAmount(aggregate.profitLossTotal),
      agentSharePercent,
      agentProfitTotal: roundAmount(aggregate.agentProfitTotal),
      closedAt: null
    },
    create: {
      purchaseId,
      status,
      qtyOpening: roundQty(aggregate.qtyOpening),
      qtyRemaining: roundQty(aggregate.qtyRemaining),
      qtySold: roundQty(aggregate.qtySold),
      qtyReturned: roundQty(aggregate.qtyReturned),
      qtyShrinkage: roundQty(aggregate.qtyShrinkage),
      costOpeningTotal: roundAmount(aggregate.costOpeningTotal),
      costAdditionalTotal: roundAmount(aggregate.costAdditionalTotal),
      revenueTotal: roundAmount(aggregate.revenueTotal),
      profitLossTotal: roundAmount(aggregate.profitLossTotal),
      agentSharePercent,
      agentProfitTotal: roundAmount(aggregate.agentProfitTotal),
      closedAt: null
    }
  });
}

async function main() {
  const purchases = await prisma.purchase.findMany({
    where: {
      status: "SUBMITTED",
      purchaseType: {
        in: ["REGULAR", "OFFICE_BUYOUT"]
      }
    },
    include: {
      profitShareScheme: {
        select: {
          shareAgent: true
        }
      },
      lots: true
    },
    orderBy: { id: "asc" }
  });

  let allocationsCreated = 0;
  let entriesCreated = 0;

  for (const purchase of purchases) {
    for (const lot of purchase.lots) {
      const allocationCount = await prisma.lotPurchaseAllocation.count({
        where: {
          lotId: lot.id,
          purchaseId: purchase.id
        }
      });

      if (allocationCount === 0) {
        await prisma.lotPurchaseAllocation.create({
          data: {
            lotId: lot.id,
            purchaseId: purchase.id,
            purchaseLineId: lot.purchaseLineId,
            sourceType: lot.sourceType,
            sourceRefId: lot.sourceRefId,
            agentIdSnapshot: purchase.agentId,
            profitShareSchemeIdSnapshot: purchase.profitShareSchemeId,
            qtyAllocated: lot.availableQty,
            costTotalAllocated: roundAmount(lot.availableQty.toNumber() * lot.unitCost.toNumber()),
            unitCostSnapshot: lot.unitCost,
            notes: "Backfill opening allocation"
          }
        });
        allocationsCreated += 1;
      }

      const openingCount = await prisma.purchaseRealizationEntry.count({
        where: {
          purchaseId: purchase.id,
          lotId: lot.id,
          eventType: "OPENING_COST"
        }
      });

      if (openingCount === 0) {
        const allocation = await prisma.lotPurchaseAllocation.findFirst({
          where: {
            lotId: lot.id,
            purchaseId: purchase.id
          },
          orderBy: { id: "asc" }
        });

        await prisma.purchaseRealizationEntry.create({
          data: {
            purchaseId: purchase.id,
            lotId: lot.id,
            allocationId: allocation?.id ?? null,
            eventType: "OPENING_COST",
            referenceType: "PURCHASE",
            referenceId: purchase.id,
            occurredAt: lot.receivedAt,
            qtyIn: lot.originalQty,
            qtyOut: 0,
            qtyShrinkage: 0,
            amountCost: roundAmount(lot.originalQty.toNumber() * lot.unitCost.toNumber()),
            amountRevenue: 0,
            amountExpense: 0,
            amountProfit: 0,
            agentSharePercentSnapshot: purchase.profitShareScheme?.shareAgent ?? null,
            agentAmount: 0,
            notes: "Backfill opening realization"
          }
        });
        entriesCreated += 1;
      }
    }

    await recalcSummary(
      purchase.id,
      purchase.profitShareScheme?.shareAgent?.toNumber() ?? null
    );
  }

  console.log(
    JSON.stringify(
      {
        purchases: purchases.length,
        allocations_created: allocationsCreated,
        entries_created: entriesCreated
      },
      null,
      2
    )
  );
}

main()
  .catch((error) => {
    console.error(error);
    process.exitCode = 1;
  })
  .finally(async () => {
    await prisma.$disconnect();
  });
