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 AnchorButton from '../../../components/buttons/AnchorButton.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'

const SignupCodeKey = 'signupCode'

interface SignupPageProps {
  authService: AuthService
}

export default function SignupPage({ authService }: SignupPageProps) {
  const { code } = useParams()
  const [signupCode, setSignupCode] = useState<string | null>(null)
  const [isSubmitting, setIsSubmitting] = useState(false)

  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 dialogRef = useRef<HTMLDialogElement | 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)
    }

    if (!theSignupCode) {
      dialogRef.current?.showModal()
    }
  }, [code, 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)
      navigate('/')
    } finally {
      setIsSubmitting(false)
    }
  }

  return (
    <SingleColumnLayout
      navbar={
        <NavBar>
          <NavButton to={'/'}>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"
              value={username}
              onInput={setUsername}
              error={usernameError}
              ref={userNameInputRef}
            />

            <FormInput
              id="password"
              value={password}
              onInput={setPassword}
              error={passwordError}
              type="password"
              ref={passwordInputRef}
            />
            <Button
              className="mt-4"
              disabled={isSubmitting || !!usernameError || !!passwordError}
              type="submit"
            >
              {isSubmitting ? 'wait...' : 'give me an account pls'}
            </Button>
            <LinkButton secondary to={'/login'}>
              login instead?
            </LinkButton>
          </form>
        </div>
      </main>

      <dialog
        id="go-away-dialog"
        ref={dialogRef}
        className="p-6 rounded-lg shadow-lg m-auto outline-none"
      >
        <div className="text-gray-600 flex flex-col gap-2">
          <h1 className={`font-bold text-lg`}>STOP !!!</h1>
          <p>You need an invitation to sign up</p>
          <p>
            I'm surprised you even found your way here without one and honestly I'd prefer it if you
            would leave
          </p>
          <p>
            If you <span className="italic">do</span> want to create an account, you should know who
            to contact
          </p>
          <AnchorButton className={`mt-4`} href="https://en.wikipedia.org/wiki/Special:Random">
            I'm sorry I'll go somewhere else :(
          </AnchorButton>
        </div>
      </dialog>
    </SingleColumnLayout>
  )
}

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

function FormInput({ id, value, onInput, error, type = 'text', ref }: FormInputProps) {
  return (
    <div className="flex flex-col gap-1">
      <label htmlFor={id} className="text-sm text-gray-600">
        {id}
      </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 :/")
  }
}
