import { NextRequest, NextResponse } from "next/server";
import { auth, clerkClient } from "@clerk/nextjs/server";
import { getAuthContext } from "@/lib/auth/context";
import { getInvitableRoles, validateGymAccess } from "@/lib/auth/permissions";
import log from "@/lib/logger";

/**
 * GET /api/invitations
 *
 * Fetch pending invitations with gym and creator filtering.
 * Users can only see invitations they created, scoped to their gym access.
 *
 * Query params:
 * - gymId: Optional gym filter (superAdmin only)
 */
export async function GET(request: NextRequest) {
  try {
    const { userId } = await auth();
    if (!userId) {
      return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
    }

    // Get auth context
    const authContext = await getAuthContext();
    const { role, gymId: userGymId } = authContext;

    // Get query params
    const { searchParams } = new URL(request.url);
    const gymIdParam = searchParams.get("gymId");

    // Validate gym access
    const targetGymId = gymIdParam || undefined;
    const accessError = validateGymAccess(role, userGymId, targetGymId);
    if (accessError) return accessError;

    // Fetch all pending invitations from Clerk
    const client = await clerkClient();
    const invitationList = await client.invitations.getInvitationList({
      status: "pending",
    });

    // Filter invitations based on:
    // 1. Creator (only show invitations created by current user)
    // 2. Gym (apply gym-scoping rules)
    const filteredInvitations = invitationList.data
      .filter((inv) => {
        const metadata = inv.publicMetadata as any;

        // Creator filter: only show if user created it
        if (metadata?.createdBy !== userId) {
          return false;
        }

        // Gym filter: SuperAdmin can see all, others only their gym
        if (role === "superAdmin") {
          // If gymId param provided, filter by it
          if (targetGymId && (metadata?.gymId ?? null) !== targetGymId) {
            return false;
          }
          return true;
        } else {
          // Non-superAdmins: must match their gym (normalize null/undefined)
          return (metadata?.gymId ?? null) === (userGymId ?? null);
        }
      })
      .map((inv) => ({
        id: inv.id,
        emailAddress: inv.emailAddress,
        publicMetadata: inv.publicMetadata,
        status: inv.status,
        url: inv.url,
        createdAt: inv.createdAt,
        updatedAt: inv.updatedAt,
        revoked: inv.revoked,
      }));

    return NextResponse.json({
      data: { invitations: filteredInvitations },
    });
  } catch (error) {
    log.error("GET /api/invitations error:", error);
    return NextResponse.json(
      { error: "Failed to fetch invitations" },
      { status: 500 },
    );
  }
}

/**
 * POST /api/invitations
 *
 * Create a Clerk-managed invitation and store role/gym context in publicMetadata.
 * This endpoint does not implement invitation acceptance; Clerk’s acceptance link flow and webhooks will handle that.
 *
 * Body: {
 *   inviteeEmail: string,
 *   roleAssigned: 'trainer' | 'client' | 'admin'
 * }
 *
 * Rules:
 * - admin: can invite trainer or client; the gymId is taken from inviter's publicMetadata or user record.
 * - trainer: can invite client; the gymId is taken from inviter's publicMetadata or user record.
 * - superAdmin: can invite admin; requires `gymId` in body, or falls back to inviter's `gymId` if present.
 *
 * Returns Clerk invitation payload.
 */
export async function POST(req: Request) {
  try {
    const { userId } = await auth();
    if (!userId) {
      return new NextResponse("Unauthorized", { status: 401 });
    }

    const body = await req.json().catch(() => null);
    if (!body || typeof body !== "object") {
      return NextResponse.json({ error: "Invalid JSON body" }, { status: 400 });
    }

    const inviteeEmail = String(body.inviteeEmail ?? "")
      .trim()
      .toLowerCase();
    const roleAssigned = String(body.roleAssigned ?? "").trim() as
      | "trainer"
      | "client"
      | "admin";
    let requestedGymId: string | null = body.gymId ? String(body.gymId) : null;

    if (!inviteeEmail || !roleAssigned) {
      return NextResponse.json(
        { error: "inviteeEmail and roleAssigned are required" },
        { status: 400 },
      );
    }

    const authContext = await getAuthContext();
    const { role: inviterRole, gymId: inviterGymId } = authContext;

    const allowedRoles = getInvitableRoles(inviterRole);
    if (!allowedRoles.includes(roleAssigned)) {
      return NextResponse.json(
        { error: `Forbidden - Cannot invite role '${roleAssigned}'` },
        { status: 403 },
      );
    }

    // Enforce role-based rules and resolve target gymId for the invitation
    let gymIdForInvite: string | null = null;
    if (inviterRole === "superAdmin") {
      gymIdForInvite = requestedGymId || inviterGymId || null;
      if (!gymIdForInvite) {
        return NextResponse.json(
          { error: "gymId is required for superAdmin invitations" },
          { status: 400 },
        );
      }
    } else {
      if (!inviterGymId) {
        return NextResponse.json(
          { error: "Inviter must be assigned to a gym" },
          { status: 400 },
        );
      }
      if (requestedGymId && requestedGymId !== inviterGymId) {
        return NextResponse.json(
          { error: "Cannot invite users into another gym" },
          { status: 403 },
        );
      }
      gymIdForInvite = inviterGymId;
    }

    // Create Clerk invitation with metadata needed by webhook to assign role & gym
    const client = await clerkClient();
    const invitation = await client.invitations.createInvitation({
      emailAddress: inviteeEmail,
      publicMetadata: {
        role: roleAssigned,
        gymId: gymIdForInvite,
        createdBy: userId,
      },
    });

    return NextResponse.json(invitation, { status: 201 });
  } catch (error: any) {
    // Surface Clerk errors where possible
    const message =
      error?.errors?.[0]?.message ||
      error?.message ||
      "Failed to create invitation";
    console.error("POST /api/invitations error:", error);
    return NextResponse.json({ error: message }, { status: 500 });
  }
}
