/**
 * Forgot Password Endpoint
 *
 * AUTH STRATEGY: Clerk-first with legacy OTP fallback.
 * When Clerk is configured (NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY is set),
 * password reset is handled by Clerk's built-in <SignIn> component UI.
 * This endpoint returns a redirect hint so the client navigates to Clerk's
 * forgot-password flow. When Clerk is NOT configured, the existing
 * OTP-based email/phone reset flow is used unchanged.
 *
 * Security features:
 * - Rate limiting (5 requests per hour per IP)
 * - Input validation and sanitization
 * - Prevents user enumeration
 *
 * Note: CSRF protection not needed for unauthenticated password reset flows
 */

import { NextRequest, NextResponse } from "next/server";
import { generateOTP, sendOTP, storeOTP } from "@/lib/otp-storage";
import { sendPasswordResetEmail } from "@/lib/email";
import { withRateLimit, rateLimiters } from "@/lib/rate-limiter";
import { validateEmail, sanitizeInput } from "@/lib/input-validation";
import { logger } from "@/lib/logger";
import { prisma } from "@/lib/db";
import { getDataSource } from "@/lib/data-source";

const isClerkConfigured = () =>
  Boolean(process.env.NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY);

// Force dynamic rendering to prevent build-time execution
export const dynamic = "force-dynamic";
export const runtime = "nodejs";

async function forgotPasswordHandler(request: NextRequest) {
  try {
    const body = await request.json();
    const { identifier } = body;

    if (!identifier) {
      return NextResponse.json(
        { error: "Email or phone number is required" },
        { status: 400 },
      );
    }

    const sanitizedIdentifier = sanitizeInput(identifier);

    // Detect if identifier is email or phone
    const emailValidation = validateEmail(sanitizedIdentifier);
    const isEmail = emailValidation.valid;
    const isPhone =
      /^[\+]?[(]?[0-9]{1,4}[)]?[-\s\.]?[(]?[0-9]{1,4}[)]?[-\s\.]?[0-9]{1,9}$/.test(
        sanitizedIdentifier,
      );

    if (!isEmail && !isPhone) {
      return NextResponse.json(
        { error: "Invalid email or phone number format" },
        { status: 400 },
      );
    }

    // ── Clerk-first path ──
    // When Clerk is the primary auth system, password reset is handled
    // entirely by Clerk's prebuilt UI. We return a generic success message
    // (to prevent user enumeration) and a useClerk flag so the client
    // knows to redirect to Clerk's forgot-password flow.
    if (isClerkConfigured() && isEmail) {
      logger.info("Password reset delegated to Clerk", {
        method: "email",
      });

      return NextResponse.json(
        {
          success: true,
          useClerk: true,
          message:
            "If an account exists with this email, you will be redirected to reset your password",
          method: "email",
        },
        { status: 200 },
      );
    }

    // Check if user exists in database
    let user: {
      id: string;
      email?: string | null;
      phoneNumber?: string | null;
    } | null = null;

    // Convex data-source routing (email lookups only — no getUserByPhone in Convex)
    if (isEmail && getDataSource("auth") === "convex") {
      try {
        const { convexQuery } = await import("@/lib/convex-client");
        const convexUser = await convexQuery("auth/queries:getUserByEmail", {
          email: sanitizedIdentifier.toLowerCase(),
        });
        if (convexUser) {
          const cu = convexUser as Record<string, unknown>;
          user = {
            id: (cu._id as string) || (cu.prismaId as string) || "",
            email: cu.email as string | null,
            phoneNumber: cu.phoneNumber as string | null,
          };
        }
      } catch (error) {
        console.error("[Convex fallback] forgot-password:", error);
        // Fall through to Prisma
        user = null;
      }
    }

    // Prisma fallback (or phone-based lookup)
    if (!user) {
      user = await prisma.users.findFirst({
        where: isEmail
          ? { email: sanitizedIdentifier.toLowerCase() }
          : { phoneNumber: sanitizedIdentifier },
      });
    }

    if (!user) {
      // Return success anyway to prevent user enumeration
      // But log the attempt for security monitoring
      logger.warn("Password reset requested for non-existent user", {
        identifier: isEmail
          ? sanitizedIdentifier
          : sanitizedIdentifier.slice(-4),
        method: isEmail ? "email" : "phone",
      });

      await prisma.audit_logs.create({
        data: {
          id: crypto.randomUUID(), // Generate unique ID for audit log
          type: "password_reset_invalid_user",
          action: "Password reset requested for non-existent user",
          success: false,
          ip:
            request.headers.get("x-forwarded-for") ||
            request.headers.get("x-real-ip") ||
            "127.0.0.1",
          userAgent: request.headers.get("user-agent") || undefined,
          metadata: {
            identifier: isEmail
              ? sanitizedIdentifier
              : sanitizedIdentifier.slice(-4),
          },
        },
      });

      // Return generic success message
      return NextResponse.json(
        {
          success: true,
          message: isEmail
            ? "If an account exists with this email, a reset code has been sent"
            : "If an account exists with this phone number, an OTP has been sent",
          method: isEmail ? "email" : "phone",
        },
        { status: 200 },
      );
    }

    if (isEmail) {
      // Generate and store OTP in Redis
      const code = generateOTP();

      // Store OTP in Redis (separate from email sending to avoid dependency issues)
      const stored = await storeOTP(sanitizedIdentifier.toLowerCase(), code);

      if (!stored) {
        logger.error("Failed to store password reset OTP", {
          email: sanitizedIdentifier,
          userId: user.id,
        });
        return NextResponse.json(
          { error: "Failed to process reset request" },
          { status: 500 },
        );
      }

      // Detect user's language from browser or default to 'en'
      const lang = (
        request.headers.get("accept-language")?.includes("ar") ? "ar" : "en"
      ) as "ar" | "en";

      // Send password reset email
      const emailSent = await sendPasswordResetEmail(
        sanitizedIdentifier.toLowerCase(),
        code,
        lang,
      );

      if (!emailSent) {
        logger.error("Failed to send password reset email", {
          email: sanitizedIdentifier,
          userId: user.id,
        });
        // OTP is still stored, so user can try again if needed
      }

      // Log password reset request
      await prisma.audit_logs.create({
        data: {
          id: crypto.randomUUID(), // Generate unique ID for audit log
          userId: user.id,
          type: "password_reset_requested",
          action: "Password reset email sent",
          success: emailSent,
          ip:
            request.headers.get("x-forwarded-for") ||
            request.headers.get("x-real-ip") ||
            "127.0.0.1",
          userAgent: request.headers.get("user-agent") || undefined,
          metadata: { method: "email", emailSent },
        },
      });

      // Return OTP in dev mode for testing
      const isDev = process.env.NODE_ENV === "development";

      return NextResponse.json(
        {
          success: true,
          message: "Password reset code sent to your email",
          method: "email",
          ...(isDev && { code }), // Include OTP in dev mode for testing
        },
        { status: 200 },
      );
    }

    if (isPhone) {
      // Send OTP via SMS/Phone
      const code = generateOTP();

      // Detect user's language
      const lang = (
        request.headers.get("accept-language")?.includes("ar") ? "ar" : "en"
      ) as "ar" | "en";

      const sent = await sendOTP(sanitizedIdentifier, code, lang);

      if (!sent) {
        logger.error("Failed to send password reset OTP", {
          phone: sanitizedIdentifier.slice(-4),
          userId: user.id,
        });
        return NextResponse.json(
          { error: "Failed to send OTP" },
          { status: 500 },
        );
      }

      // Log password reset request
      await prisma.audit_logs.create({
        data: {
          id: crypto.randomUUID(), // Generate unique ID for audit log
          userId: user.id,
          type: "password_reset_requested",
          action: "Password reset OTP sent to phone",
          success: true,
          ip:
            request.headers.get("x-forwarded-for") ||
            request.headers.get("x-real-ip") ||
            "127.0.0.1",
          userAgent: request.headers.get("user-agent") || undefined,
          metadata: { method: "phone" },
        },
      });

      return NextResponse.json(
        {
          success: true,
          message: "OTP sent to your phone",
          method: "phone",
          requiresOTP: true,
        },
        { status: 200 },
      );
    }

    return NextResponse.json({ error: "Invalid request" }, { status: 400 });
  } catch (error) {
    logger.error("Forgot password API error", error);
    return NextResponse.json(
      { error: "Internal server error" },
      { status: 500 },
    );
  }
}

// Apply rate limiting only (CSRF not needed for unauthenticated flows)
export const POST = withRateLimit(
  forgotPasswordHandler,
  rateLimiters.authGeneral,
);
