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

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;

type RouteContext = {
  params: Promise<{
    id: string;
  }>;
};

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

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

  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 washingPlaceId = parseBigInt(parsedRequest.payload.washing_place_id);
  const lotId = parseBigInt(parsedRequest.payload.lot_id);
  if (washingPlaceId === null || lotId === null) {
    return NextResponse.json({ message: "Referensi washing tidak valid" }, { status: 400 });
  }

  const existing = await prisma.lotWashing.findUnique({
    where: { id: washingId }
  });
  if (!existing) {
    return NextResponse.json({ message: "Data washing tidak ditemukan" }, { status: 404 });
  }
  if (existing.status === "DONE") {
    return NextResponse.json(
      { message: "Data washing yang sudah selesai tidak bisa diubah" },
      { status: 422 }
    );
  }
  if (existing.lotId !== lotId) {
    return NextResponse.json({ message: "Lot washing tidak bisa diganti" }, { status: 422 });
  }

  const washingPlace = await prisma.washingPlace.findUnique({ where: { id: washingPlaceId } });
  if (!washingPlace) {
    return NextResponse.json({ message: "Tempat cuci tidak ditemukan" }, { status: 404 });
  }

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

  const updated = await prisma.lotWashing.update({
    where: { id: washingId },
    data: {
      washingPlaceId,
      washingCost: new Prisma.Decimal(parsedRequest.payload.washing_cost),
      durationHours: parsedRequest.payload.duration_hours,
      receiptFileUrl,
      expectedDoneAt: new Date(
        existing.startedAt.getTime() + parsedRequest.payload.duration_hours * 60 * 60 * 1000
      )
    },
    include: washingInclude
  });

  await createAuditTrailSafe({
    userId: auth.user.id,
    action: "WASHING_UPDATED",
    entityType: "LOT_WASHING",
    entityId: updated.id,
    method: request.method,
    pathname: new URL(request.url).pathname,
    statusCode: 200,
    summary: `Washing ${updated.washingNo} diubah`,
    metadata: {
      washing_no: updated.washingNo,
      washing_place: updated.washingPlace.name
    }
  });

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