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

import { generateWashingNo } from "@/features/washing/lib/generate-washing-no";
import {
  isWashingPayloadValidationError,
  parseWashingRequestPayload
} from "@/features/washing/lib/parse-washing-request";
import { serializeWashing } from "@/features/washing/lib/serialize-washing";
import {
  storeWashingReceipt,
  validateWashingReceiptFile
} from "@/features/washing/lib/store-washing-receipt";
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 washingInclude = {
  lot: {
    include: {
      purchase: {
        select: {
          agent: {
            select: {
              name: true
            }
          }
        }
      },
      grade: { select: { name: true } },
      unit: { select: { code: true } },
      warehouse: { select: { name: true } },
      warehouseLocation: { select: { name: true } }
    }
  },
  washingPlace: {
    select: {
      id: true,
      name: true
    }
  }
} as const;

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

  const items = await prisma.lotWashing.findMany({
    include: washingInclude,
    orderBy: [{ startedAt: "desc" }, { createdAt: "desc" }]
  });

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

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

  let parsedRequest;
  try {
    parsedRequest = await parseWashingRequestPayload(request);
  } catch (error) {
    if (isWashingPayloadValidationError(error)) {
      return NextResponse.json(
        {
          message: error.message,
          errors: error.errors
        },
        { status: 422 }
      );
    }
    return NextResponse.json({ message: "Payload washing tidak valid" }, { status: 400 });
  }

  const lotId = parseBigInt(parsedRequest.payload.lot_id);
  const washingPlaceId = parseBigInt(parsedRequest.payload.washing_place_id);
  if (lotId === null || washingPlaceId === null) {
    return NextResponse.json({ message: "Referensi lot atau tempat cuci tidak valid" }, { status: 400 });
  }

  const [lot, washingPlace, existingInProgress] = await Promise.all([
    prisma.inventoryLot.findUnique({
      where: { id: lotId },
      include: {
        grade: true,
        warehouse: true,
        warehouseLocation: true
      }
    }),
    prisma.washingPlace.findUnique({ where: { id: washingPlaceId } }),
    prisma.lotWashing.findFirst({
      where: {
        lotId,
        status: "IN_PROGRESS"
      }
    })
  ]);

  if (!lot) {
    return NextResponse.json({ message: "Lot tidak ditemukan" }, { status: 404 });
  }
  if (!washingPlace) {
    return NextResponse.json({ message: "Tempat cuci tidak ditemukan" }, { status: 404 });
  }
  if (existingInProgress) {
    return NextResponse.json(
      { message: "Lot ini masih memiliki proses washing yang belum selesai" },
      { status: 409 }
    );
  }
  if (lot.status !== "ACTIVE" || lot.availableQty.toNumber() <= 0) {
    return NextResponse.json(
      { message: "Lot harus aktif dan memiliki stok tersedia untuk dikirim ke washing" },
      { status: 422 }
    );
  }
  if (lot.reservedQty.toNumber() > 0) {
    return NextResponse.json(
      { message: "Lot dengan qty reserved tidak bisa dikirim ke washing" },
      { status: 422 }
    );
  }

  let receiptFileUrl: string | null = null;
  if (parsedRequest.receiptFile) {
    const validationError = validateWashingReceiptFile(parsedRequest.receiptFile);
    if (validationError) {
      return NextResponse.json({ message: validationError }, { status: 422 });
    }
    receiptFileUrl = await storeWashingReceipt(parsedRequest.receiptFile);
  }

  const startedAt = new Date();
  const expectedDoneAt = new Date(
    startedAt.getTime() + parsedRequest.payload.duration_hours * 60 * 60 * 1000
  );
  const washingNo = await generateWashingNo(startedAt);

  const created = await prisma.lotWashing.create({
    data: {
      washingNo,
      lotId,
      washingPlaceId,
      washingCost: new Prisma.Decimal(parsedRequest.payload.washing_cost),
      durationHours: parsedRequest.payload.duration_hours,
      receiptFileUrl,
      startedAt,
      expectedDoneAt,
      beforeQty: lot.availableQty,
      beforeGradeName: lot.grade?.name ?? null,
      beforeWarehouseName: lot.warehouse.name,
      beforeLocationName: lot.warehouseLocation?.name ?? null,
      createdById: BigInt(auth.user.id)
    },
    include: washingInclude
  });

  await createAuditTrailSafe({
    userId: auth.user.id,
    action: "WASHING_CREATED",
    entityType: "LOT_WASHING",
    entityId: created.id,
    method: request.method,
    pathname: new URL(request.url).pathname,
    statusCode: 201,
    summary: `Washing ${created.washingNo} dibuat untuk lot ${created.lot.lotCode}`,
    metadata: {
      washing_no: created.washingNo,
      lot_code: created.lot.lotCode,
      washing_place: created.washingPlace.name,
      before_qty: created.beforeQty.toNumber()
    }
  });

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