import { useEffect, useState, type FormEvent } from 'react'
import { createPortal } from 'react-dom'
import { X } from 'lucide-react'
import styles from './PasswordResetModal.module.css'

export type ChangePasswordHandler = (
  currentPassword: string,
  newPassword: string,
) => Promise<{ message: string }>

export interface PasswordResetModalProps {
  open: boolean
  onClose: () => void
  onChangePassword: ChangePasswordHandler
  title?: string
  subtitle?: string
}

const ANIMATION_MS = 220

const PASSWORD_RULES = [
  {
    id: 'length',
    label: 'At least 8 characters',
    test: (value: string) => value.length >= 8,
  },
  {
    id: 'upper',
    label: 'One uppercase letter',
    test: (value: string) => /[A-Z]/.test(value),
  },
  {
    id: 'number',
    label: 'One number',
    test: (value: string) => /\d/.test(value),
  },
  {
    id: 'special',
    label: 'One special character',
    test: (value: string) => /[^A-Za-z0-9]/.test(value),
  },
] as const

type StrengthTone = 'empty' | 'weak' | 'fair' | 'good' | 'strong'

function getPasswordChecks(password: string) {
  return PASSWORD_RULES.map((rule) => ({
    id: rule.id,
    label: rule.label,
    met: rule.test(password),
  }))
}

function getStrengthMeta(metCount: number, hasInput: boolean): {
  tone: StrengthTone
  label: string
  percent: number
} {
  if (!hasInput || metCount === 0) {
    return { tone: 'empty', label: 'Enter a password', percent: 0 }
  }

  if (metCount === 1) return { tone: 'weak', label: 'Weak', percent: 25 }
  if (metCount === 2) return { tone: 'fair', label: 'Fair', percent: 50 }
  if (metCount === 3) return { tone: 'good', label: 'Good', percent: 75 }
  return { tone: 'strong', label: 'Strong', percent: 100 }
}

function getPasswordValidationError(password: string): string | null {
  const unmet = PASSWORD_RULES.filter((rule) => !rule.test(password))
  if (unmet.length === 0) return null
  return `Password must include: ${unmet.map((rule) => rule.label.toLowerCase()).join(', ')}.`
}

export function PasswordResetModal({
  open,
  onClose,
  onChangePassword,
  title = 'Change password',
  subtitle = 'Enter your current password and choose a new one.',
}: PasswordResetModalProps) {
  const [mounted, setMounted] = useState(open)
  const [closing, setClosing] = useState(false)
  const [currentPassword, setCurrentPassword] = useState('')
  const [newPassword, setNewPassword] = useState('')
  const [confirmPassword, setConfirmPassword] = useState('')
  const [error, setError] = useState('')
  const [success, setSuccess] = useState('')
  const [isSubmitting, setIsSubmitting] = useState(false)

  const checks = getPasswordChecks(newPassword)
  const metCount = checks.filter((check) => check.met).length
  const strength = getStrengthMeta(metCount, newPassword.length > 0)
  const passwordValid = metCount === PASSWORD_RULES.length

  useEffect(() => {
    if (open) {
      setMounted(true)
      setClosing(false)
      setError('')
      setSuccess('')
      setCurrentPassword('')
      setNewPassword('')
      setConfirmPassword('')
    } else if (mounted) {
      setClosing(true)
      const timer = setTimeout(() => {
        setMounted(false)
        setClosing(false)
      }, ANIMATION_MS)
      return () => clearTimeout(timer)
    }
  }, [open, mounted])

  useEffect(() => {
    if (!mounted || closing) return
    const onKey = (e: KeyboardEvent) => {
      if (e.key === 'Escape') onClose()
    }
    document.addEventListener('keydown', onKey)
    return () => document.removeEventListener('keydown', onKey)
  }, [mounted, closing, onClose])

  async function handleSubmit(e: FormEvent) {
    e.preventDefault()
    setError('')
    setSuccess('')

    const validationError = getPasswordValidationError(newPassword)
    if (validationError) {
      setError(validationError)
      return
    }

    if (newPassword !== confirmPassword) {
      setError('New password and confirmation do not match.')
      return
    }

    setIsSubmitting(true)

    try {
      const result = await onChangePassword(currentPassword, newPassword)
      setSuccess(result.message)
      setCurrentPassword('')
      setNewPassword('')
      setConfirmPassword('')
    } catch (err) {
      setError(err instanceof Error ? err.message : 'Unable to change password.')
    } finally {
      setIsSubmitting(false)
    }
  }

  if (!mounted) return null

  return createPortal(
    <div
      className={`${styles.overlay} ${closing ? styles.overlayOut : styles.overlayIn}`}
      onClick={onClose}
    >
      <div
        className={`${styles.modal} ${closing ? styles.modalOut : styles.modalIn}`}
        onClick={(e) => e.stopPropagation()}
        role="dialog"
        aria-modal="true"
        aria-labelledby="password-reset-title"
      >
        <button type="button" className={styles.closeBtn} onClick={onClose} aria-label="Close">
          <X size={18} />
        </button>

        <h3 id="password-reset-title" className={styles.title}>
          {title}
        </h3>
        <p className={styles.subtitle}>{subtitle}</p>

        <form className={styles.form} onSubmit={(e) => void handleSubmit(e)} autoComplete="off">
          {error && (
            <div className={styles.error} role="alert">
              {error}
            </div>
          )}
          {success && (
            <div className={styles.success} role="status">
              {success}
            </div>
          )}

          <div className={styles.field}>
            <label htmlFor="password-reset-current">Current password</label>
            <input
              id="password-reset-current"
              type="password"
              name="current-password-field"
              autoComplete="current-password"
              value={currentPassword}
              onChange={(e) => setCurrentPassword(e.target.value)}
              required
              disabled={isSubmitting}
            />
          </div>

          <div className={styles.field}>
            <label htmlFor="password-reset-new">New password</label>
            <input
              id="password-reset-new"
              type="password"
              name="new-password-field"
              autoComplete="new-password"
              value={newPassword}
              onChange={(e) => setNewPassword(e.target.value)}
              required
              minLength={8}
              disabled={isSubmitting}
              aria-describedby="password-reset-strength password-reset-rules"
            />

            <div className={styles.strength} id="password-reset-strength">
              <div className={styles.strengthHeader}>
                <span className={styles.strengthTitle}>Password strength</span>
                <span className={`${styles.strengthLabel} ${styles[`tone_${strength.tone}`]}`}>
                  {strength.label}
                </span>
              </div>
              <div
                className={styles.strengthTrack}
                role="progressbar"
                aria-valuemin={0}
                aria-valuemax={100}
                aria-valuenow={strength.percent}
                aria-label="Password strength"
              >
                <div
                  className={`${styles.strengthFill} ${styles[`fill_${strength.tone}`]}`}
                  style={{ width: `${strength.percent}%` }}
                />
              </div>
              <ul className={styles.rules} id="password-reset-rules">
                {checks.map((check) => (
                  <li
                    key={check.id}
                    className={`${styles.rule} ${check.met ? styles.ruleMet : ''}`}
                  >
                    <span className={styles.ruleDot} aria-hidden="true" />
                    {check.label}
                  </li>
                ))}
              </ul>
            </div>
          </div>

          <div className={styles.field}>
            <label htmlFor="password-reset-confirm">Confirm password</label>
            <input
              id="password-reset-confirm"
              type="password"
              name="confirm-password-field"
              autoComplete="new-password"
              value={confirmPassword}
              onChange={(e) => setConfirmPassword(e.target.value)}
              required
              minLength={8}
              disabled={isSubmitting}
            />
          </div>

          <div className={styles.actions}>
            <button type="button" className={styles.cancelBtn} onClick={onClose} disabled={isSubmitting}>
              Cancel
            </button>
            <button
              type="submit"
              className={styles.submitBtn}
              disabled={isSubmitting || !passwordValid}
            >
              {isSubmitting ? 'Saving...' : 'Update password'}
            </button>
          </div>
        </form>
      </div>
    </div>,
    document.body,
  )
}
