import { NextResponse } from "next/server";
import { Prisma } from "@prisma/client";

import { createAuditTrailSafe } from "@/lib/audit-trail";
import { buildAuditChangeMetadata } from "@/lib/audit-trail-diff";
import { requireApiAccess } from "@/lib/authorization";
import { prisma } from "@/lib/prisma";
import { recalculatePurchaseRealizationSummary } from "@/features/purchase-realization/lib/recalculate-purchase-realization-summary";
import { generateLotCode } from "@/features/receipts/lib/generate-lot-code";
import { generateReceiptNo } from "@/features/receipts/lib/generate-receipt-no";

type RouteContext = { params: Promise<{ id: string }> };
type SubmitTx = Prisma.TransactionClient & {
  lotPurchaseAllocation: typeof prisma.lotPurchaseAllocation;
  purchaseRealizationEntry: typeof prisma.purchaseRealizationEntry;
  purchaseRealizationSummary: typeof prisma.purchaseRealizationSummary;
};
const parseId = (id: string) => {
  try {
    return BigInt(id);
  } catch {
    return null;
  }
};

export async function POST(request: Request, context: RouteContext) {
  const auth = requireApiAccess(request);
  if (!auth.ok) return auth.response;

  const parsedId = parseId((await context.params).id);
  if (parsedId === null) return NextResponse.json({ message: "Invalid id" }, { status: 400 });

  try {
    const purchaseId = parsedId;
    const purchase = await prisma.purchase.findUnique({
      where: { id: purchaseId },
      include: {
        agent: { select: { id: true, name: true } },
        profitShareScheme: { select: { id: true, shareAgent: true } },
        receipts: {
          select: {
            id: true,
            receiptNo: true
          }
        },
        lines: {
          include: {
            grade: true,
            unit: true,
            warehouse: true,
            warehouseLocation: true
          }
        }
      }
    });
    if (!purchase) return NextResponse.json({ message: "Purchase not found" }, { status: 404 });

    if (purchase.status === "SUBMITTED") {
      return NextResponse.json({ message: "Purchase sudah disubmit" }, { status: 409 });
    }

    if (purchase.receipts.length > 0) {
      return NextResponse.json(
        { message: `Purchase sudah memiliki receipt ${purchase.receipts[0]?.receiptNo ?? ""}`.trim() },
        { status: 409 }
      );
    }

    if (!purchase.receivedByEmployeeId) {
      return NextResponse.json({ message: "Penerima belum dipilih" }, { status: 400 });
    }

    if (!purchase.receivedAt) {
      return NextResponse.json({ message: "Waktu diterima belum diisi" }, { status: 400 });
    }

    const receivedAt = purchase.receivedAt;
    const receiptDate = new Date(receivedAt.toISOString().slice(0, 10));
    const receiptNo = await generateReceiptNo(receiptDate);
    const sourceCode = purchase.agent?.name || purchase.purchaseNo;
    const enteredQtyLines = purchase.lines.filter((line) => Number(line.qtyAccepted.toNumber()) > 0);
    if (enteredQtyLines.length === 0) {
      return NextResponse.json(
        { message: "Tidak ada baris dengan qty yang masuk > 0 untuk generate lot" },
        { status: 400 }
      );
    }
    const missingWarehouseLine = enteredQtyLines.find((line) => !line.warehouseId);
    if (missingWarehouseLine) {
      return NextResponse.json(
        { message: "Warehouse belum diisi pada salah satu baris pembelian" },
        { status: 400 }
      );
    }

    const generated = await prisma.$transaction(async (rawTx) => {
      const tx = rawTx as SubmitTx;
      const baseLotCode = await generateLotCode(receivedAt, sourceCode);
      const codeMatch = baseLotCode.match(/-(\d+)$/);
      if (!codeMatch) {
        throw new Error("Gagal generate kode lot");
      }
      const lotPrefix = baseLotCode.replace(/-\d+$/, "");
      let lotSuffix = Number(codeMatch[1] ?? "0") || 1;

      const availableLotCount = await tx.inventoryLot.count({ where: { purchaseId } });
      if (availableLotCount > 0) {
        throw new Error("Lot untuk purchase ini sudah pernah digenerate");
      }

      const existingReceiptCount = await tx.receipt.count({ where: { purchaseId } });
      if (existingReceiptCount > 0) {
        throw new Error("Receipt untuk purchase ini sudah pernah dibuat");
      }

      const receipt = await tx.receipt.create({
        data: {
          receiptNo,
          purchaseId: purchase.id,
          receiptDate,
          status: "FINALIZED",
          notes: purchase.notes || null,
          receivedById: BigInt(auth.user.id)
        }
      });

      await tx.receiptLine.createMany({
        data: enteredQtyLines.map((line) => ({
          receiptId: receipt.id,
          purchaseLineId: line.id,
          gradeId: line.gradeId,
          qtyReceived: line.qtyReceived,
          qtyAccepted: line.qtyAccepted,
          qtyRejected: line.qtyRejected,
          unitId: line.unitId,
          unitCost: line.unitCost,
          warehouseId: line.warehouseId!,
          warehouseLocationId: line.warehouseLocationId,
          notes: line.notes || null
        }))
      });

      const receiptLines = await tx.receiptLine.findMany({
        where: { receiptId: receipt.id },
        orderBy: { id: "asc" }
      });

      const lots = [];
      for (const line of receiptLines) {
        if (!line.warehouseId) {
          throw new Error("Warehouse belum diisi pada salah satu baris pembelian");
        }
        const availableQty = Number(line.qtyAccepted.toNumber());
        const lotCode = `${lotPrefix}-${String(lotSuffix).padStart(3, "0")}`;
        lotSuffix += 1;

        const lot = await tx.inventoryLot.create({
          data: {
            lotCode,
            sourceType: "PURCHASE",
            sourceRefId: purchaseId,
            purchaseId: purchase.id,
            purchaseLineId: line.purchaseLineId,
            receiptId: receipt.id,
            receiptLineId: line.id,
            gradeId: line.gradeId,
            warehouseId: line.warehouseId,
            warehouseLocationId: line.warehouseLocationId,
            originalQty: availableQty,
            availableQty,
            unitId: line.unitId,
            unitCost: line.unitCost,
            receivedAt,
            status: "ACTIVE",
            qrCodeValue: lotCode,
            barcodeValue: lotCode
          }
        });

        const costTotalAllocated = Number(
          (availableQty * line.unitCost.toNumber()).toFixed(2)
        );
        const allocation = await tx.lotPurchaseAllocation.create({
          data: {
            lotId: lot.id,
            purchaseId: purchase.id,
            purchaseLineId: line.id,
            sourceType: "PURCHASE",
            sourceRefId: purchase.id,
            agentIdSnapshot: purchase.agentId,
            profitShareSchemeIdSnapshot: purchase.profitShareSchemeId,
            qtyAllocated: new Prisma.Decimal(availableQty),
            costTotalAllocated: new Prisma.Decimal(costTotalAllocated),
            unitCostSnapshot: line.unitCost
          }
        });

        await tx.purchaseRealizationEntry.create({
          data: {
            purchaseId: purchase.id,
            lotId: lot.id,
            allocationId: allocation.id,
            eventType: "OPENING_COST",
            referenceType: "PURCHASE",
            referenceId: purchase.id,
            occurredAt: receivedAt,
            qtyIn: new Prisma.Decimal(availableQty),
            qtyOut: new Prisma.Decimal(0),
            qtyShrinkage: new Prisma.Decimal(0),
            amountCost: new Prisma.Decimal(costTotalAllocated),
            amountRevenue: new Prisma.Decimal(0),
            amountExpense: new Prisma.Decimal(0),
            amountProfit: new Prisma.Decimal(0),
            agentSharePercentSnapshot: purchase.profitShareScheme?.shareAgent ?? null,
            agentAmount: new Prisma.Decimal(0),
            notes: `Opening realization dari purchase line ${line.purchaseLineId.toString()}`
          }
        });

        lots.push({
          id: lot.id.toString(),
          lot_code: lot.lotCode
        });
      }

      const updated = await tx.purchase.update({
        where: { id: purchaseId },
        data: { status: "SUBMITTED" }
      });

      await recalculatePurchaseRealizationSummary(
        tx,
        purchase.id,
        purchase.profitShareScheme?.shareAgent.toNumber() ?? null
      );

      return {
        purchase: updated,
        receipt: {
          id: receipt.id.toString(),
          receipt_no: receipt.receiptNo
        },
        lots
      };
    });

    await createAuditTrailSafe({
      userId: auth.user.id,
      action: "PURCHASE_SUBMITTED",
      entityType: "PURCHASE",
      entityId: generated.purchase.id,
      method: request.method,
      pathname: new URL(request.url).pathname,
      statusCode: 200,
      summary: `Purchase ${purchase.purchaseNo} disubmit + receipt + lot dibuat`,
      metadata: buildAuditChangeMetadata(
        { status: purchase.status, lot_count: 0, receipt_no: null },
        {
          status: generated.purchase.status,
          lot_count: generated.lots.length,
          receipt_no: generated.receipt.receipt_no
        }
      )
    });

    if (generated.lots.length === 0) {
      return NextResponse.json({ message: "Lot untuk purchase ini sudah pernah digenerate" }, { status: 409 });
    }

    return NextResponse.json(
      {
        success: true,
        status: generated.purchase.status,
        lot_count: generated.lots.length,
        receipt: generated.receipt
      },
      { status: 200 }
    );
  } catch (error) {
    if (error instanceof Prisma.PrismaClientKnownRequestError && error.code === "P2002") {
      return NextResponse.json(
        { message: "Kode lot duplikat saat submit. Coba lagi." },
        { status: 409 }
      );
    }
    if (error instanceof Error) {
      console.error("PURCHASE_SUBMIT_ERROR", {
        purchaseId: parsedId?.toString(),
        message: error.message,
        stack: error.stack
      });
      return NextResponse.json({ message: error.message }, { status: 400 });
    }
    console.error("PURCHASE_SUBMIT_ERROR", {
      purchaseId: parsedId?.toString(),
      message: "Unknown error"
    });
    return NextResponse.json({ message: "Submit gagal karena kesalahan internal" }, { status: 500 });
  }
}
