import { Loader2 } from "lucide-react"
import { useEffect, useMemo, useRef, useState } from "react"
import { Link, Navigate, useNavigate } from "react-router-dom"
import { toast } from "sonner"

import { loginWithOtp, sendOtp } from "../../api/users"
import { Button } from "../../components/ui/button"
import { useAuthFlow } from "../../context/AuthFlowContext"
import { useTranslation } from "../../hooks/useTranslation"
import { AuthOtpInput } from "./AuthOtpInput"
import { AuthPanel } from "./AuthPanel"
import {
  completeAuthentication,
  formatCooldown,
  getApiErrorMessage,
  getOtpRemainingSeconds,
  handleThrottleError,
} from "./utils"

export function LoginOtpPage() {
  const navigate = useNavigate()
  const { t, lang } = useTranslation()
  const {
    state,
    setCode,
    setCooldown,
    clearCooldown,
    setOtpDelivery,
    clearOtpDelivery,
  } = useAuthFlow()
  const isRtl = lang === "fa"
  const autoSendStartedRef = useRef(false)
  const activeSubmitCodeRef = useRef("")
  const lastFailedCodeRef = useRef("")
  const [isSendingOtp, setIsSendingOtp] = useState(false)
  const [isSubmitting, setIsSubmitting] = useState(false)
  const [now, setNow] = useState(Date.now())

  if (!state.login.mobile) {
    return <Navigate to="/auth" replace />
  }

  useEffect(() => {
    if (!state.login.otpExpiresAt || getOtpRemainingSeconds(state.login.otpExpiresAt) <= 0) {
      return
    }

    const timer = window.setInterval(() => {
      setNow(Date.now())
    }, 1000)

    return () => window.clearInterval(timer)
  }, [state.login.otpExpiresAt])

  const sendLoginOtp = async () => {
    setIsSendingOtp(true)
    try {
      const response = await sendOtp(state.login.mobile, "login")
      clearCooldown("loginOtpSend")
      clearCooldown("loginOtpVerify")
      lastFailedCodeRef.current = ""
      setCode("login", "")
      setOtpDelivery("login", response.expires_in_seconds)
      setNow(Date.now())
      toast.success(t.login.toasts.verifySent)
    } catch (error) {
      clearOtpDelivery("login")
      if (
        !handleThrottleError({
          error,
          cooldownKey: "loginOtpSend",
          setCooldown,
          formatTime: (seconds) => formatCooldown(seconds, isRtl),
          throttleCopy: t.login.throttle,
        })
      ) {
        toast.error(getApiErrorMessage(error, t.login.toasts.failedOtp))
      }
    } finally {
      setIsSendingOtp(false)
    }
  }

  useEffect(() => {
    if (!state.login.pendingOtpSend || autoSendStartedRef.current) {
      return
    }

    autoSendStartedRef.current = true
    void sendLoginOtp()
  }, [state.login.pendingOtpSend])

  const otpRemainingSeconds = state.login.otpExpiresAt
    ? Math.max(0, Math.ceil((state.login.otpExpiresAt - now) / 1000))
    : 0

  const throttleAlert = useMemo(() => {
    if (state.cooldowns.loginOtpVerify > 0) {
      const formatted = formatCooldown(state.cooldowns.loginOtpVerify, isRtl)
      return {
        title: t.login.throttle.title,
        description: t.login.throttle.otpLoginMessage(formatted),
      }
    }

    if (state.cooldowns.loginOtpSend > 0) {
      const formatted = formatCooldown(state.cooldowns.loginOtpSend, isRtl)
      return {
        title: t.login.throttle.title,
        description: t.login.throttle.otpSendMessage(formatted),
      }
    }

    return null
  }, [isRtl, state.cooldowns.loginOtpSend, state.cooldowns.loginOtpVerify, t.login.throttle])

  const expiryMessage =
    otpRemainingSeconds > 0
      ? t.login.otpExpiresIn(formatCooldown(otpRemainingSeconds, isRtl))
      : state.login.otpExpiresAt
        ? t.login.otpExpired
        : null

  const submitCode = async (code: string) => {
    if (code.length !== 5) {
      toast.error(t.login.toasts.enterOtp)
      return
    }

    if (isSendingOtp || isSubmitting || code === activeSubmitCodeRef.current) {
      return
    }

    if (code === lastFailedCodeRef.current) {
      toast.error(t.login.toasts.invalidOtp)
      return
    }

    activeSubmitCodeRef.current = code
    setIsSubmitting(true)
    try {
      const data = await loginWithOtp(state.login.mobile, code)
      clearCooldown("loginOtpVerify")
      activeSubmitCodeRef.current = ""
      lastFailedCodeRef.current = ""
      completeAuthentication({
        access: data.access,
        refresh: data.refresh,
        successMessage: t.login.toasts.successLogin,
        redirectTo: "/profile",
        navigate,
      })
    } catch (error) {
      if (
        !handleThrottleError({
          error,
          cooldownKey: "loginOtpVerify",
          setCooldown,
          formatTime: (seconds) => formatCooldown(seconds, isRtl),
          throttleCopy: t.login.throttle,
        })
      ) {
        lastFailedCodeRef.current = code
        toast.error(getApiErrorMessage(error, t.login.toasts.invalidOtp))
      }
    } finally {
      activeSubmitCodeRef.current = ""
      setIsSubmitting(false)
    }
  }

  const handleResend = async () => {
    autoSendStartedRef.current = false
    await sendLoginOtp()
  }

  const isBusy = isSendingOtp || isSubmitting
  const isRepeatedInvalidCode = state.login.code.length === 5 && state.login.code === lastFailedCodeRef.current

  return (
    <AuthPanel
      title={t.login.loginOtpTitle}
      description={t.login.sentCodeDesc(state.login.mobile)}
      backTo="/auth/login/password"
      backLabel={t.login.backToPasswordLogin}
      step={{ current: 2, total: 2, label: t.login.stepLabel(2, 2) }}
      alert={throttleAlert}
    >
      <form
        onSubmit={(event) => {
          event.preventDefault()
          void submitCode(state.login.code)
        }}
        className="grid gap-4"
      >
        <AuthOtpInput
          id="login-otp"
          value={state.login.code}
          disabled={isBusy}
          onChange={(value) => setCode("login", value)}
          onComplete={(value) => void submitCode(value)}
        />

        {expiryMessage ? (
          <p className="text-center text-sm text-slate-500 dark:text-slate-400">{expiryMessage}</p>
        ) : null}

        <Button
          type="submit"
          className="h-11 w-full"
          disabled={isBusy || isRepeatedInvalidCode || state.cooldowns.loginOtpVerify > 0}
        >
          {(isSubmitting || isSendingOtp) && <Loader2 className="me-2 h-4 w-4 animate-spin" />}
          {isSubmitting ? t.login.verifyingOtp : isSendingOtp ? t.login.sendingOtp : t.login.verifyAndContinue}
        </Button>

        <Button
          type="button"
          variant="outline"
          className="h-11 w-full"
          disabled={isBusy || otpRemainingSeconds > 0 || state.cooldowns.loginOtpSend > 0}
          onClick={() => void handleResend()}
        >
          {state.cooldowns.loginOtpSend > 0
            ? t.login.throttle.countdownLabel(formatCooldown(state.cooldowns.loginOtpSend, isRtl))
            : t.login.resendOtp}
        </Button>

        <div className="text-center text-sm text-slate-500 dark:text-slate-400 underline">
          <Link
            to="/auth/login/password"
            className="font-medium text-blue-600 transition-colors hover:text-blue-500 dark:text-blue-400 dark:hover:text-blue-300"
          >
            {t.login.usePasswordInstead}
          </Link>
        </div>
      </form>
    </AuthPanel>
  )
}
