import { NextRequest, NextResponse } from "next/server";
import { auth, clerkClient } from "@clerk/nextjs/server";
import { db, users, clients } from "@fitai/database";
import { createUserSchema } from "@/lib/validation/user-schemas";
import { sendWelcomeEmail, sendInvitationEmail } from "@/lib/email";
import {
  successResponse,
  errorResponse,
  unauthorizedResponse,
} from "@/lib/api/responses";
import { getAuthContext } from "@/lib/auth/context";
import { getInvitableRoles } from "@/lib/auth/permissions";
import log from "@/lib/logger";

/**
 * POST /api/users/create
 *
 * Create a new user with support for:
 * - Direct creation (database only, no Clerk account)
 * - Invitation-based creation (sends Clerk invitation email)
 * - Client-specific fields (membership type, status)
 * - Welcome emails for clients
 */
export async function POST(request: NextRequest) {
  try {
    const { userId } = await auth();
    if (!userId) {
      return unauthorizedResponse("Unauthorized");
    }

    // Get full auth context (role and gymId)
    const authContext = await getAuthContext();
    const { role: requesterRole, gymId: requesterGymId } = authContext;

    const body = await request.json();
    const validationResult = createUserSchema.safeParse(body);

    if (!validationResult.success) {
      return errorResponse("Validation failed", {
        status: 400,
        details: validationResult.error.flatten().fieldErrors as Record<
          string,
          string[]
        >,
      });
    }

    const data = validationResult.data;

    // Validate role creation permission
    const allowedRoles = getInvitableRoles(requesterRole);
    if (!allowedRoles.includes(data.role)) {
      const roleMessages: Record<string, string> = {
        trainer:
          "Trainers can only create clients. Contact an admin to create other roles.",
        admin:
          "Admins can create trainers and clients only. Contact a superAdmin to create other admins.",
        client: "Clients cannot create users.",
      };
      return errorResponse(
        roleMessages[requesterRole] ||
          "You are not authorized to create this role",
        { status: 403 },
      );
    }

    // Auto-assign gym for non-superAdmins
    // SuperAdmins can choose any gym, others must use their own gym
    const assignedGymId =
      requesterRole === "superAdmin" ? data.gymId : requesterGymId;

    // Validate that non-superAdmins have a gym assigned
    if (!assignedGymId && requesterRole !== "superAdmin") {
      return errorResponse("You are not assigned to a gym", { status: 400 });
    }

    // Note: Email is required in current schema
    // TODO: Add schema migration to make email optional for direct creation
    if (!data.email) {
      return errorResponse("Email is required", { status: 400 });
    }

    // Case 1: Send Clerk Invitation (requires email)
    if (data.sendInvitation && data.email) {
      try {
        const client = await clerkClient();

        // Build publicMetadata with consistent field names
        const publicMetadata: Record<string, any> = {
          role: data.role,
          createdBy: userId,
          gymId: assignedGymId ?? null,
        };

        // Determine redirect URL based on role
        // Staff (admin, trainer, superAdmin) → Admin web app
        // Clients → Mobile app (handled by Clerk's default)
        const isStaffRole = ["admin", "trainer", "superAdmin"].includes(
          data.role,
        );
        const redirectUrl = isStaffRole
          ? `${process.env.NEXT_PUBLIC_APP_URL || "http://localhost:3000"}/sign-up`
          : undefined; // Clients use default Clerk redirect

        const invitation = await client.invitations.createInvitation({
          emailAddress: data.email,
          publicMetadata,
          redirectUrl,
          ignoreExisting: true, // Don't fail if invitation already exists
        });

        log.info("Clerk invitation sent", {
          email: data.email,
          role: data.role,
          gymId: assignedGymId,
          invitationId: invitation.id,
          redirectUrl: redirectUrl || "default (mobile app)",
        });

        // Send custom invitation email (in addition to Clerk's)
        if (invitation.url) {
          await sendInvitationEmail(data.email, data.role, invitation.url);
        }

        return successResponse(
          {
            invitation: {
              id: invitation.id,
              email: invitation.emailAddress,
              status: invitation.status,
            },
          },
          { status: 200 },
        );
      } catch (error: unknown) {
        const errorMessage =
          error instanceof Error ? error.message : "Unknown error";

        // Extract Clerk-specific error details
        const clerkError = error as any;

        log.error("Failed to send Clerk invitation", error, {
          email: data.email,
          role: data.role,
          gymId: assignedGymId,
          publicMetadata: assignedGymId
            ? { role: data.role, gymId: assignedGymId }
            : { role: data.role },
          clerkStatus: clerkError?.status,
          clerkErrors: clerkError?.errors,
          clerkTraceId: clerkError?.clerkTraceId,
        });

        return errorResponse(`Failed to send invitation: ${errorMessage}`, {
          status: 500,
        });
      }
    }

    // Case 2: Direct Creation (database only, no Clerk account)
    // This creates a user record that can later be linked when they sign up

    // Generate a temporary user ID (will be replaced when they sign up with Clerk)
    const tempUserId = `temp_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;

    // Insert user into database
    const [newUser] = await db
      .insert(users)
      .values({
        id: tempUserId,
        email: data.email,
        firstName: data.firstName,
        lastName: data.lastName,
        role: data.role,
        phone: data.phone,
        gymId: assignedGymId,
      })
      .returning();

    log.info("User created in database", {
      userId: newUser.id,
      role: data.role,
      gymId: assignedGymId,
      hasEmail: !!data.email,
    });

    // Case 3: Create client record if role is client
    if (data.role === "client" && data.membershipType) {
      const clientId = `client_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;

      await db.insert(clients).values({
        id: clientId,
        userId: newUser.id,
        membershipType: data.membershipType,
        membershipStatus: data.membershipStatus || "active",
        joinDate: new Date(),
      });

      log.info("Client record created", {
        userId: newUser.id,
        membershipType: data.membershipType,
      });

      // Send welcome email if requested and email provided
      if (data.sendWelcomeEmail && data.email) {
        await sendWelcomeEmail(data.email, data.firstName);
        log.info("Welcome email sent", { email: data.email });
      }
    }

    return successResponse(
      {
        user: {
          id: newUser.id,
          email: newUser.email,
          firstName: newUser.firstName,
          lastName: newUser.lastName,
          role: newUser.role,
        },
      },
      { status: 201 },
    );
  } catch (error) {
    log.error("Failed to create user", error);
    return errorResponse("Failed to create user", { status: 500 });
  }
}
