import { createContext, useContext, useEffect, useState, useCallback, type ReactNode } from "react"
import { useNavigate } from "react-router-dom"
import {
  login as apiLogin,
  loginByCode as apiLoginByCode,
  loginBySms as apiLoginBySms,
  register as apiRegister,
  registerBySms as apiRegisterBySms,
  getMe,
  refreshToken,
} from "../api/auth"
import type { User } from "../types"
import type { CaptchaResult } from "../utils/geetest"

type Captcha = CaptchaResult | null

interface AuthContextType {
  user: User | null
  isLoading: boolean
  login: (email: string, password: string, captcha: Captcha) => Promise<void>
  loginByCode: (email: string, code: string, captcha: Captcha) => Promise<void>
  loginBySms: (phone: string, code: string, captcha: Captcha) => Promise<void>
  register: (email: string, username: string, code: string, password: string | undefined, captcha: Captcha, inviteCode?: string) => Promise<void>
  registerBySms: (phone: string, username: string, code: string, password: string | undefined, captcha: Captcha, inviteCode?: string) => Promise<void>
  logout: () => void
  setUser: (user: User) => void
}

const AuthContext = createContext<AuthContextType | undefined>(undefined)

export function AuthProvider({ children }: { children: ReactNode }) {
  const [user, setUser] = useState<User | null>(null)
  const [isLoading, setIsLoading] = useState(true)
  const navigate = useNavigate()

  const clearAuthAndRedirect = useCallback(() => {
    localStorage.removeItem("access_token")
    localStorage.removeItem("refresh_token")
    setUser(null)
    if (window.location.pathname !== "/login") {
      navigate("/login", { replace: true })
    }
  }, [navigate])

  const loadUser = useCallback(async () => {
    const hadToken = Boolean(localStorage.getItem("access_token") || localStorage.getItem("refresh_token"))
    try {
      const accessToken = localStorage.getItem("access_token")
      const refreshTokenValue = localStorage.getItem("refresh_token")

      if (!accessToken && !refreshTokenValue) {
        setIsLoading(false)
        return
      }

      if (!accessToken && refreshTokenValue) {
        const refreshResult = await refreshToken(refreshTokenValue)
        if (refreshResult.data) {
          localStorage.setItem("access_token", refreshResult.data.access_token)
          localStorage.setItem("refresh_token", refreshResult.data.refresh_token)
        }
      }

      const meResult = await getMe()
      if (meResult.data) {
        setUser(meResult.data)
      }
    } catch {
      if (hadToken) {
        clearAuthAndRedirect()
      } else {
        setUser(null)
      }
    } finally {
      setIsLoading(false)
    }
  }, [clearAuthAndRedirect])

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

  useEffect(() => {
    const onUnauthorized = () => {
      clearAuthAndRedirect()
    }
    window.addEventListener("auth:unauthorized", onUnauthorized)
    return () => window.removeEventListener("auth:unauthorized", onUnauthorized)
  }, [clearAuthAndRedirect])

  const login = async (email: string, password: string, captcha: Captcha) => {
    const result = await apiLogin(email, password, captcha)
    if (result.data) {
      localStorage.setItem("access_token", result.data.tokens.access_token)
      localStorage.setItem("refresh_token", result.data.tokens.refresh_token)
      setUser(result.data.user)
      navigate("/dashboard")
    }
  }

  const loginByCode = async (email: string, code: string, captcha: Captcha) => {
    const result = await apiLoginByCode(email, code, captcha)
    if (result.data) {
      localStorage.setItem("access_token", result.data.tokens.access_token)
      localStorage.setItem("refresh_token", result.data.tokens.refresh_token)
      setUser(result.data.user)
      navigate("/dashboard")
    }
  }

  const loginBySms = async (phone: string, code: string, captcha: Captcha) => {
    const result = await apiLoginBySms(phone, code, captcha)
    if (result.data) {
      localStorage.setItem("access_token", result.data.tokens.access_token)
      localStorage.setItem("refresh_token", result.data.tokens.refresh_token)
      setUser(result.data.user)
      navigate("/dashboard")
    }
  }

  const register = async (email: string, username: string, code: string, password: string | undefined, captcha: Captcha, inviteCode?: string) => {
    const result = await apiRegister(email, username, code, password, captcha, inviteCode)
    if (result.data) {
      localStorage.setItem("access_token", result.data.tokens.access_token)
      localStorage.setItem("refresh_token", result.data.tokens.refresh_token)
      setUser(result.data.user)
      navigate("/dashboard")
    }
  }

  const registerBySms = async (phone: string, username: string, code: string, password: string | undefined, captcha: Captcha, inviteCode?: string) => {
    const result = await apiRegisterBySms(phone, username, code, password, captcha, inviteCode)
    if (result.data) {
      localStorage.setItem("access_token", result.data.tokens.access_token)
      localStorage.setItem("refresh_token", result.data.tokens.refresh_token)
      setUser(result.data.user)
      navigate("/dashboard")
    }
  }

  const logout = () => {
    clearAuthAndRedirect()
  }

  return (
    <AuthContext.Provider value={{ user, isLoading, login, loginByCode, loginBySms, register, registerBySms, logout, setUser }}>
      {children}
    </AuthContext.Provider>
  )
}

export function useAuth() {
  const context = useContext(AuthContext)
  if (!context) {
    throw new Error("useAuth must be used within an AuthProvider")
  }
  return context
}
