import { useNavigate, useParams } from 'react-router-dom'
import { useEffect, useRef, useState, FormEvent, useCallback, Ref } from 'react'
import SingleColumnLayout from '../../../layouts/SingleColumnLayout.tsx'
import TextInput from '../../../components/inputs/TextInput.tsx'
import Button from '../../../components/buttons/Button.tsx'
import { invalid, valid, Validation } from '../../../utils/validation.ts'
import { AuthService } from '../authService.ts'
import LinkButton from '../../../components/buttons/LinkButton.tsx'
import NavBar from '../../../components/NavBar.tsx'
import NavButton from '../../../components/buttons/NavButton.tsx'
import { useTranslations } from '../../i18n/translations.ts'

const SignupCodeKey = 'signupCode'

interface SignupPageProps {
  authService: AuthService
}

export default function SignupPage({ authService }: SignupPageProps) {
  const { t } = useTranslations()
  const { code } = useParams()
  const [signupCode, setSignupCode] = useState<string | null>(null)
  const [isSubmitting, setIsSubmitting] = useState(false)
  const [rememberMe, setRememberMe] = useState(false)
  const [error, setError] = useState<string>('')
  const [username, setUsername, usernameError, validateUsername] =
    useValidatedInput(isValidUsername)

  const [password, setPassword, passwordError, validatePassword] =
    useValidatedInput(isValidPassword)

  const userNameInputRef = useRef<HTMLInputElement | null>(null)
  const passwordInputRef = useRef<HTMLInputElement | null>(null)

  const navigate = useNavigate()

  useEffect(() => {
    if (signupCode) return

    let theSignupCode: string | null
    if (code) {
      theSignupCode = code
      setSignupCode(theSignupCode)
      localStorage.setItem(SignupCodeKey, theSignupCode)
    } else {
      theSignupCode = localStorage.getItem(SignupCodeKey)
      setSignupCode(theSignupCode)
    }
  }, [code, signupCode])

  useEffect(() => {}, [signupCode])

  const onSubmit = async (e: FormEvent<HTMLFormElement>) => {
    e.preventDefault()

    if (!signupCode) {
      throw new Error("there's no code")
    }

    const isUsernameValid = validateUsername()
    const isPasswordValid = validatePassword()

    if (!isPasswordValid) {
      passwordInputRef.current?.focus()
    }

    if (!isUsernameValid) {
      userNameInputRef.current?.focus()
    }

    if (!isUsernameValid || !isPasswordValid) {
      return
    }

    setIsSubmitting(true)

    try {
      await authService.signup(username, password, signupCode, rememberMe)
      navigate('/')
    } catch (e: unknown) {
      const err = e as Error
      setError(err.message)
    } finally {
      setIsSubmitting(false)
    }
  }

  return (
    <SingleColumnLayout
      navbar={
        <NavBar>
          <NavButton to={'/'}>{t('nav.home')}</NavButton>
        </NavBar>
      }
    >
      <main className="w-full mx-auto p-4">
        <div className="mt-12">
          <form className="flex flex-col gap-4 max-w-md" onSubmit={onSubmit}>
            <FormInput
              id="username"
              label={t('auth.username.label')}
              value={username}
              onInput={setUsername}
              error={usernameError}
              ref={userNameInputRef}
            />

            <FormInput
              id="password"
              label={t('auth.password.label')}
              value={password}
              onInput={setPassword}
              error={passwordError}
              type="password"
              ref={passwordInputRef}
            />
            <div className="flex items-center hidden gap-2 mt-2">
              <input
                type="checkbox"
                id="rememberMe"
                checked={rememberMe}
                onChange={(e) => setRememberMe(e.target.checked)}
                className="h-4 w-4"
              />
              <label htmlFor="rememberMe" className="text-sm text-gray-600">
                {t('auth.remember_me.label')}
              </label>
            </div>
            <Button
              className="mt-4"
              disabled={isSubmitting || !!usernameError || !!passwordError}
              type="submit"
            >
              {isSubmitting ? t('misc.loading') : t('auth.register.cta')}
            </Button>
            <LinkButton secondary to={'/login'}>
              {t('auth.register.login_instead')}
            </LinkButton>

            <span className="text-xs h-3 text-red-500">{error}</span>
          </form>
        </div>
      </main>
    </SingleColumnLayout>
  )
}

interface FormInputProps {
  id: string
  label: string
  value: string
  onInput: (value: string) => void
  error: string | null
  type?: 'text' | 'password'
  ref: Ref<HTMLInputElement>
}

function FormInput({ id, label, value, onInput, error, type = 'text', ref }: FormInputProps) {
  return (
    <div className="flex flex-col gap-1">
      <label htmlFor={id} className="text-sm text-gray-600">
        {label}
      </label>
      <TextInput ref={ref} type={type} id={id} value={value} onInput={onInput} />
      <div className="text-xs h-3 text-red-500">{error}</div>
    </div>
  )
}

type UseValidateInputReturn = [string, (value: string) => void, string | null, () => boolean]

function useValidatedInput(validator: (value: string) => Validation): UseValidateInputReturn {
  const [value, setValue] = useState<string>('')
  const [error, setError] = useState<string | null>(null)
  const [keepValidating, setKeepValidating] = useState(false)

  const validate = useCallback(() => {
    const { isValid, error } = validator(value)
    if (isValid) {
      setError(null)
    } else {
      // We only want to validate on input after they have invalidly submitted once.
      // It's annoying if we set error messages before they've even finished typing.
      setKeepValidating(true)
      setError(error)
    }

    return isValid
  }, [validator, value])

  useEffect(() => {
    if (keepValidating) {
      validate()
    }
  }, [keepValidating, validate])

  return [value, setValue, error, validate]
}

function isValidUsername(username: string): Validation {
  if (!username) return invalid('you need to enter a username :/')

  if (username.length < 3) {
    return invalid('not long enough :(')
  }

  const usernameRegex = /^[a-zA-Z0-9_-]+$/
  if (usernameRegex.test(username)) {
    return valid()
  } else {
    return invalid("that's not a good username :'(")
  }
}

function isValidPassword(password: string): Validation {
  if (password.length >= 6) {
    return valid()
  } else {
    return invalid("that isn't a good password :/")
  }
}
