import {
  createContext,
  FC,
  PropsWithChildren,
  useCallback,
  useContext,
  useEffect,
  useMemo,
  useRef,
  useState,
} from "react"
import { AppState, AppStateStatus } from "react-native"
import { GoogleSignin, statusCodes } from "@react-native-google-signin/google-signin"
import i18n from "i18next"
import { useMMKVString } from "react-native-mmkv"

import Config from "@/config"
import { useToast } from "@/context/ToastContext"
import { authApi } from "@/services/api/authApi"
import type {
  ForgotPasswordStep,
  LoginStep,
  RegisterStep,
  User,
  UpdateProfileRequest,
} from "@/services/api/authTypes"
import { translateAuthError } from "@/utils/authErrorTranslator"

// Initialize Google Sign-In
GoogleSignin.configure({
  webClientId: Config.GOOGLE_WEB_CLIENT_ID,
  iosClientId: Config.GOOGLE_IOS_CLIENT_ID,
})

// Email change step type
export type EmailChangeStep = "idle" | "verify-current" | "bind-new"

export type AuthContextType = {
  // State
  isAuthenticated: boolean
  user: User | null
  accessToken?: string
  refreshToken?: string
  accessTokenExpiry?: string
  refreshTokenExpiry?: string
  pendingEmail: string
  loginStep: LoginStep
  registerStep: RegisterStep
  forgotPasswordStep: ForgotPasswordStep
  emailChangeStep: EmailChangeStep
  isLoading: boolean
  error: string

  // Legacy - for backward compatibility
  authToken?: string
  authEmail?: string
  setAuthToken: (token?: string) => void
  setAuthEmail: (email: string) => void
  validationError: string

  // Login methods
  preLogin: (email: string, password: string) => Promise<boolean>
  resendCode: () => Promise<boolean>
  verifyLogin: (code: string) => Promise<boolean>
  googleLogin: () => Promise<boolean>
  resetLoginStep: () => void

  // Register methods
  preRegister: (
    username: string,
    password: string,
    email: string,
    nickname?: string,
    referralCode?: string,
  ) => Promise<boolean>
  resendRegisterCode: () => Promise<boolean>
  verifyRegister: (code: string) => Promise<boolean>
  resetRegisterStep: () => void

  // Forgot password methods
  forgotPassword: (email: string) => Promise<boolean>
  resendForgotPasswordCode: () => Promise<boolean>
  resetPassword: (code: string, newPassword: string) => Promise<boolean>
  resetForgotPasswordStep: () => void

  // Profile methods
  fetchProfile: () => Promise<boolean>
  updateProfile: (data: UpdateProfileRequest) => Promise<boolean>

  // Password methods
  changePassword: (
    oldPassword: string,
    newPassword: string,
    logoutOtherDevices?: boolean,
  ) => Promise<boolean>

  // Email change methods
  sendEmailCode: (email: string) => Promise<boolean>
  verifyCurrentEmail: (code: string) => Promise<boolean>
  bindNewEmail: (newEmail: string, code: string) => Promise<boolean>
  resetEmailChangeStep: () => void

  // Common methods
  clearError: () => void
  logout: () => void
}

export const AuthContext = createContext<AuthContextType | null>(null)

export interface AuthProviderProps {}

// One day in milliseconds
const ONE_DAY_MS = 24 * 60 * 60 * 1000

export const AuthProvider: FC<PropsWithChildren<AuthProviderProps>> = ({ children }) => {
  // Toast for error notifications
  const toast = useToast()

  // MMKV persistence
  const [accessToken, setAccessToken] = useMMKVString("AuthProvider.accessToken")
  const [refreshToken, setRefreshToken] = useMMKVString("AuthProvider.refreshToken")
  const [accessTokenExpiry, setAccessTokenExpiry] = useMMKVString("AuthProvider.accessTokenExpiry")
  const [refreshTokenExpiry, setRefreshTokenExpiry] = useMMKVString(
    "AuthProvider.refreshTokenExpiry",
  )
  const [userJson, setUserJson] = useMMKVString("AuthProvider.user")

  // Legacy compatibility
  const [authEmail, setAuthEmail] = useMMKVString("AuthProvider.authEmail")

  // Local state
  const [pendingEmail, setPendingEmail] = useState("")
  const [loginStep, setLoginStep] = useState<LoginStep>("credentials")
  const [registerStep, setRegisterStep] = useState<RegisterStep>("credentials")
  const [forgotPasswordStep, setForgotPasswordStep] = useState<ForgotPasswordStep>("email")
  const [emailChangeStep, setEmailChangeStep] = useState<EmailChangeStep>("idle")
  const [isLoading, setIsLoading] = useState(false)
  const [error, setError] = useState("")

  // Ref to track if token refresh is in progress
  const isRefreshing = useRef(false)

  // Parse user from JSON
  const user = useMemo<User | null>(() => {
    if (!userJson) return null
    try {
      return JSON.parse(userJson)
    } catch {
      return null
    }
  }, [userJson])

  // Legacy validation error
  const validationError = useMemo(() => {
    if (!authEmail || authEmail.length === 0) return "can't be blank"
    if (authEmail.length < 6) return "must be at least 6 characters"
    if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(authEmail)) return "must be a valid email address"
    return ""
  }, [authEmail])

  // Check if token needs refresh (within 1 day of expiry)
  const shouldRefreshToken = useCallback(() => {
    if (!accessTokenExpiry || !refreshToken) return false

    const expiryTime = new Date(accessTokenExpiry).getTime()
    const now = Date.now()
    const timeUntilExpiry = expiryTime - now

    // Refresh if within 1 day of expiry
    return timeUntilExpiry > 0 && timeUntilExpiry <= ONE_DAY_MS
  }, [accessTokenExpiry, refreshToken])

  // Check if refresh token is expired
  const isRefreshTokenExpired = useCallback(() => {
    if (!refreshTokenExpiry) return true

    const expiryTime = new Date(refreshTokenExpiry).getTime()
    return Date.now() >= expiryTime
  }, [refreshTokenExpiry])

  // Perform token refresh
  const performTokenRefresh = useCallback(async (): Promise<boolean> => {
    if (!refreshToken || isRefreshing.current) return false

    // Check if refresh token is expired
    if (isRefreshTokenExpired()) {
      if (__DEV__) console.log("Refresh token expired, logging out...")
      return false
    }

    isRefreshing.current = true

    try {
      const result = await authApi.refreshToken(refreshToken)

      if (result.kind === "ok" && result.data.success && result.data.data) {
        const { accessToken: newAccessToken, accessTokenExpiry: newExpiry } = result.data.data
        setAccessToken(newAccessToken)
        setAccessTokenExpiry(newExpiry)
        if (__DEV__) console.log("Token refreshed successfully")
        isRefreshing.current = false
        return true
      }

      if (__DEV__) console.log("Token refresh failed:", result)
      isRefreshing.current = false
      return false
    } catch (e) {
      if (__DEV__) console.error("Token refresh error:", e)
      isRefreshing.current = false
      return false
    }
  }, [refreshToken, isRefreshTokenExpired, setAccessToken, setAccessTokenExpiry])

  // Auto-refresh token on app state change (when app comes to foreground)
  useEffect(() => {
    const handleAppStateChange = async (nextAppState: AppStateStatus) => {
      if (nextAppState === "active" && accessToken) {
        // Check if refresh token is expired first
        if (isRefreshTokenExpired()) {
          if (__DEV__) console.log("Refresh token expired on app active, logging out...")
          // Clear auth state
          setAccessToken(undefined)
          setRefreshToken(undefined)
          setAccessTokenExpiry(undefined)
          setRefreshTokenExpiry(undefined)
          setUserJson(undefined)
          return
        }

        // Check if we should refresh
        if (shouldRefreshToken()) {
          await performTokenRefresh()
        }
      }
    }

    const subscription = AppState.addEventListener("change", handleAppStateChange)

    // Also check on mount
    if (accessToken) {
      if (isRefreshTokenExpired()) {
        if (__DEV__) console.log("Refresh token expired on mount, logging out...")
        setAccessToken(undefined)
        setRefreshToken(undefined)
        setAccessTokenExpiry(undefined)
        setRefreshTokenExpiry(undefined)
        setUserJson(undefined)
      } else if (shouldRefreshToken()) {
        performTokenRefresh()
      }
    }

    return () => subscription.remove()
  }, [
    accessToken,
    shouldRefreshToken,
    isRefreshTokenExpired,
    performTokenRefresh,
    setAccessToken,
    setRefreshToken,
    setAccessTokenExpiry,
    setRefreshTokenExpiry,
    setUserJson,
  ])

  // Clear error
  const clearError = useCallback(() => {
    setError("")
  }, [])

  // Set error and show toast
  const showError = useCallback(
    (errorMessage: string) => {
      setError(errorMessage)
      toast.error(errorMessage)
    },
    [toast],
  )

  // Reset login step
  const resetLoginStep = useCallback(() => {
    setLoginStep("credentials")
    setPendingEmail("")
    setError("")
  }, [])

  // Helper to save tokens with expiry
  const saveTokens = useCallback(
    (tokens: {
      accessToken: string
      refreshToken: string
      accessTokenExpiry: string
      refreshTokenExpiry: string
    }) => {
      setAccessToken(tokens.accessToken)
      setRefreshToken(tokens.refreshToken)
      setAccessTokenExpiry(tokens.accessTokenExpiry)
      setRefreshTokenExpiry(tokens.refreshTokenExpiry)
    },
    [setAccessToken, setRefreshToken, setAccessTokenExpiry, setRefreshTokenExpiry],
  )

  // Get current language code (e.g., "en", "zh")
  const getCurrentLanguage = useCallback(() => {
    return i18n.language?.split("-")[0] || "en"
  }, [])

  // Pre-login: verify password and send verification code
  const preLogin = useCallback(async (email: string, password: string): Promise<boolean> => {
    setIsLoading(true)
    setError("")

    try {
      const result = await authApi.preLogin(email, password, getCurrentLanguage())

      if (result.kind === "ok" && result.data.success) {
        setPendingEmail(email)
        setLoginStep("verification")
        setIsLoading(false)
        return true
      }

      // Handle API errors (login page shows error inline, no toast needed)
      if (result.kind !== "ok") {
        setError(getErrorMessage(result.kind, result.errorCode, result.errorMessage))
      } else {
        setError(
          getErrorMessage(
            undefined,
            extractErrorCode(result.data),
            extractErrorMessage(result.data),
          ),
        )
      }
      setIsLoading(false)
      return false
    } catch {
      setError(getErrorMessage("cannot-connect"))
      setIsLoading(false)
      return false
    }
  }, [])

  // Resend verification code
  const resendCode = useCallback(async (): Promise<boolean> => {
    if (!pendingEmail) {
      setError(getErrorMessage(undefined, "E013"))
      return false
    }

    setIsLoading(true)
    setError("")

    try {
      const result = await authApi.resendCode(pendingEmail, getCurrentLanguage())

      if (result.kind === "ok" && result.data.success) {
        setIsLoading(false)
        return true
      }

      if (result.kind !== "ok") {
        setError(getErrorMessage(result.kind, result.errorCode, result.errorMessage))
      } else {
        setError(
          getErrorMessage(
            undefined,
            extractErrorCode(result.data),
            extractErrorMessage(result.data),
          ),
        )
      }
      setIsLoading(false)
      return false
    } catch {
      setError(getErrorMessage("cannot-connect"))
      setIsLoading(false)
      return false
    }
  }, [pendingEmail, getCurrentLanguage])

  // Verify login: verify code and get tokens
  const verifyLogin = useCallback(
    async (code: string): Promise<boolean> => {
      if (!pendingEmail) {
        setError(getErrorMessage(undefined, "E013"))
        return false
      }

      setIsLoading(true)
      setError("")

      try {
        const result = await authApi.verifyLogin(pendingEmail, code)

        if (result.kind === "ok" && result.data.success && result.data.data?.tokens) {
          const { tokens, user } = result.data.data
          // Save tokens with expiry
          saveTokens(tokens)
          if (user) {
            setUserJson(JSON.stringify(user))
          }

          // Reset login state
          setPendingEmail("")
          setLoginStep("credentials")
          setIsLoading(false)
          return true
        }

        if (result.kind !== "ok") {
          setError(getErrorMessage(result.kind))
        } else {
          setError(
            getErrorMessage(
              undefined,
              extractErrorCode(result.data),
              extractErrorMessage(result.data),
            ),
          )
        }
        setIsLoading(false)
        return false
      } catch {
        setError(getErrorMessage("cannot-connect"))
        setIsLoading(false)
        return false
      }
    },
    [pendingEmail, saveTokens, setUserJson],
  )

  // Google login - always show account picker so user can choose account
  const googleLogin = useCallback(async (): Promise<boolean> => {
    setIsLoading(true)
    setError("")

    try {
      // Check Play Services (Android)
      await GoogleSignin.hasPlayServices()

      // Sign out first to ensure user can pick a different account
      try {
        await GoogleSignin.signOut()
      } catch {
        // Ignore sign out errors
      }

      // Show Google Sign-In UI
      const signInResult = await GoogleSignin.signIn()

      // User cancelled - just return false without error
      if (signInResult.type === "cancelled") {
        setIsLoading(false)
        return false
      }

      const idToken = signInResult.type === "success" ? signInResult.data.idToken : null

      if (!idToken) {
        setError(getErrorMessage(undefined, "E019"))
        setIsLoading(false)
        return false
      }

      // Call backend API
      const result = await authApi.googleLogin(idToken)

      if (result.kind === "ok" && result.data.success && result.data.data?.tokens) {
        const { tokens, user } = result.data.data
        // Save tokens with expiry
        saveTokens(tokens)
        if (user) {
          setUserJson(JSON.stringify(user))
        }

        setIsLoading(false)
        return true
      }

      if (result.kind !== "ok") {
        setError(getErrorMessage(result.kind, result.errorCode, result.errorMessage))
      } else {
        setError(
          getErrorMessage(
            undefined,
            extractErrorCode(result.data),
            extractErrorMessage(result.data),
          ),
        )
      }
      setIsLoading(false)
      return false
    } catch (e) {
      const error = e as { code?: string }
      // User cancelled the sign in
      if (error.code === statusCodes.SIGN_IN_CANCELLED) {
        setIsLoading(false)
        return false
      }
      if (__DEV__) {
        console.error("Google login error:", e)
      }
      setError(getErrorMessage(undefined, "E018"))
      setIsLoading(false)
      return false
    }
  }, [saveTokens, setUserJson])

  // Reset register step
  const resetRegisterStep = useCallback(() => {
    setRegisterStep("credentials")
    setPendingEmail("")
    setError("")
  }, [])

  // Pre-register: validate data and send verification code
  const preRegister = useCallback(
    async (
      username: string,
      password: string,
      email: string,
      nickname?: string,
      referralCode?: string,
    ): Promise<boolean> => {
      setIsLoading(true)
      setError("")

      try {
        const result = await authApi.preRegister(
          username,
          password,
          email,
          nickname,
          referralCode,
          getCurrentLanguage(),
        )

        if (result.kind === "ok" && result.data.success) {
          setPendingEmail(email)
          setRegisterStep("verification")
          setIsLoading(false)
          return true
        }

        if (result.kind !== "ok") {
          setError(getErrorMessage(result.kind))
        } else {
          setError(
            getErrorMessage(
              undefined,
              extractErrorCode(result.data),
              extractErrorMessage(result.data),
            ),
          )
        }
        setIsLoading(false)
        return false
      } catch {
        setError(getErrorMessage("cannot-connect"))
        setIsLoading(false)
        return false
      }
    },
    [getCurrentLanguage],
  )

  // Resend register verification code
  const resendRegisterCode = useCallback(async (): Promise<boolean> => {
    if (!pendingEmail) {
      setError(getErrorMessage(undefined, "E004"))
      return false
    }

    setIsLoading(true)
    setError("")

    try {
      const result = await authApi.resendRegisterCode(pendingEmail, getCurrentLanguage())

      if (result.kind === "ok" && result.data.success) {
        setIsLoading(false)
        return true
      }

      if (result.kind !== "ok") {
        setError(getErrorMessage(result.kind, result.errorCode, result.errorMessage))
      } else {
        setError(
          getErrorMessage(
            undefined,
            extractErrorCode(result.data),
            extractErrorMessage(result.data),
          ),
        )
      }
      setIsLoading(false)
      return false
    } catch {
      setError(getErrorMessage("cannot-connect"))
      setIsLoading(false)
      return false
    }
  }, [pendingEmail, getCurrentLanguage])

  // Verify register: verify code and create user
  const verifyRegister = useCallback(
    async (code: string): Promise<boolean> => {
      if (!pendingEmail) {
        setError(getErrorMessage(undefined, "E004"))
        return false
      }

      setIsLoading(true)
      setError("")

      try {
        const result = await authApi.verifyRegister(pendingEmail, code)

        if (result.kind === "ok" && result.data.success && result.data.data?.tokens) {
          const { tokens, user } = result.data.data
          // Save tokens with expiry
          saveTokens(tokens)
          if (user) {
            setUserJson(JSON.stringify(user))
          }

          // Reset register state
          setPendingEmail("")
          setRegisterStep("credentials")
          setIsLoading(false)
          return true
        }

        if (result.kind !== "ok") {
          setError(getErrorMessage(result.kind))
        } else {
          setError(
            getErrorMessage(
              undefined,
              extractErrorCode(result.data),
              extractErrorMessage(result.data),
            ),
          )
        }
        setIsLoading(false)
        return false
      } catch {
        setError(getErrorMessage("cannot-connect"))
        setIsLoading(false)
        return false
      }
    },
    [pendingEmail, saveTokens, setUserJson],
  )

  // Reset forgot password step
  const resetForgotPasswordStep = useCallback(() => {
    setForgotPasswordStep("email")
    setPendingEmail("")
    setError("")
  }, [])

  // Forgot password: send verification code
  const forgotPassword = useCallback(
    async (email: string): Promise<boolean> => {
      setIsLoading(true)
      setError("")

      try {
        const result = await authApi.forgotPassword(email, getCurrentLanguage())

        if (result.kind === "ok" && result.data.success) {
          setPendingEmail(email)
          setForgotPasswordStep("reset")
          setIsLoading(false)
          return true
        }

        if (result.kind !== "ok") {
          setError(getErrorMessage(result.kind, result.errorCode, result.errorMessage))
        } else {
          setError(
            getErrorMessage(
              undefined,
              extractErrorCode(result.data),
              extractErrorMessage(result.data),
            ),
          )
        }
        setIsLoading(false)
        return false
      } catch {
        setError(getErrorMessage("cannot-connect"))
        setIsLoading(false)
        return false
      }
    },
    [getCurrentLanguage],
  )

  // Resend forgot password verification code
  const resendForgotPasswordCode = useCallback(async (): Promise<boolean> => {
    if (!pendingEmail) {
      setError(getErrorMessage(undefined, "E025"))
      return false
    }

    setIsLoading(true)
    setError("")

    try {
      const result = await authApi.resendForgotPasswordCode(pendingEmail, getCurrentLanguage())

      if (result.kind === "ok" && result.data.success) {
        setIsLoading(false)
        return true
      }

      if (result.kind !== "ok") {
        setError(getErrorMessage(result.kind, result.errorCode, result.errorMessage))
      } else {
        setError(
          getErrorMessage(
            undefined,
            extractErrorCode(result.data),
            extractErrorMessage(result.data),
          ),
        )
      }
      setIsLoading(false)
      return false
    } catch {
      setError(getErrorMessage("cannot-connect"))
      setIsLoading(false)
      return false
    }
  }, [pendingEmail, getCurrentLanguage])

  // Reset password: verify code and set new password
  const resetPassword = useCallback(
    async (code: string, newPassword: string): Promise<boolean> => {
      if (!pendingEmail) {
        setError(getErrorMessage(undefined, "E025"))
        return false
      }

      setIsLoading(true)
      setError("")

      try {
        const result = await authApi.resetPassword(pendingEmail, code, newPassword)

        if (result.kind === "ok" && result.data.success) {
          // Reset state
          setPendingEmail("")
          setForgotPasswordStep("email")
          setIsLoading(false)
          return true
        }

        if (result.kind !== "ok") {
          setError(getErrorMessage(result.kind))
        } else {
          setError(
            getErrorMessage(
              undefined,
              extractErrorCode(result.data),
              extractErrorMessage(result.data),
            ),
          )
        }
        setIsLoading(false)
        return false
      } catch {
        setError(getErrorMessage("cannot-connect"))
        setIsLoading(false)
        return false
      }
    },
    [pendingEmail],
  )

  // ============================================
  // Profile Methods
  // ============================================

  // Fetch user profile
  const fetchProfile = useCallback(async (): Promise<boolean> => {
    if (!accessToken) {
      showError(getErrorMessage(undefined, "E013"))
      return false
    }

    setIsLoading(true)
    setError("")

    try {
      const result = await authApi.getProfile(accessToken)

      if (result.kind === "ok" && result.data.success && result.data.data) {
        // Update user data
        const profileData = result.data.data
        const updatedUser: User = {
          _id: user?._id || "",
          userId: profileData.userID,
          username: profileData.username,
          email: profileData.email,
          emailVerified: user?.emailVerified ?? true,
          loginMethods: user?.loginMethods || [],
          roles: profileData.roles,
          profile: profileData.profile,
          status: profileData.status,
          referralCode: profileData.referralCode,
          referredByCode: profileData.referredByCode,
          lastLogin: profileData.lastLogin,
          createdAt: profileData.createdAt,
          updatedAt: user?.updatedAt || profileData.createdAt,
        }
        setUserJson(JSON.stringify(updatedUser))
        setIsLoading(false)
        return true
      }

      if (result.kind !== "ok") {
        showError(getErrorMessage(result.kind, result.errorCode, result.errorMessage))
      } else {
        showError(
          getErrorMessage(
            undefined,
            extractErrorCode(result.data),
            extractErrorMessage(result.data),
          ),
        )
      }
      setIsLoading(false)
      return false
    } catch {
      showError(getErrorMessage("cannot-connect"))
      setIsLoading(false)
      return false
    }
  }, [accessToken, user, setUserJson])

  // Update user profile
  const updateProfile = useCallback(
    async (data: UpdateProfileRequest): Promise<boolean> => {
      if (!accessToken) {
        showError(getErrorMessage(undefined, "E013"))
        return false
      }

      setIsLoading(true)
      setError("")

      try {
        const result = await authApi.updateProfile(accessToken, data)

        if (result.kind === "ok" && result.data.success && result.data.data?.user) {
          // Update user data
          setUserJson(JSON.stringify(result.data.data.user))
          setIsLoading(false)
          return true
        }

        if (result.kind !== "ok") {
          showError(getErrorMessage(result.kind))
        } else {
          showError(
            getErrorMessage(
              undefined,
              extractErrorCode(result.data),
              extractErrorMessage(result.data),
            ),
          )
        }
        setIsLoading(false)
        return false
      } catch {
        showError(getErrorMessage("cannot-connect"))
        setIsLoading(false)
        return false
      }
    },
    [accessToken, setUserJson],
  )

  // ============================================
  // Password Methods
  // ============================================

  // Change password
  const changePassword = useCallback(
    async (
      oldPassword: string,
      newPassword: string,
      logoutOtherDevices?: boolean,
    ): Promise<boolean> => {
      if (!accessToken) {
        showError(getErrorMessage(undefined, "E013"))
        return false
      }

      setIsLoading(true)
      setError("")

      try {
        const result = await authApi.changePassword(accessToken, {
          oldPassword,
          newPassword,
          logoutOtherDevices,
        })

        if (result.kind === "ok" && result.data.success) {
          setIsLoading(false)
          return true
        }

        if (result.kind !== "ok") {
          showError(getErrorMessage(result.kind))
        } else {
          showError(
            getErrorMessage(
              undefined,
              extractErrorCode(result.data),
              extractErrorMessage(result.data),
            ),
          )
        }
        setIsLoading(false)
        return false
      } catch {
        showError(getErrorMessage("cannot-connect"))
        setIsLoading(false)
        return false
      }
    },
    [accessToken],
  )

  // ============================================
  // Email Change Methods
  // ============================================

  // Reset email change step
  const resetEmailChangeStep = useCallback(() => {
    setEmailChangeStep("idle")
    setPendingEmail("")
    setError("")
  }, [])

  // Send email verification code
  const sendEmailCode = useCallback(
    async (email: string): Promise<boolean> => {
      if (!accessToken) {
        showError(getErrorMessage(undefined, "E013"))
        return false
      }

      setIsLoading(true)
      setError("")

      try {
        const result = await authApi.sendEmailCode(accessToken, email, getCurrentLanguage())

        if (result.kind === "ok" && result.data.success) {
          // Update step to verify-current if currently idle (first time sending to current email)
          if (emailChangeStep === "idle") {
            setEmailChangeStep("verify-current")
          }
          setIsLoading(false)
          return true
        }

        if (result.kind !== "ok") {
          showError(getErrorMessage(result.kind))
        } else {
          showError(
            getErrorMessage(
              undefined,
              extractErrorCode(result.data),
              extractErrorMessage(result.data),
            ),
          )
        }
        setIsLoading(false)
        return false
      } catch {
        showError(getErrorMessage("cannot-connect"))
        setIsLoading(false)
        return false
      }
    },
    [accessToken, getCurrentLanguage, emailChangeStep],
  )

  // Verify current email (Step 1)
  const verifyCurrentEmail = useCallback(
    async (code: string): Promise<boolean> => {
      if (!accessToken) {
        showError(getErrorMessage(undefined, "E013"))
        return false
      }

      setIsLoading(true)
      setError("")

      try {
        const result = await authApi.verifyCurrentEmail(accessToken, code)

        if (result.kind === "ok" && result.data.success) {
          // Move to next step
          setEmailChangeStep("bind-new")
          setIsLoading(false)
          return true
        }

        if (result.kind !== "ok") {
          showError(getErrorMessage(result.kind))
        } else {
          showError(
            getErrorMessage(
              undefined,
              extractErrorCode(result.data),
              extractErrorMessage(result.data),
            ),
          )
        }
        setIsLoading(false)
        return false
      } catch {
        showError(getErrorMessage("cannot-connect"))
        setIsLoading(false)
        return false
      }
    },
    [accessToken],
  )

  // Bind new email (Step 2)
  const bindNewEmail = useCallback(
    async (newEmail: string, code: string): Promise<boolean> => {
      if (!accessToken) {
        showError(getErrorMessage(undefined, "E013"))
        return false
      }

      setIsLoading(true)
      setError("")

      try {
        const result = await authApi.bindNewEmail(accessToken, newEmail, code)

        if (result.kind === "ok" && result.data.success && result.data.data?.user) {
          // Update user data
          setUserJson(JSON.stringify(result.data.data.user))
          // Reset email change state
          setEmailChangeStep("idle")
          setPendingEmail("")
          setIsLoading(false)
          return true
        }

        if (result.kind !== "ok") {
          showError(getErrorMessage(result.kind))
        } else {
          showError(
            getErrorMessage(
              undefined,
              extractErrorCode(result.data),
              extractErrorMessage(result.data),
            ),
          )
        }
        setIsLoading(false)
        return false
      } catch {
        showError(getErrorMessage("cannot-connect"))
        setIsLoading(false)
        return false
      }
    },
    [accessToken, setUserJson],
  )

  // ============================================
  // Logout
  // ============================================

  const logout = useCallback(async () => {
    // Call logout API if we have a token
    if (accessToken) {
      try {
        await authApi.logoutApi(accessToken)
      } catch {
        // Ignore logout API errors
      }
    }

    setAccessToken(undefined)
    setRefreshToken(undefined)
    setAccessTokenExpiry(undefined)
    setRefreshTokenExpiry(undefined)
    setUserJson(undefined)
    setAuthEmail("")
    setPendingEmail("")
    setLoginStep("credentials")
    setRegisterStep("credentials")
    setForgotPasswordStep("email")
    setEmailChangeStep("idle")
    setError("")

    // Sign out from Google if signed in
    try {
      await GoogleSignin.signOut()
    } catch {
      // Ignore Google sign out errors
    }
  }, [
    accessToken,
    setAccessToken,
    setRefreshToken,
    setAccessTokenExpiry,
    setRefreshTokenExpiry,
    setUserJson,
    setAuthEmail,
  ])

  // Legacy setAuthToken for backward compatibility
  const setAuthToken = useCallback(
    (token?: string) => {
      setAccessToken(token)
    },
    [setAccessToken],
  )

  const value: AuthContextType = {
    // State
    isAuthenticated: !!accessToken,
    user,
    accessToken,
    refreshToken,
    accessTokenExpiry,
    refreshTokenExpiry,
    pendingEmail,
    loginStep,
    registerStep,
    forgotPasswordStep,
    emailChangeStep,
    isLoading,
    error,

    // Legacy
    authToken: accessToken,
    authEmail,
    setAuthToken,
    setAuthEmail,
    validationError,

    // Login methods
    preLogin,
    resendCode,
    verifyLogin,
    googleLogin,
    resetLoginStep,

    // Register methods
    preRegister,
    resendRegisterCode,
    verifyRegister,
    resetRegisterStep,

    // Forgot password methods
    forgotPassword,
    resendForgotPasswordCode,
    resetPassword,
    resetForgotPasswordStep,

    // Profile methods
    fetchProfile,
    updateProfile,

    // Password methods
    changePassword,

    // Email change methods
    sendEmailCode,
    verifyCurrentEmail,
    bindNewEmail,
    resetEmailChangeStep,

    // Common methods
    clearError,
    logout,
  }

  return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>
}

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

// Helper function to get translated error message
function getErrorMessage(problemKind?: string, errorCode?: string, message?: string): string {
  return translateAuthError(errorCode, message, problemKind)
}

// Helper to extract error code from API response (handles both code and error.code)
function extractErrorCode(data: { code?: string; error?: { code?: string } }): string | undefined {
  return data.code || data.error?.code
}

// Helper to extract error message from API response (handles both message and error.message)
function extractErrorMessage(data: {
  message?: string
  error?: { message?: string }
}): string | undefined {
  return data.message || data.error?.message
}
