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

import { serializeGrade } from "@/features/grades/lib/serialize-grade";
import { gradeInputSchema, type GradeInput } from "@/features/grades/schemas/grade.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;
  }
};

function getGradePrefix(isMangkok: boolean) {
  return isMangkok ? "MGK" : "GRD";
}

function hasOverlap(items: GradeInput["buy_price_standards"]) {
  const normalized = items.map((item) => ({
    start: new Date(item.start_date).getTime(),
    end: item.end_date ? new Date(item.end_date).getTime() : Number.POSITIVE_INFINITY
  }));

  for (let i = 0; i < normalized.length; i += 1) {
    for (let j = i + 1; j < normalized.length; j += 1) {
      const a = normalized[i];
      const b = normalized[j];
      if (a.start <= b.end && b.start <= a.end) {
        return true;
      }
    }
  }

  return false;
}

function validatePriceStandards(parsed: GradeInput) {
  if (hasOverlap(parsed.buy_price_standards)) {
    return { buy_price_standards: ["Periode standar harga beli tidak boleh overlap"] };
  }

  if (hasOverlap(parsed.sell_price_standards)) {
    return { sell_price_standards: ["Periode standar harga jual tidak boleh overlap"] };
  }

  return null;
}

function buildCodeResolutionParams(parsed: GradeInput, existing: { code: string; isMangkok: boolean }, parsedId: bigint) {
  const prefix = getGradePrefix(parsed.is_mangkok);
  const existingMatchesPrefix = existing.code.startsWith(prefix);
  const requestedCode = parsed.code?.trim();

  return {
    prefix,
    requestedCode: requestedCode || undefined,
    existingCode: requestedCode ? existing.code : existingMatchesPrefix ? existing.code : null,
    countExisting: () => prisma.grade.count({ where: { code: { startsWith: prefix } } }),
    exists: async (code: string) =>
      (await prisma.grade.count({ where: { code, id: { not: parsedId } } })) > 0
  };
}

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 item = await prisma.grade.findUnique({
    where: { id: parsedId },
    include: {
      buyPriceStandards: { orderBy: [{ startDate: "asc" }, { id: "asc" }] },
      sellPriceStandards: { orderBy: [{ startDate: "asc" }, { id: "asc" }] }
    }
  });

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

  return NextResponse.json({ data: serializeGrade(item) });
}

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 = gradeInputSchema.safeParse(await request.json());
  if (!parsed.success) {
    return NextResponse.json(
      { message: "Validasi gagal", errors: parsed.error.flatten().fieldErrors },
      { status: 400 }
    );
  }

  const priceValidationError = validatePriceStandards(parsed.data);
  if (priceValidationError) {
    return NextResponse.json({ message: "Validasi gagal", errors: priceValidationError }, { status: 400 });
  }

  try {
    const existing = await prisma.grade.findUnique({
      where: { id: parsedId },
      include: {
        buyPriceStandards: { orderBy: [{ startDate: "asc" }, { id: "asc" }] },
        sellPriceStandards: { orderBy: [{ startDate: "asc" }, { id: "asc" }] }
      }
    });
    if (!existing) return NextResponse.json({ message: "Grade not found" }, { status: 404 });

    const resolution = buildCodeResolutionParams(parsed.data, existing, parsedId);
    const resolvedCode = await resolveMasterCode({
      role: auth.user.role,
      prefix: resolution.prefix,
      requestedCode: resolution.requestedCode,
      existingCode: resolution.existingCode,
      countExisting: resolution.countExisting,
      exists: resolution.exists
    });

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

    const item = await prisma.grade.update({
      where: { id: parsedId },
      data: {
        code: resolvedCode.code,
        isMangkok: parsed.data.is_mangkok,
        name: parsed.data.name,
        description: parsed.data.description || null,
        status: parsed.data.status,
        buyPriceStandards: {
          deleteMany: {},
          create: parsed.data.buy_price_standards.map((standard) => ({
            startDate: new Date(standard.start_date),
            endDate: standard.end_date ? new Date(standard.end_date) : null,
            minPrice: standard.min_price,
            maxPrice: standard.max_price,
            notes: standard.notes || null
          }))
        },
        sellPriceStandards: {
          deleteMany: {},
          create: parsed.data.sell_price_standards.map((standard) => ({
            startDate: new Date(standard.start_date),
            endDate: standard.end_date ? new Date(standard.end_date) : null,
            minPrice: standard.min_price,
            maxPrice: standard.max_price,
            notes: standard.notes || null
          }))
        }
      },
      include: {
        buyPriceStandards: { orderBy: [{ startDate: "asc" }, { id: "asc" }] },
        sellPriceStandards: { orderBy: [{ startDate: "asc" }, { id: "asc" }] }
      }
    });

    await createAuditTrailSafe({
      userId: auth.user.id,
      action: "GRADE_UPDATED",
      entityType: "GRADE",
      entityId: item.id,
      method: request.method,
      pathname: new URL(request.url).pathname,
      statusCode: 200,
      summary: `Grade ${item.code} diubah`,
      metadata: buildAuditChangeMetadata(
        {
          code: existing.code,
          is_mangkok: existing.isMangkok,
          name: existing.name,
          description: existing.description,
          status: existing.status,
          buy_price_standards: existing.buyPriceStandards.map((standard) => ({
            start_date: standard.startDate.toISOString().slice(0, 10),
            end_date: standard.endDate ? standard.endDate.toISOString().slice(0, 10) : null,
            min_price: Number(standard.minPrice),
            max_price: Number(standard.maxPrice),
            notes: standard.notes
          })),
          sell_price_standards: existing.sellPriceStandards.map((standard) => ({
            start_date: standard.startDate.toISOString().slice(0, 10),
            end_date: standard.endDate ? standard.endDate.toISOString().slice(0, 10) : null,
            min_price: Number(standard.minPrice),
            max_price: Number(standard.maxPrice),
            notes: standard.notes
          }))
        },
        {
          code: item.code,
          is_mangkok: item.isMangkok,
          name: item.name,
          description: item.description,
          status: item.status,
          buy_price_standards: item.buyPriceStandards.map((standard) => ({
            start_date: standard.startDate.toISOString().slice(0, 10),
            end_date: standard.endDate ? standard.endDate.toISOString().slice(0, 10) : null,
            min_price: Number(standard.minPrice),
            max_price: Number(standard.maxPrice),
            notes: standard.notes
          })),
          sell_price_standards: item.sellPriceStandards.map((standard) => ({
            start_date: standard.startDate.toISOString().slice(0, 10),
            end_date: standard.endDate ? standard.endDate.toISOString().slice(0, 10) : null,
            min_price: Number(standard.minPrice),
            max_price: Number(standard.maxPrice),
            notes: standard.notes
          }))
        }
      )
    });

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

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

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

