import { NextRequest, NextResponse } from "next/server";
import { auth } from "@clerk/nextjs/server";
import { USER_ROLES, type UserRole } from "@fitai/shared";
import { getDatabase } from "../database";

export interface AuthContext {
  userId: string;
  role: UserRole;
  gymId: string | null;
}

export interface AuthResult extends AuthContext {}

/**
 * Get the full authentication context including gymId
 * Combines Clerk session claims with database lookup for accurate gym assignment
 *
 * @returns AuthContext with userId, role, and gymId
 *
 * @example
 * const { userId, role, gymId } = await getAuthContext();
 * // userId: "user_abc123"
 * // role: "admin"
 * // gymId: "gym_xyz"
 */
export async function getAuthContext(): Promise<AuthContext> {
  const { userId, sessionClaims } = await auth();

  if (!userId) {
    throw new Error("Unauthorized: No authenticated user");
  }

  // Get role from Clerk metadata
  const role = (sessionClaims?.metadata as { role?: UserRole })?.role;

  if (!role || !USER_ROLES.includes(role)) {
    throw new Error(`Forbidden: Invalid or missing role - ${role}`);
  }

  // Get gymId from Clerk metadata or database
  let gymId: string | null = null;

  // First try Clerk metadata (faster path)
  const clerkGymId = (sessionClaims?.metadata as { gymId?: string })?.gymId;
  if (clerkGymId) {
    gymId = clerkGymId;
  } else {
    // Fallback to database lookup
    try {
      const db = await getDatabase();
      const user = await db.getUserById(userId);
      gymId = user?.gymId ?? null;
    } catch (error) {
      console.error("Failed to get gymId from database:", error);
      gymId = null;
    }
  }

  return { userId, role, gymId };
}

/**
 * Middleware to require authentication and optionally check user role
 * Enhanced version that also retrieves gymId
 *
 * @param allowedRoles - Array of roles allowed to access the endpoint (optional)
 * @returns Authentication result with userId, role, and gymId, or NextResponse error
 *
 * @example
 * export async function DELETE(request: NextRequest) {
 *   const authResult = await requireAuth(["admin", "superAdmin"]);
 *   if (authResult instanceof NextResponse) return authResult;
 *   const { userId, role, gymId } = authResult;
 *   // ... proceed with authorized logic
 * }
 */
export async function requireAuth(
  allowedRoles?: UserRole[],
): Promise<AuthResult | NextResponse> {
  try {
    const context = await getAuthContext();

    // Check if user has required role
    if (allowedRoles && allowedRoles.length > 0) {
      if (!allowedRoles.includes(context.role)) {
        return NextResponse.json(
          {
            error: `Forbidden - Requires one of: ${allowedRoles.join(", ")}`,
            requiredRoles: allowedRoles,
            userRole: context.role,
          },
          { status: 403 },
        );
      }
    }

    return context;
  } catch (error) {
    const message = error instanceof Error ? error.message : "Unknown error";

    if (message.includes("Unauthorized")) {
      return NextResponse.json(
        { error: "Unauthorized - Authentication required" },
        { status: 401 },
      );
    }

    if (message.includes("Forbidden")) {
      return NextResponse.json({ error: message }, { status: 403 });
    }

    return NextResponse.json(
      { error: "Internal server error" },
      { status: 500 },
    );
  }
}

/**
 * Require authentication with gym access validation
 * Use this for endpoints that need to verify the user can access a specific gym
 *
 * @param allowedRoles - Roles allowed to access this endpoint
 * @param requiredGymId - Gym ID that must be accessed (optional - uses user's gym if not provided)
 * @returns AuthResult with full context, or NextResponse error
 *
 * @example
 * // Admin accessing their own gym
 * const result = await requireGymAccess(["admin"]);
 *
 * // SuperAdmin accessing specific gym
 * const result = await requireGymAccess(["superAdmin"], "gym_123");
 */
export async function requireGymAccess(
  allowedRoles: UserRole[],
  requiredGymId?: string,
): Promise<AuthResult | NextResponse> {
  const authResult = await requireAuth(allowedRoles);

  if (authResult instanceof NextResponse) {
    return authResult;
  }

  const { role, gymId: userGymId } = authResult;

  // Determine target gym
  const targetGymId = requiredGymId ?? userGymId;

  // SuperAdmin can access any gym
  if (role === "superAdmin") {
    return { ...authResult, gymId: targetGymId };
  }

  // Other roles must have matching gym
  if (!targetGymId) {
    return NextResponse.json(
      { error: "Forbidden - User is not assigned to a gym" },
      { status: 403 },
    );
  }

  if (userGymId !== targetGymId) {
    return NextResponse.json(
      { error: "Forbidden - Cannot access other gym's data" },
      { status: 403 },
    );
  }

  return { ...authResult, gymId: targetGymId };
}

/**
 * Get the target gym ID from request for superAdmin context switching
 * SuperAdmins can pass ?gymId to view specific gym data
 *
 * @param request - NextRequest with query params
 * @param userRole - Current user's role
 * @param userGymId - Current user's gym ID
 * @returns Target gym ID or undefined for superAdmin viewing all
 *
 * @example
 * const targetGymId = getTargetGymId(request, role, gymId);
 * // For superAdmin with ?gymId=abc -> "abc"
 * // For superAdmin without param -> undefined (all gyms)
 * // For admin -> always returns their gymId
 */
export function getTargetGymId(
  request: NextRequest,
  userRole: UserRole,
  userGymId: string | null,
): string | undefined {
  // For superAdmin, allow gymId query param for context switching
  if (userRole === "superAdmin") {
    const queryGymId = request.nextUrl.searchParams.get("gymId");
    return queryGymId ?? undefined;
  }

  // For other roles, they can only access their own gym
  return userGymId ?? undefined;
}

/**
 * Check if the authenticated user is trying to modify their own account
 * Useful for preventing self-deletion or self-demotion
 */
export function isSelfModification(
  userId: string,
  targetUserId: string,
): boolean {
  return userId === targetUserId;
}

/**
 * Validate that an admin isn't demoting themselves or deleting their own account
 */
export function preventSelfModification(
  userId: string,
  targetUserId: string,
  action: string,
): NextResponse | undefined {
  if (isSelfModification(userId, targetUserId)) {
    return NextResponse.json(
      {
        error: `Cannot ${action} your own account`,
        hint: "Ask another administrator to perform this action",
      },
      { status: 403 },
    );
  }
  return undefined;
}
