import { Eye, EyeOff } from 'lucide-react'
import { useState, type FormEvent } from 'react'
import { Link, useNavigate, useSearchParams } from 'react-router-dom'

import { AuthLayout } from './AuthLayout'
import { Alert, Button, Input } from '@/components/ui'
import { PASSWORD_HINT } from '@/features/auth/passwordPolicy'
import { ApiError, api } from '@/lib/api'

export function ResetPasswordPage() {
  const [searchParams] = useSearchParams()
  const navigate = useNavigate()

  const token = searchParams.get('token') ?? ''
  const email = searchParams.get('email') ?? ''

  const [password, setPassword] = useState('')
  const [confirmation, setConfirmation] = useState('')
  const [showPassword, setShowPassword] = useState(false)
  const [error, setError] = useState<ApiError | null>(null)
  const [isSubmitting, setIsSubmitting] = useState(false)

  const handleSubmit = async (event: FormEvent) => {
    event.preventDefault()
    setError(null)
    setIsSubmitting(true)

    try {
      await api.post('/auth/reset-password', {
        token,
        email,
        password,
        password_confirmation: confirmation,
      })

      navigate('/login', {
        replace: true,
        state: { notice: 'Your password has been updated. Sign in with your new password.' },
      })
    } catch (caught) {
      if (caught instanceof ApiError) {
        setError(caught)
      } else {
        throw caught
      }
    } finally {
      setIsSubmitting(false)
    }
  }

  // A link that lost its parameters cannot be completed, so say so rather than
  // presenting a form that is guaranteed to fail.
  if (token === '' || email === '') {
    return (
      <AuthLayout title="Reset link incomplete">
        <Alert tone="error" title="This link is not usable">
          Open the most recent reset link from your email, or request a new one.
        </Alert>
        <div className="mt-5">
          <Link to="/forgot-password">
            <Button variant="secondary" fullWidth>
              Request a new link
            </Button>
          </Link>
        </div>
      </AuthLayout>
    )
  }

  return (
    <AuthLayout title="Choose a new password" description={`Resetting the password for ${email}.`}>
      <form onSubmit={handleSubmit} className="flex flex-col gap-4" noValidate>
        {error !== null && !error.isValidation && (
          <Alert tone="error" title="Cannot reset password">
            {error.message}
          </Alert>
        )}

        {error?.errorFor('email') !== undefined && (
          <Alert tone="error" title="This link has expired">
            {error.errorFor('email')}
          </Alert>
        )}

        <Input
          label="New password"
          type={showPassword ? 'text' : 'password'}
          value={password}
          onChange={(event) => setPassword(event.target.value)}
          autoComplete="new-password"
          autoFocus
          required
          hint={PASSWORD_HINT}
          error={error?.errorFor('password')}
          trailingSlot={
            <button
              type="button"
              onClick={() => setShowPassword((value) => !value)}
              aria-label={showPassword ? 'Hide password' : 'Show password'}
              className="flex size-8 items-center justify-center rounded-md text-text-muted hover:text-text-primary"
            >
              {showPassword ? (
                <EyeOff aria-hidden className="size-4" />
              ) : (
                <Eye aria-hidden className="size-4" />
              )}
            </button>
          }
        />

        <Input
          label="Confirm new password"
          type={showPassword ? 'text' : 'password'}
          value={confirmation}
          onChange={(event) => setConfirmation(event.target.value)}
          autoComplete="new-password"
          required
          error={
            confirmation !== '' && confirmation !== password
              ? 'The passwords do not match.'
              : undefined
          }
        />

        {/*
          Worth saying explicitly: resetting the password signs every other
          device out, which is the point if the reset is a response to a
          suspected compromise.
        */}
        <p className="text-sm text-text-muted">
          For your security, this will sign you out on all other devices.
        </p>

        <Button type="submit" size="lg" fullWidth isLoading={isSubmitting}>
          Update password
        </Button>
      </form>
    </AuthLayout>
  )
}
