/**
 * Change Password API
 * POST - Securely change user password with validation
 *
 * TODO: If using Clerk for auth, password changes should go through Clerk's API instead.
 * This route handles the legacy password-hash-in-DB flow.
 */

import bcrypt from "bcryptjs";
import { NextRequest, NextResponse } from "next/server";
import { auth } from "@/lib/auth";
import { validateCSRFToken } from "@/lib/csrf";
import { withRateLimit } from "@/lib/rate-limiter";
import { validatePasswordStrength } from "@/lib/validators/password-validator";
import { revokeAllOtherSessions } from "@/lib/services/session-manager";
import { sendEmail } from "@/lib/email";
import { getIPFromRequest, getLocationFromIP } from "@/lib/geolocation";
import { convexMutation } from "@/lib/convex-client";
import {
  getAuthenticatedConvexUser,
  getRawConvexUserByEmail,
} from "@/lib/convex-auth-state";

/**
 * POST /api/user/change-password
 * Changes user password with security validations
 * Rate limited to 3 requests per hour
 * Supports both NextAuth sessions and custom auth_token cookies (OTP login)
 */
export async function POST(request: NextRequest) {
  try {
    const userEmail = await getAuthenticatedEmail(request, auth);

    if (!userEmail) {
      return NextResponse.json(
        { success: false, error: "Unauthorized" },
        { status: 401 },
      );
    }

    // Rate limiting - strict for password changes
    const rateLimitResponse = await applyRateLimit(
      request,
      rateLimiters.passwordChange,
    );
    if (rateLimitResponse) return rateLimitResponse;

    // CSRF protection
    if (!validateCSRFToken(request)) {
      return NextResponse.json(
        { success: false, error: "Invalid CSRF token" },
        { status: 403 },
      );
    }

    // Parse request body
    const body = await request.json();
    const { currentPassword, newPassword, confirmPassword } = body;

    // Validate required fields
    if (!currentPassword || !newPassword || !confirmPassword) {
      return NextResponse.json(
        { success: false, error: "All password fields are required" },
        { status: 400 },
      );
    }

    // Check new password matches confirmation
    if (newPassword !== confirmPassword) {
      return NextResponse.json(
        { success: false, error: "New passwords do not match" },
        { status: 400 },
      );
    }

    // Get current user from Convex
    const user = await convexQuery<ConvexUser>("auth/queries:getUserByEmail", {
      email: userEmail,
    });

    if (!user) {
      return NextResponse.json(
        { success: false, error: "User not found" },
        { status: 404 },
      );
    }

    // Verify current password
    if (!user.password) {
      return NextResponse.json(
        {
          success: false,
          error: "Account uses OAuth login. Password change not available.",
        },
        { status: 400 },
      );
    }

    const isCurrentPasswordValid = await bcrypt.compare(
      currentPassword,
      user.password,
    );

    if (!isCurrentPasswordValid) {
      await convexMutation("auth/mutations:createAuditLog", {
        userId: user.id,
        type: "security",
        action: "password_change_failed",
        success: false,
        ip: ipAddress,
        userAgent: request.headers.get("user-agent") || "",
        metadata: { reason: "Invalid current password" },
      });

      return NextResponse.json(
        { success: false, error: "Current password is incorrect" },
        { status: 400 },
      );
    }

    // Validate new password strength
    const passwordValidation = validatePasswordStrength(newPassword, {
      email: user.email,
      name: user.name,
    });

    if (!passwordValidation.valid) {
      return NextResponse.json(
        {
          success: false,
          error: "Password does not meet security requirements",
          feedback: passwordValidation.feedback,
          score: passwordValidation.score,
        },
        { status: 400 },
      );
    }

    // Hash new password
    const hashedPassword = await bcrypt.hash(newPassword, 12);

    await convexMutation("auth/mutations:changePasswordByEmail", {
      email: user.email,
      passwordHash: hashedPassword,
    });

    // Invalidate all other sessions (security best practice)
    // Note: revokeAllOtherSessions still uses Prisma internally for now
    const currentToken = user._id;
    const revokedCount = await revokeAllOtherSessions(user._id, currentToken);

    // Send email notification
    const ipAddress = await getIPFromRequest(request);
    const locationData = await getLocationFromIP(ipAddress);
    const location =
      locationData?.city && locationData?.country
        ? `${locationData.city}, ${locationData.country}`
        : "Unknown location";

    try {
      const emailSubject = user.name
        ? `Password Changed - ${user.name}`
        : "Password Changed";
      const emailHtml = `
        <h2>Password Changed Successfully</h2>
        <p>Your password was changed from:</p>
        <ul>
          <li><strong>Location:</strong> ${location}</li>
          <li><strong>IP Address:</strong> ${ipAddress}</li>
          <li><strong>Time:</strong> ${new Date().toLocaleString()}</li>
        </ul>
        <p>If you did not make this change, please contact support immediately.</p>
        <p><strong>Security Note:</strong> All other active sessions have been logged out for your security.</p>
      `;
      await sendEmail(user.email, {
        subject: emailSubject,
        html: emailHtml,
        text: emailHtml.replace(/<[^>]*>/g, ""),
      });
    } catch (emailError) {
      console.error("Failed to send password change email:", emailError);
      // Don't fail the request if email fails
    }

    await convexMutation("auth/mutations:createAuditLog", {
      userId: user.id,
      type: "security",
      action: "password_changed",
      success: true,
      ip: ipAddress,
      userAgent: request.headers.get("user-agent") || "",
      metadata: {
        location: locationData,
        sessionsRevoked: revokedCount,
      },
    });

    return NextResponse.json({
      success: true,
      message:
        "Password changed successfully. Other sessions have been logged out.",
      sessionsRevoked: revokedCount,
    });
  } catch (error) {
    console.error("[API] Failed to change password:", error);
    return NextResponse.json(
      { success: false, error: "Failed to change password" },
      { status: 500 },
    );
  }
}
