/**
 * Auth Adapter — Clerk Auth Bridge
 *
 * Provides a unified auth interface backed by Clerk + Convex.
 * Falls back to NextAuth session for backwards compatibility during transition.
 *
 * Usage:
 *   import { getAuthUser } from '@/lib/auth-adapter';
 *   const user = await getAuthUser();
 *
 *   import { withAuth } from '@/lib/auth-adapter';
 *   export const GET = withAuth(async (req, ctx, user) => { ... });
 */

import { NextRequest, NextResponse } from "next/server";

export interface AuthAdapterUser {
  id: string;
  email: string | null;
  name: string | null;
  role?: string;
  organizationId?: string | null;
  orgRole?: string | null;
  allowedTabs?: string[] | null;
  stripeCustomerId?: string | null;
}

export interface AuthAdapterSession {
  user: AuthAdapterUser;
  provider: "nextauth" | "clerk";
  expiresAt?: Date;
}

/**
 * Get authenticated user from Clerk (primary) or NextAuth (fallback).
 * Returns null if not authenticated.
 */
export async function getAuthUser(): Promise<AuthAdapterUser | null> {
  // Primary: Clerk
  try {
    const { getClerkUser } = await import("./clerk");
    const clerkUser = await getClerkUser();

    if (clerkUser) {
      const { bridgeClerkUser } = await import("./clerk-user-bridge");
      let bridged = null;
      try {
        bridged = await bridgeClerkUser(clerkUser.id);
      } catch (err) {
        console.error(
          "[getAuthUser] Clerk bridge error, using minimal user:",
          err,
        );
      }

      if (!bridged) {
        return {
          id: clerkUser.id,
          email: clerkUser.email,
          name: clerkUser.name,
        };
      }

      return {
        id: bridged.id,
        email: bridged.email,
        name: bridged.name,
        role: bridged.role,
        organizationId: bridged.organizationId ?? null,
        orgRole: bridged.orgRole ?? null,
        allowedTabs: bridged.allowedTabs ?? null,
        stripeCustomerId: bridged.stripeCustomerId ?? null,
      };
    }
  } catch {
    // Clerk not available — fall through to NextAuth
  }

  // Fallback: NextAuth
  const { auth } = await import("./auth");
  const session = await auth();
  if (!session?.user) return null;

  return {
    id: session.user.id,
    email: session.user.email ?? null,
    name: session.user.name ?? null,
    role: session.user.role,
    organizationId: session.user.organizationId ?? null,
    orgRole: session.user.orgRole ?? null,
    allowedTabs: session.user.allowedTabs ?? null,
    stripeCustomerId: session.user.stripeCustomerId ?? null,
  };
}

/**
 * Require authenticated user — throws UNAUTHENTICATED if no session.
 */
export async function requireAuthUser(): Promise<AuthAdapterUser> {
  const user = await getAuthUser();

  if (!user) {
    throw new Error("UNAUTHENTICATED");
  }

  return user;
}

/**
 * Get auth user ID only — cheaper than fetching the full session.
 */
export async function getAuthUserId(): Promise<string | null> {
  try {
    const { getClerkUserId } = await import("./clerk");
    const clerkId = await getClerkUserId();
    if (clerkId) return clerkId;
  } catch {
    // Fall through to NextAuth
  }

  const { auth } = await import("./auth");
  const session = await auth();
  return session?.user?.id ?? null;
}

/**
 * Get a normalized session object with provider information.
 */
export async function getAuthSession(): Promise<AuthAdapterSession | null> {
  // Primary: Clerk
  try {
    const { getClerkUser } = await import("./clerk");
    const clerkUser = await getClerkUser();

    if (clerkUser) {
      const { bridgeClerkUser } = await import("./clerk-user-bridge");
      let bridged = null;
      try {
        bridged = await bridgeClerkUser(clerkUser.id);
      } catch (err) {
        console.error(
          "[getAuthSession] Clerk bridge error, using minimal user:",
          err,
        );
      }

      return {
        provider: "clerk",
        user: bridged
          ? {
              id: bridged.id,
              email: bridged.email,
              name: bridged.name,
              role: bridged.role,
              organizationId: bridged.organizationId ?? null,
              orgRole: bridged.orgRole ?? null,
              allowedTabs: bridged.allowedTabs ?? null,
              stripeCustomerId: bridged.stripeCustomerId ?? null,
            }
          : {
              id: clerkUser.id,
              email: clerkUser.email,
              name: clerkUser.name,
            },
      };
    }
  } catch {
    // Fall through to NextAuth
  }

  // Fallback: NextAuth
  const { auth } = await import("./auth");
  const session = await auth();
  if (!session?.user) return null;

  return {
    provider: "nextauth",
    user: {
      id: session.user.id,
      email: session.user.email ?? null,
      name: session.user.name ?? null,
      role: session.user.role,
      organizationId: session.user.organizationId ?? null,
      orgRole: session.user.orgRole ?? null,
      allowedTabs: session.user.allowedTabs ?? null,
      stripeCustomerId: session.user.stripeCustomerId ?? null,
    },
    expiresAt: session.expires ? new Date(session.expires) : undefined,
  };
}

// ---------------------------------------------------------------------------
// withAuth — Higher-Order Function for API Route Handlers
// ---------------------------------------------------------------------------

type RouteContext<P = Record<string, string>> = { params: Promise<P> };

type AuthedHandler<P = Record<string, string>> = (
  request: NextRequest,
  context: RouteContext<P>,
  user: AuthAdapterUser,
) => Promise<NextResponse>;

/**
 * Wrap a Next.js App Router route handler with auth checking.
 *
 * The wrapped handler receives the authenticated user as a third argument.
 * Returns 401 automatically when no session is found.
 *
 * @example
 * ```typescript
 * export const GET = withAuth(async (request, context, user) => {
 *   return NextResponse.json({ userId: user.id });
 * });
 * ```
 */
export function withAuth<P = Record<string, string>>(
  handler: AuthedHandler<P>,
): (request: NextRequest, context: RouteContext<P>) => Promise<NextResponse> {
  return async (request: NextRequest, context: RouteContext<P>) => {
    const user = await getAuthUser();
    if (!user) {
      return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
    }
    return handler(request, context, user);
  };
}
