import { NextResponse } from "next/server";
import { ZodError } from "zod";
import { Prisma } from "@prisma/client";
import { getSession, SessionUser } from "./auth";
import { ROLES, Role } from "./enums";

// Error khusus dengan HTTP status.
export class ApiError extends Error {
  constructor(public status: number, message: string) {
    super(message);
  }
}

export function ok(data: unknown, init?: ResponseInit) {
  return NextResponse.json({ data }, init);
}
export function created(data: unknown) {
  return NextResponse.json({ data }, { status: 201 });
}
export function fail(status: number, message: string) {
  return NextResponse.json({ error: message }, { status });
}

// Bungkus handler: tangani error terpusat (ApiError, ZodError, dll).
export function handle(
  fn: () => Promise<NextResponse>
): Promise<NextResponse> {
  return fn().catch((err) => {
    if (err instanceof ApiError) return fail(err.status, err.message);
    if (err instanceof ZodError) {
      return NextResponse.json(
        { error: "Validasi gagal", details: err.flatten() },
        { status: 422 }
      );
    }
    if (err instanceof Prisma.PrismaClientKnownRequestError && err.code === "P2002") {
      const target = err.meta?.target;
      const field = Array.isArray(target) ? target.join(", ") : "Data";
      return fail(409, `${field} sudah terdaftar, gunakan yang lain`);
    }
    console.error(err);
    return fail(500, "Terjadi kesalahan pada server");
  });
}

// --- RBAC guards ----------------------------------------------------------

// Wajib login. Lempar 401 jika tidak ada sesi.
export async function requireAuth(): Promise<SessionUser> {
  const user = await getSession();
  if (!user) throw new ApiError(401, "Tidak terautentikasi");
  return user;
}

// Wajib salah satu role tertentu.
export async function requireRole(...roles: Role[]): Promise<SessionUser> {
  const user = await requireAuth();
  if (!roles.includes(user.role))
    throw new ApiError(403, "Akses ditolak untuk role Anda");
  return user;
}

export const isSuperAdmin = (u: SessionUser) => u.role === ROLES.SUPER_ADMIN;

// Enforce scoping cabang. Super admin boleh lintas cabang (read agregat).
// Admin cabang / kurir HANYA boleh branch miliknya sendiri.
// Mengembalikan branchId efektif untuk query, atau melempar 403.
export function scopeBranch(user: SessionUser, requestedBranchId?: string | null): string | null {
  if (isSuperAdmin(user) || user.role === ROLES.OWNER_APPROVER) {
    return requestedBranchId ?? null; // null = semua cabang
  }
  // branch_admin & courier
  if (!user.branchId) throw new ApiError(403, "User tidak terikat cabang");
  if (requestedBranchId && requestedBranchId !== user.branchId)
    throw new ApiError(403, "Tidak boleh mengakses data cabang lain");
  return user.branchId;
}
