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

import { serializeSales } from "@/features/sales/lib/serialize-sales";
import { salesInputSchema } from "@/features/sales/schemas/sales.schema";
import { createAuditTrailSafe } from "@/lib/audit-trail";
import { buildAuditChangeMetadata } from "@/lib/audit-trail-diff";
import { requireApiAccess } from "@/lib/authorization";
import { resolveMasterCode } from "@/lib/master-code";
import { prisma } from "@/lib/prisma";

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

const parseId = (id: string) => {
  try {
    return BigInt(id);
  } catch {
    return null;
  }
};

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 sales = await prisma.sales.findUnique({
    where: { id: parsedId },
    include: {
      bankAccounts: { include: { bank: true } }
    }
  });

  if (!sales) return NextResponse.json({ message: "Sales not found" }, { status: 404 });
  return NextResponse.json({ data: serializeSales(sales) });
}

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 = salesInputSchema.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.sales.findUnique({
      where: { id: parsedId },
      include: {
        bankAccounts: { include: { bank: true } }
      }
    });

    if (!existing) return NextResponse.json({ message: "Sales not found" }, { status: 404 });

    const bankIds = parsed.data.bank_accounts.map((account) => BigInt(account.bank_id));

    const banks = await prisma.bank.findMany({
      where: { id: { in: bankIds }, status: "ACTIVE" }
    });

    if (banks.length !== bankIds.length) {
      return NextResponse.json(
        { message: "Validasi gagal", errors: { bank_accounts: ["Semua rekening harus memilih bank aktif dari master"] } },
        { status: 400 }
      );
    }

    const resolvedCode = await resolveMasterCode({
      role: auth.user.role,
      prefix: "SEL",
      requestedCode: parsed.data.code,
      existingCode: existing.code,
      countExisting: () => prisma.sales.count({ where: { code: { startsWith: "SEL" } } }),
      exists: async (code) => (await prisma.sales.count({ where: { code, id: { not: parsedId } } })) > 0
    });

    if (!resolvedCode.ok) {
      return NextResponse.json(
        { message: "Validasi gagal", errors: { code: [resolvedCode.message] } },
        { status: 400 }
      );
    }

    const sales = await prisma.sales.update({
      where: { id: parsedId },
      data: {
        code: resolvedCode.code,
        name: parsed.data.name,
        identityType: parsed.data.identity_type,
        identityNumber: parsed.data.identity_number,
        mobilePhone: parsed.data.mobile_phone || null,
        email: parsed.data.email || null,
        address: parsed.data.address || null,
        notes: parsed.data.notes || null,
        joinDate: new Date(parsed.data.join_date),
        bankAccounts: {
          deleteMany: {},
          create: parsed.data.bank_accounts.map((account) => ({
            bankId: BigInt(account.bank_id),
            accountNumber: account.account_number
          }))
        }
      },
      include: {
        bankAccounts: { include: { bank: true } }
      }
    });

    await createAuditTrailSafe({
      userId: auth.user.id,
      action: "SELLER_UPDATED",
      entityType: "SELLER",
      entityId: sales.id,
      method: request.method,
      pathname: new URL(request.url).pathname,
      statusCode: 200,
      summary: `Sales ${sales.code} diubah`,
      metadata: buildAuditChangeMetadata(
        {
          code: existing.code,
          name: existing.name,
          identity_type: existing.identityType,
          identity_number: existing.identityNumber,
          mobile_phone: existing.mobilePhone,
          email: existing.email,
          address: existing.address,
          notes: existing.notes,
          join_date: existing.joinDate.toISOString().slice(0, 10),
          bank_accounts: existing.bankAccounts.map((account) => ({
            bank_id: account.bank.id.toString(),
            account_number: account.accountNumber
          }))
        },
        {
          code: sales.code,
          name: sales.name,
          identity_type: sales.identityType,
          identity_number: sales.identityNumber,
          mobile_phone: sales.mobilePhone,
          email: sales.email,
          address: sales.address,
          notes: sales.notes,
          join_date: sales.joinDate.toISOString().slice(0, 10),
          bank_accounts: sales.bankAccounts.map((account) => ({
            bank_id: account.bank.id.toString(),
            account_number: account.accountNumber
          }))
        }
      )
    });

    return NextResponse.json({ data: serializeSales(sales) });
  } catch (error) {
    if (error instanceof PrismaClientKnownRequestError && error.code === "P2025") {
      return NextResponse.json({ message: "Sales not found" }, { status: 404 });
    }
    if (error instanceof PrismaClientKnownRequestError && error.code === "P2002") {
      return NextResponse.json(
        { message: "Validasi gagal", errors: { identity_number: ["Kode sales atau identitas 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.sales.findUnique({ where: { id: parsedId } });
    await prisma.sales.delete({ where: { id: parsedId } });

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

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