/**
 * ─────────────────────────────────────────────────────────────────────────────
 * TENANT ISOLATION — helpers to ensure every API route is locked to its clinic
 * ─────────────────────────────────────────────────────────────────────────────
 *
 * Usage in any API route:
 *
 *   const { session, error } = await requireAuth();
 *   if (error) return error;
 *   // session.id is the clinicId — use it in ALL DB queries
 *
 *   // When reading a resource:
 *   const appt = await prisma.appointment.findFirst({
 *     where: { id: apptId, clinicId: session.id }  // ← ALWAYS scope by clinicId
 *   });
 *   if (!appt) return notFound();
 */

import { NextResponse } from "next/server";
import { getSession, type ClinicSession } from "./auth";

// ── Require authenticated session ─────────────────────────────────────────────
export async function requireAuth(): Promise<
  | { session: ClinicSession; error: null }
  | { session: null; error: NextResponse }
> {
  const session = await getSession();
  if (!session) {
    return {
      session: null,
      error: NextResponse.json({ error: "Unauthorized" }, { status: 401 }),
    };
  }
  return { session, error: null };
}

// ── Require a specific plan ───────────────────────────────────────────────────
export function requirePlan(
  session: ClinicSession,
  minPlan: "free" | "starter" | "pro" | "enterprise"
): NextResponse | null {
  const planRank: Record<string, number> = {
    free: 0, starter: 1, pro: 2, enterprise: 3,
  };

  const current = planRank[session.plan] ?? 0;
  const required = planRank[minPlan] ?? 0;

  if (current < required) {
    return NextResponse.json(
      {
        error: `This feature requires the ${minPlan} plan or higher.`,
        requiredPlan: minPlan,
        currentPlan:  session.plan,
      },
      { status: 403 }
    );
  }
  return null;
}

// ── Verify tenant ownership of any resource ───────────────────────────────────
/**
 * Throws a 404 if the resource doesn't belong to the current clinic.
 * Call after fetching a resource with `findFirst({ where: { id, clinicId } })`.
 */
export function assertOwnership<T extends object | null>(
  resource: T,
  label = "Resource"
): asserts resource is NonNullable<T> {
  if (!resource) {
    throw new TenantError(`${label} not found`, 404);
  }
}

// ── Tenant-scoped error ───────────────────────────────────────────────────────
export class TenantError extends Error {
  constructor(
    message: string,
    public readonly status: number = 403
  ) {
    super(message);
    this.name = "TenantError";
  }
}

// ── Wrap an API handler with auth + error handling ────────────────────────────
export function withTenant(
  handler: (
    req: Request,
    ctx: { session: ClinicSession; params?: Record<string, string> }
  ) => Promise<Response>
) {
  return async (req: Request, ctx?: { params?: Promise<Record<string, string>> }) => {
    const { session, error } = await requireAuth();
    if (error) return error;

    try {
      const params = ctx?.params ? await ctx.params : undefined;
      return await handler(req, { session, params });
    } catch (err) {
      if (err instanceof TenantError) {
        return NextResponse.json({ error: err.message }, { status: err.status });
      }
      console.error("[API Error]", err);
      return NextResponse.json({ error: "Internal server error" }, { status: 500 });
    }
  };
}

// ── Standard JSON helpers ─────────────────────────────────────────────────────
export const ok      = (data: unknown)  => NextResponse.json(data);
export const created = (data: unknown)  => NextResponse.json(data, { status: 201 });
export const notFound = (msg = "Not found") =>
  NextResponse.json({ error: msg }, { status: 404 });
export const badRequest = (msg: string) =>
  NextResponse.json({ error: msg }, { status: 400 });
export const forbidden = (msg = "Forbidden") =>
  NextResponse.json({ error: msg }, { status: 403 });
