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

import { buildPurchaseCostEntries } from "@/features/purchases/lib/build-purchase-cost-entries";
import { serializePurchaseDetail } from "@/features/purchases/lib/serialize-purchase";
import { isPurchasePayloadValidationError, parsePurchaseRequestPayload } from "@/features/purchases/lib/parse-purchase-request";
import { type PurchaseInput } from "@/features/purchases/schemas/purchase.schema";
import { createAuditTrailSafe } from "@/lib/audit-trail";
import { buildAuditChangeMetadata } from "@/lib/audit-trail-diff";
import { requireApiAccess } from "@/lib/authorization";
import { prisma } from "@/lib/prisma";

type RouteContext = { params: Promise<{ id: string }> };
const parseId = (id: string) => {
  try {
    return BigInt(id);
  } catch {
    return null;
  }
};

type ParsedLine = PurchaseInput["lines"][number];

function sumLineQty(lines: ParsedLine[]) {
  return lines.reduce((sum, line) => sum + Number(line.qty_ordered || 0), 0);
}

function toBigIntOrNull(raw: string | undefined | null) {
  if (!raw) return null;
  return BigInt(raw);
}

function toDateOrThrow(rawDate: string, label: string): Date {
  const parsed = new Date(rawDate);
  if (Number.isNaN(parsed.getTime())) {
    throw new Error(`${label} tidak valid`);
  }
  return parsed;
}

function buildLineCreateInput(
  line: ParsedLine,
  defaultWarehouseId: string,
  defaultWarehouseLocationId: string | null,
  fallbackGradeId: string
) {
  const warehouseId = line.warehouse_id || defaultWarehouseId;
  const warehouseLocationId = line.warehouse_location_id || defaultWarehouseLocationId;
  return {
    gradeId: BigInt(line.grade_id || fallbackGradeId),
    qtyOrdered: line.qty_ordered,
    purchaseMoisturePercent: null,
    qtyReceived: line.qty_received,
    qtyAccepted: line.qty_accepted,
    qtyRejected: line.qty_rejected,
    moistureReceivedPercent: null,
    unitId: BigInt(line.unit_id),
    unitPrice: line.unit_price,
    unitCost: line.unit_cost,
    malUnitPrice: line.mal_unit_price ?? null,
    warehouseId: toBigIntOrNull(warehouseId),
    warehouseLocationId: toBigIntOrNull(warehouseLocationId),
    classificationStatus: line.classification_status,
    subtotal: line.qty_ordered * line.unit_price,
    notes: line.notes || null
  };
}

const detailInclude = {
  agent: true,
  profitShareScheme: true,
  courier: true,
  receivedByEmployee: true,
  analysis: {
    include: {
    costEntries: true
    }
  },
  lines: {
    include: {
      grade: true,
      unit: true,
      warehouse: true,
      warehouseLocation: true
    }
  }
} as const;

export async function GET(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 });
  const purchase = await prisma.purchase.findUnique({
    where: { id: parsedId },
    include: detailInclude
  });
  if (!purchase) return NextResponse.json({ message: "Purchase not found" }, { status: 404 });
  return NextResponse.json({ data: serializePurchaseDetail(purchase) });
}

export async function PUT(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 });
  let parsed: { payload: PurchaseInput; costProofFiles: Array<{ index: number; file: File }> };
  try {
    parsed = await parsePurchaseRequestPayload(request);
  } catch (error) {
    if (isPurchasePayloadValidationError(error)) {
      return NextResponse.json(
        { message: error.message, errors: error.errors },
        { status: 400 }
      );
    }
    if (error instanceof Error) {
      return NextResponse.json({ message: error.message }, { status: 400 });
    }
    return NextResponse.json({ message: "Request tidak valid" }, { status: 400 });
  }
  const { payload, costProofFiles } = parsed;
  const finalWeight = sumLineQty(payload.lines);
  const costEntries = await buildPurchaseCostEntries(payload, costProofFiles);
  const missingGradeLine = payload.lines.some((line) => !line.grade_id?.trim());

  try {
    const existing = await prisma.purchase.findUnique({
      where: { id: parsedId },
      include: {
        lines: true,
        _count: {
          select: {
            receipts: true,
            lots: true
          }
        }
      }
    });
    if (!existing) {
      return NextResponse.json({ message: "Purchase not found" }, { status: 404 });
    }
    if (existing._count.receipts > 0 || existing._count.lots > 0) {
      return NextResponse.json(
        {
          message:
            "Purchase sudah memiliki receipt/lot, sehingga baris pembelian tidak bisa diubah dari form purchase. Gunakan koreksi stok atau buat purchase baru untuk perubahan setelah barang diterima."
        },
        { status: 409 }
      );
    }

    const purchaseDate = toDateOrThrow(`${payload.purchase_date}T00:00:00.000Z`, "Tanggal pembelian");
    const receivedAt = toDateOrThrow(payload.received_at, "Waktu diterima");
    let defaultGradeId = "";
    if (missingGradeLine) {
      const defaultGrade = await prisma.grade.findFirst({
        select: { id: true },
        orderBy: { id: "asc" }
      });
      defaultGradeId = defaultGrade ? defaultGrade.id.toString() : "";
      if (!defaultGradeId) {
        return NextResponse.json(
          { message: "Grade tidak tersedia. Tambahkan grade di master data atau isi grade per line." },
          { status: 400 }
        );
      }
    }

    const purchase = await prisma.$transaction(async (tx) => {
      await tx.purchaseLine.deleteMany({ where: { purchaseId: parsedId } });
      return tx.purchase.update({
        where: { id: parsedId },
        data: {
          purchaseDate,
          agentId: toBigIntOrNull(payload.agent_id),
          profitShareSchemeId: toBigIntOrNull(payload.profit_share_scheme_id),
          courierId: toBigIntOrNull(payload.courier_id),
          receivedByEmployeeId: toBigIntOrNull(payload.received_by_employee_id),
          receivedAt,
          moistureBuyPercent: payload.moisture_buy_percent ?? null,
          moistureReceivedPercent: payload.moisture_received_percent ?? null,
          aboveAverageRatioPercent: payload.above_average_ratio_percent ?? null,
          mkSharePercent: payload.mk_share_percent ?? null,
          nonMkSharePercent: payload.non_mk_share_percent ?? null,
          shippingCost: null,
          incomingOperationalCost: null,
          afterArrivalOperationalCost: null,
          notes: payload.notes || null,
          analysis: {
            upsert: {
              create: {
                status: "DRAFT",
                weightBuy: payload.berat_beli ?? finalWeight,
                weightReceived: payload.berat_masuk ?? finalWeight,
                weightFinal: finalWeight,
                moistureBuyPercent: payload.moisture_buy_percent ?? null,
                moistureReceivedPercent: payload.moisture_received_percent ?? null,
                moistureFinalPercent: payload.moisture_final_percent ?? null,
                aboveAverageRatioPercent: payload.above_average_ratio_percent ?? null,
                averagePrice: payload.average_price ?? null,
                modalBeli: payload.modal_beli ?? null,
                modalMasuk: payload.modal_masuk ?? null,
                modalJual: payload.modal_jual ?? null,
                modalBarang: payload.modal_barang ?? null,
                totalModalBeli: payload.total_modal_beli ?? null,
                totalModalMal: payload.total_modal_mal ?? null,
                marketReferencePrice: payload.market_reference_price ?? null,
                costEntries: {
                  create: costEntries
                }
              },
              update: {
                weightBuy: payload.berat_beli ?? finalWeight,
                weightReceived: payload.berat_masuk ?? finalWeight,
                weightFinal: finalWeight,
                moistureBuyPercent: payload.moisture_buy_percent ?? null,
                moistureReceivedPercent: payload.moisture_received_percent ?? null,
                moistureFinalPercent: payload.moisture_final_percent ?? null,
                aboveAverageRatioPercent: payload.above_average_ratio_percent ?? null,
                averagePrice: payload.average_price ?? null,
                modalBeli: payload.modal_beli ?? null,
                modalMasuk: payload.modal_masuk ?? null,
                modalJual: payload.modal_jual ?? null,
                modalBarang: payload.modal_barang ?? null,
                totalModalBeli: payload.total_modal_beli ?? null,
                totalModalMal: payload.total_modal_mal ?? null,
                marketReferencePrice: payload.market_reference_price ?? null,
                costEntries: {
                  deleteMany: {},
                  create: costEntries
                }
              }
            }
          },
          lines: {
            create: payload.lines.map((line) =>
              buildLineCreateInput(
                line,
                payload.warehouse_id || "",
                payload.warehouse_location_id || null,
                missingGradeLine
                  ? defaultGradeId
                  : line.grade_id || defaultGradeId
              )
            )
          }
        },
        include: detailInclude
      });
    });

    await createAuditTrailSafe({
      userId: auth.user.id,
      action: "PURCHASE_UPDATED",
      entityType: "PURCHASE",
      entityId: purchase.id,
      method: request.method,
      pathname: new URL(request.url).pathname,
      statusCode: 200,
      summary: `Purchase ${purchase.purchaseNo} diubah`,
      metadata: buildAuditChangeMetadata(
        {
          purchase_date: existing.purchaseDate,
          notes: existing.notes,
          status: existing.status,
          line_count: existing.lines.length,
          total_qty: existing.lines.reduce((sum, line) => sum + line.qtyOrdered.toNumber(), 0),
          grand_total: existing.lines.reduce((sum, line) => sum + line.subtotal.toNumber(), 0)
        },
        {
          purchase_date: purchase.purchaseDate,
          notes: purchase.notes,
          status: purchase.status,
          line_count: purchase.lines.length,
          total_qty: purchase.lines.reduce((sum, line) => sum + line.qtyOrdered.toNumber(), 0),
          grand_total: purchase.lines.reduce((sum, line) => sum + line.subtotal.toNumber(), 0)
        }
      )
    });

    return NextResponse.json({ data: serializePurchaseDetail(purchase) });
  } catch (error) {
    if (error instanceof Prisma.PrismaClientKnownRequestError && error.code === "P2003") {
      return NextResponse.json(
        {
          message:
            "Purchase masih terkait receipt/lot, sehingga baris pembelian tidak bisa diubah dari form purchase."
        },
        { status: 409 }
      );
    }
    if (error instanceof Prisma.PrismaClientKnownRequestError || error instanceof Prisma.PrismaClientValidationError) {
      return NextResponse.json({ message: error.message }, { status: 400 });
    }
    throw error;
  }
}

export async function DELETE(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 existing = await prisma.purchase.findUnique({
      where: { id: parsedId },
      include: {
        _count: {
          select: {
            receipts: true,
            lots: true
          }
        }
      }
    });

    if (!existing) {
      return NextResponse.json({ message: "Purchase not found" }, { status: 404 });
    }

    if (!["DRAFT", "CANCELLED"].includes(existing.status)) {
      return NextResponse.json(
        { message: "Hanya draft atau cancelled yang bisa dihapus" },
        { status: 409 }
      );
    }

    if (existing._count.receipts > 0 || existing._count.lots > 0) {
      return NextResponse.json(
        { message: "Tidak dapat menghapus karena sudah memiliki relasi transaksi." },
        { status: 409 }
      );
    }

    await prisma.purchase.delete({ where: { id: parsedId } });

    await createAuditTrailSafe({
      userId: auth.user.id,
      action: "PURCHASE_DELETED",
      entityType: "PURCHASE",
      entityId: parsedId,
      method: request.method,
      pathname: new URL(request.url).pathname,
      statusCode: 200,
      summary: `Purchase ${existing.purchaseNo} dihapus`
    });

    return NextResponse.json({ success: true });
  } catch (error) {
    if (error instanceof Prisma.PrismaClientKnownRequestError && error.code === "P2025") {
      return NextResponse.json({ message: "Purchase not found" }, { status: 404 });
    }
    if (error instanceof Prisma.PrismaClientKnownRequestError && error.code === "P2003") {
      return NextResponse.json(
        { message: "Tidak dapat menghapus karena masih terkait transaksi lain." },
        { status: 409 }
      );
    }
    throw error;
  }
}
