"use client";

import { useState, type FormEvent } from "react";
import { Button } from "@/components/ui/button";
import { PasswordInput } from "@/components/ui/password-input";
import { PasswordRequirementsChecklist } from "@/components/auth/password-requirements-checklist";
import { PasswordStrengthMeter } from "@/components/auth/password-strength-meter";
import { useToast } from "@/components/ui/toast";
import { clientFetch } from "@/lib/api/client";
import { isPasswordValid } from "@/lib/password-validation";

// Fields the form already shows an inline message for (current_password
// under its own input, new_password under its own input + the shared
// requirements checklist). Any other key the backend returns (e.g. a future
// validation on a field this form doesn't render its own slot for) still
// needs to reach the user somehow, so it falls back to the generic banner
// below instead of silently vanishing.
const FIELDS_WITH_OWN_ERROR_SLOT = new Set(["current_password", "new_password"]);

export function PasswordForm() {
  const [currentPassword, setCurrentPassword] = useState("");
  const [newPassword, setNewPassword] = useState("");
  const [newPasswordConfirmation, setNewPasswordConfirmation] = useState("");
  const [errors, setErrors] = useState<Record<string, string[]>>({});
  const [formError, setFormError] = useState<string | null>(null);
  const [message, setMessage] = useState<string | null>(null);
  const [isSubmitting, setIsSubmitting] = useState(false);
  const { toast } = useToast();

  const newPasswordValid = isPasswordValid(newPassword);
  const passwordsMatch = newPasswordConfirmation.length > 0 && newPassword === newPasswordConfirmation;

  function fieldError(name: string) {
    return errors[name]?.[0];
  }

  async function handleSubmit(formEvent: FormEvent<HTMLFormElement>) {
    formEvent.preventDefault();
    setMessage(null);
    setErrors({});
    setFormError(null);
    setIsSubmitting(true);

    const res = await clientFetch("/api/profile/password", {
      method: "PUT",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({
        current_password: currentPassword,
        new_password: newPassword,
        new_password_confirmation: newPasswordConfirmation,
      }),
    });

    setIsSubmitting(false);
    const body = await res.json().catch(() => null);

    if (!res.ok) {
      const fieldErrors: Record<string, string[]> = body?.errors ?? {};
      setErrors(fieldErrors);

      const unmatched = Object.entries(fieldErrors).filter(([key]) => !FIELDS_WITH_OWN_ERROR_SLOT.has(key));
      const specificMessage = fieldErrors.current_password?.[0] ?? fieldErrors.new_password?.[0] ?? unmatched[0]?.[1]?.[0];
      const errorMessage = specificMessage ?? body?.message ?? "Failed to change password.";

      // Only shown when there's a message not already covered by a
      // field-level slot above (an unmatched key, or no field errors at
      // all) — avoids double-showing the same text twice on screen.
      setFormError(unmatched.length > 0 || Object.keys(fieldErrors).length === 0 ? errorMessage : null);
      toast(errorMessage, "error");
      return;
    }

    const successMessage = body?.message ?? "Password changed successfully.";
    setMessage(successMessage);
    toast(successMessage, "success");
    setCurrentPassword("");
    setNewPassword("");
    setNewPasswordConfirmation("");
  }

  return (
    <form onSubmit={handleSubmit} className="mt-6 flex flex-col gap-4">
      <label className="flex flex-col gap-1 text-sm font-medium">
        Current Password
        <PasswordInput required value={currentPassword} onChange={(e) => setCurrentPassword(e.target.value)} />
        {fieldError("current_password") && (
          <span className="text-xs text-red-600">{fieldError("current_password")}</span>
        )}
      </label>

      <div className="flex flex-col gap-1 text-sm font-medium">
        <label htmlFor="new_password">New Password</label>
        <PasswordInput
          id="new_password"
          required
          value={newPassword}
          onChange={(e) => setNewPassword(e.target.value)}
          aria-describedby="new-password-requirements"
        />
        {fieldError("new_password") && <span className="text-xs text-red-600">{fieldError("new_password")}</span>}
        <PasswordStrengthMeter password={newPassword} />
        <PasswordRequirementsChecklist password={newPassword} id="new-password-requirements" />
      </div>

      <label className="flex flex-col gap-1 text-sm font-medium">
        Confirm New Password
        <PasswordInput
          required
          value={newPasswordConfirmation}
          onChange={(e) => setNewPasswordConfirmation(e.target.value)}
          aria-describedby="new-password-match-status"
        />
        {newPasswordConfirmation.length > 0 && (
          <p
            id="new-password-match-status"
            aria-live="polite"
            className={`flex items-center gap-1 text-xs font-medium transition-colors duration-200 ${
              passwordsMatch ? "text-green-600" : "text-red-600"
            }`}
          >
            {passwordsMatch ? "✓ Passwords match" : "✕ Passwords do not match"}
          </p>
        )}
      </label>

      {message && <p className="text-sm text-green-600">{message}</p>}
      {formError && <p className="text-sm text-red-600">{formError}</p>}

      <Button
        type="submit"
        size="lg"
        loading={isSubmitting}
        disabled={!newPasswordValid || !passwordsMatch}
        className="mt-2 self-start"
      >
        Change Password
      </Button>
    </form>
  );
}
