import { PrismaClientKnownRequestError } from "@prisma/client/runtime/library";
import { NextResponse } from "next/server";

import { serializeBank } from "@/features/banks/lib/serialize-bank";
import { bankInputSchema } from "@/features/banks/schemas/bank.schema";
import { createAuditTrailSafe } from "@/lib/audit-trail";
import { buildAuditChangeMetadata } from "@/lib/audit-trail-diff";
import { prisma } from "@/lib/prisma";
import { requireApiAccess } from "@/lib/authorization";

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

function parseId(rawId: string) {
  try {
    return BigInt(rawId);
  } catch {
    return null;
  }
}

async function countBankUsage(bankName: string) {
  const customerCount = await prisma.buyer.count({ where: { bankName } });

  return customerCount;
}

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 bank = await prisma.bank.findUnique({ where: { id: parsedId } });
  if (!bank) return NextResponse.json({ message: "Bank not found" }, { status: 404 });

  return NextResponse.json({ data: serializeBank(bank) });
}

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 });

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

  try {
    const existing = await prisma.bank.findUnique({ where: { id: parsedId } });
    if (!existing) return NextResponse.json({ message: "Bank not found" }, { status: 404 });

    const usageCount = await countBankUsage(existing.name);
    if (usageCount > 0 && existing.name !== parsed.data.name) {
      return NextResponse.json(
        { message: "Nama bank sedang dipakai di buyer dan tidak bisa diubah." },
        { status: 409 }
      );
    }

    const bank = await prisma.bank.update({
      where: { id: parsedId },
      data: {
        code: parsed.data.code,
        name: parsed.data.name,
        address: parsed.data.address || null,
        status: parsed.data.status
      }
    });

    await createAuditTrailSafe({
      userId: auth.user.id,
      action: "BANK_UPDATED",
      entityType: "BANK",
      entityId: bank.id,
      method: request.method,
      pathname: new URL(request.url).pathname,
      statusCode: 200,
      summary: `Bank ${bank.code} diubah`,
      metadata: buildAuditChangeMetadata(
        {
          code: existing.code,
          name: existing.name,
          address: existing.address,
          status: existing.status
        },
        {
          code: bank.code,
          name: bank.name,
          address: bank.address,
          status: bank.status
        }
      )
    });

    return NextResponse.json({ data: serializeBank(bank) });
  } catch (error) {
    if (error instanceof PrismaClientKnownRequestError && error.code === "P2025") {
      return NextResponse.json({ message: "Bank not found" }, { status: 404 });
    }
    if (error instanceof PrismaClientKnownRequestError && error.code === "P2002") {
      return NextResponse.json(
        {
          message: "Validasi gagal",
          errors: {
            code: ["Kode atau nama bank sudah dipakai"]
          }
        },
        { status: 409 }
      );
    }
    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.bank.findUnique({ where: { id: parsedId } });
    if (!existing) return NextResponse.json({ message: "Bank not found" }, { status: 404 });

    const usageCount = await countBankUsage(existing.name);
    if (usageCount > 0) {
      return NextResponse.json(
        { message: "Bank sedang dipakai di buyer dan tidak bisa dihapus." },
        { status: 409 }
      );
    }

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

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

    return NextResponse.json({ success: true });
  } catch (error) {
    if (error instanceof PrismaClientKnownRequestError && error.code === "P2025") {
      return NextResponse.json({ message: "Bank not found" }, { status: 404 });
    }
    throw error;
  }
}
