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

import { generateConsignmentNo } from "@/features/consignments/lib/generate-consignment-no";
import {
  serializeConsignmentDetail,
  serializeConsignmentListItem
} from "@/features/consignments/lib/serialize-consignment";
import { consignmentCreateSchema } from "@/features/consignments/schemas/consignment.schema";
import { createAuditTrailSafe } from "@/lib/audit-trail";
import { requireApiAccess } from "@/lib/authorization";
import { prisma } from "@/lib/prisma";

function parseBigInt(value: string) {
  try {
    return BigInt(value);
  } catch {
    return null;
  }
}

const consignmentInclude = {
  sales: true,
  buyer: true,
  lines: true
} as const;

const consignmentDetailInclude = {
  sales: true,
  buyer: true,
  lines: {
    include: {
      lot: {
        include: {
          purchase: {
            select: {
              agent: {
                select: {
                  name: true
                }
              },
              profitShareScheme: {
                select: {
                  shareAgent: true
                }
              }
            }
          },
          grade: true,
          unit: true,
          warehouse: true,
          warehouseLocation: true
        }
      }
    }
  }
} as const;

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

  const consignments = await prisma.consignment.findMany({
    include: consignmentInclude,
    orderBy: [{ createdAt: "desc" }]
  });

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

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

  const parsed = consignmentCreateSchema.safeParse(await request.json());
  if (!parsed.success) {
    return NextResponse.json(
      {
        message: "Validasi gagal",
        errors: parsed.error.flatten().fieldErrors
      },
      { status: 422 }
    );
  }

  const salesId = parseBigInt(parsed.data.sales_id);
  const buyerId = parseBigInt(parsed.data.buyer_id);
  const consignmentDate = new Date(`${parsed.data.consignment_date}T00:00:00.000Z`);

  if (
    salesId === null ||
    buyerId === null ||
    Number.isNaN(consignmentDate.getTime())
  ) {
    return NextResponse.json({ message: "Invalid reference id or date" }, { status: 400 });
  }

  const lotIds = parsed.data.lines.map((line) => parseBigInt(line.lot_id));
  if (lotIds.some((id) => id === null)) {
    return NextResponse.json({ message: "Lot tidak valid" }, { status: 400 });
  }

  const [sales, buyer, lots] = await Promise.all([
    prisma.sales.findUnique({ where: { id: salesId } }),
    prisma.buyer.findUnique({ where: { id: buyerId } }),
    prisma.inventoryLot.findMany({
      where: {
        id: {
          in: lotIds as bigint[]
        }
      },
      include: {
        purchase: {
          select: {
            agent: {
              select: {
                name: true
              }
            },
            profitShareScheme: {
              select: {
                shareAgent: true
              }
            }
          }
        },
        purchaseLine: {
          select: {
            malUnitPrice: true
          }
        }
      }
    })
  ]);

  if (!sales) {
    return NextResponse.json({ message: "Sales tidak ditemukan" }, { status: 404 });
  }
  if (!buyer) {
    return NextResponse.json({ message: "Buyer tidak ditemukan" }, { status: 404 });
  }

  const lotMap = new Map(lots.map((lot) => [lot.id.toString(), lot]));
  for (const line of parsed.data.lines) {
    const lot = lotMap.get(line.lot_id);
    if (!lot) {
      return NextResponse.json({ message: `Lot ${line.lot_id} tidak ditemukan` }, { status: 404 });
    }
    if (lot.status !== "ACTIVE") {
      return NextResponse.json(
        { message: `Lot ${lot.lotCode} tidak aktif untuk dititipkan` },
        { status: 422 }
      );
    }
    if (line.qty_consigned > lot.availableQty.toNumber()) {
      return NextResponse.json(
        {
          message: `Qty titip untuk ${lot.lotCode} melebihi stok tersedia ${lot.availableQty.toNumber()}`
        },
        { status: 422 }
      );
    }
  }

  const consignmentNo = await generateConsignmentNo(consignmentDate);

  const created = await prisma.$transaction(async (tx) => {
    const consignment = await tx.consignment.create({
      data: {
        consignmentNo,
        consignmentDate,
        salesId,
        buyerId,
        notes: parsed.data.notes ?? null,
        createdById: BigInt(auth.user.id),
        lines: {
          create: parsed.data.lines.map((line) => {
            const lot = lotMap.get(line.lot_id)!;
            return {
              lotId: lot.id,
              qtyConsigned: new Prisma.Decimal(line.qty_consigned),
              availableQtySnapshot: lot.availableQty,
              malUnitPriceSnapshot: lot.purchaseLine?.malUnitPrice ?? null,
              agentNameSnapshot: lot.purchase?.agent?.name ?? null,
              agentSharePercent: lot.purchase?.profitShareScheme?.shareAgent ?? null,
              status: "OPEN",
              notes: line.notes ?? null
            };
          })
        }
      }
    });

    for (const line of parsed.data.lines) {
      const lot = lotMap.get(line.lot_id)!;
      const nextAvailable = Number((lot.availableQty.toNumber() - line.qty_consigned).toFixed(3));
      await tx.inventoryLot.update({
        where: { id: lot.id },
        data: {
          availableQty: new Prisma.Decimal(nextAvailable),
          status: nextAvailable <= 0 ? "DEPLETED" : "ACTIVE"
        }
      });
    }

    return tx.consignment.findUniqueOrThrow({
      where: { id: consignment.id },
      include: consignmentDetailInclude
    });
  });

  await createAuditTrailSafe({
    userId: auth.user.id,
    action: "CONSIGNMENT_CREATED",
    entityType: "CONSIGNMENT",
    entityId: created.id,
    method: request.method,
    pathname: new URL(request.url).pathname,
    statusCode: 201,
    summary: `Titip jual ${created.consignmentNo} dibuat`,
    metadata: {
      consignment_no: created.consignmentNo,
      sales_name: created.sales.name,
      buyer_name: created.buyer.name,
      line_count: created.lines.length
    }
  });

  return NextResponse.json(
    {
      data: serializeConsignmentDetail(created)
    },
    { status: 201 }
  );
}
