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

import { ensureSystemUser } from "@/features/purchases/lib/system-user";
import {
  serializePurchaseDetail,
  serializePurchaseListItem
} from "@/features/purchases/lib/serialize-purchase";
import { generatePurchaseNo } from "@/features/purchases/lib/generate-purchase-no";
import { buildPurchaseCostEntries } from "@/features/purchases/lib/build-purchase-cost-entries";
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 { prisma } from "@/lib/prisma";
import { requireApiAccess } from "@/lib/authorization";

function toBigIntOrNull(raw: string | null | undefined): bigint | 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;
}

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

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

function buildLineCreateInput(
  line: PurchaseLinePayload,
  defaultWarehouseId: string,
  defaultWarehouseLocationId: string | null,
  fallbackGradeId: string
) {
  const gradeId = line.grade_id || fallbackGradeId;
  const warehouseId = line.warehouse_id || defaultWarehouseId;
  const warehouseLocationId = line.warehouse_location_id || defaultWarehouseLocationId;
  return {
    gradeId: gradeId ? BigInt(gradeId) : null,
    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,
    subtotal: line.qty_ordered * line.unit_price,
    classificationStatus: line.classification_status,
    warehouseId: toBigIntOrNull(warehouseId),
    warehouseLocationId: toBigIntOrNull(warehouseLocationId),
    notes: line.notes || null
  };
}

export async function GET(request: Request) {
  const auth = requireApiAccess(request);
  if (!auth.ok) return auth.response;
  const purchaseType = new URL(request.url).searchParams.get("purchase_type")?.trim().toUpperCase() || "REGULAR";
  const data = await prisma.purchase.findMany({
    where: {
      purchaseType
    },
    include: {
      agent: {
        select: {
          id: true,
          name: true
        }
      },
      lines: {
        select: {
          subtotal: true
        }
      },
      _count: {
        select: {
          receipts: true,
          lots: true
        }
      }
    },
    orderBy: [{ createdAt: "desc" }]
  });

  return NextResponse.json({
    data: data.map(serializePurchaseListItem)
  });
}

export async function POST(request: Request) {
  const auth = requireApiAccess(request);
  if (!auth.ok) return auth.response;
  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 systemUser = await ensureSystemUser();
  const { payload, costProofFiles } = parsed;
  const finalWeight = sumLineQty(payload.lines);
  const purchaseDate = toDateOrThrow(`${payload.purchase_date}T00:00:00.000Z`, "Tanggal pembelian");
  const receivedAt = toDateOrThrow(payload.received_at, "Waktu diterima");
  const purchaseNo = await generatePurchaseNo(purchaseDate);
  const costEntries = await buildPurchaseCostEntries(payload, costProofFiles);
  const missingGradeLine = payload.lines.some((line) => !line.grade_id?.trim());
  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 }
      );
    }
  }

  try {
    const purchase = await prisma.purchase.create({
      data: {
        purchaseNo,
        purchaseType: "REGULAR",
        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,
        status: "DRAFT",
        notes: payload.notes || null,
        createdById: systemUser.id,
        lines: {
          create: payload.lines.map((line) =>
            buildLineCreateInput(
              line,
              payload.warehouse_id || "",
              payload.warehouse_location_id || null,
              missingGradeLine
                ? defaultGradeId
                : line.grade_id || defaultGradeId
            )
          )
        },
        analysis: {
          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
            }
          }
        }
      },
      include: {
        agent: true,
        profitShareScheme: true,
        courier: true,
        receivedByEmployee: true,
        lines: {
          include: {
            grade: true,
            unit: true,
            warehouse: true,
            warehouseLocation: true
          }
        },
        analysis: {
          include: {
            costEntries: true
          }
        }
      }
    });

    await createAuditTrailSafe({
      userId: auth.user.id,
      action: "PURCHASE_CREATED",
      entityType: "PURCHASE",
      entityId: purchase.id,
      method: request.method,
      pathname: new URL(request.url).pathname,
      statusCode: 201,
      summary: `Purchase ${purchase.purchaseNo} dibuat`,
      metadata: {
        purchase_no: purchase.purchaseNo,
        line_count: purchase.lines.length
      }
    });

    return NextResponse.json({ data: serializePurchaseDetail(purchase) }, { status: 201 });
  } catch (error) {
    if (error instanceof Prisma.PrismaClientKnownRequestError || error instanceof Prisma.PrismaClientValidationError) {
      return NextResponse.json({ message: error.message }, { status: 400 });
    }
    throw error;
  }
}
