import { createContext, useContext, useState, useEffect, useCallback, type ReactNode } from 'react'
import {
  type User,
  loginApi,
  logoutApi,
  getStoredTokens,
  getStoredUser,
  isTokenExpired,
  refreshTokenApi,
  clearAuthStorage,
  changePasswordApi,
} from '../services/authApi'
import { TOKEN_REFRESH_BUFFER_MS } from '../config/api'

interface AuthContextType {
  user: User | null
  isAuthenticated: boolean
  isLoading: boolean
  login: (email: string, password: string) => Promise<{ success: boolean; error?: string }>
  logout: () => Promise<void>
  changePassword: (currentPassword: string, newPassword: string) => Promise<{ success: boolean; error?: string }>
}

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

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

  // Refresh token before expiry
  const scheduleTokenRefresh = useCallback((expiresInMs: number) => {
    const refreshIn = Math.max(expiresInMs - TOKEN_REFRESH_BUFFER_MS, 10000)
    
    const timeoutId = setTimeout(async () => {
      const tokens = await refreshTokenApi()
      if (tokens) {
        // Schedule next refresh
        scheduleTokenRefresh(tokens.expiresIn * 1000)
      } else {
        // Refresh failed - logout
        setUser(null)
      }
    }, refreshIn)

    return () => clearTimeout(timeoutId)
  }, [])

  // Check for existing session on mount
  useEffect(() => {
    const initAuth = async () => {
      const { accessToken, refreshToken } = getStoredTokens()
      
      if (!accessToken && !refreshToken) {
        setIsLoading(false)
        return
      }

      // Check if access token is still valid
      if (accessToken && !isTokenExpired(accessToken)) {
        const storedUser = getStoredUser()
        if (storedUser) {
          setUser(storedUser)
          
          // Schedule token refresh
          const payload = JSON.parse(atob(accessToken.split('.')[1]))
          const expiresInMs = (payload.exp * 1000) - Date.now()
          scheduleTokenRefresh(expiresInMs)
        }
        setIsLoading(false)
        return
      }

      // Access token expired - try to refresh
      if (refreshToken && !isTokenExpired(refreshToken)) {
        try {
          const tokens = await refreshTokenApi()
          if (tokens) {
            const storedUser = getStoredUser()
            if (storedUser) {
              setUser(storedUser)
              scheduleTokenRefresh(tokens.expiresIn * 1000)
            }
          }
        } catch {
          clearAuthStorage()
        }
      } else {
        clearAuthStorage()
      }

      setIsLoading(false)
    }

    initAuth()
  }, [scheduleTokenRefresh])

  const login = async (email: string, password: string): Promise<{ success: boolean; error?: string }> => {
    try {
      const response = await loginApi(email, password)
      setUser(response.user)
      
      // Schedule token refresh
      scheduleTokenRefresh(response.tokens.expiresIn * 1000)
      
      return { success: true }
    } catch (error) {
      const message = error instanceof Error ? error.message : 'Login failed'
      return { success: false, error: message }
    }
  }

  const logout = async () => {
    await logoutApi()
    setUser(null)
  }

  const changePassword = async (
    currentPassword: string,
    newPassword: string
  ): Promise<{ success: boolean; error?: string }> => {
    return changePasswordApi(currentPassword, newPassword)
  }

  return (
    <AuthContext.Provider
      value={{
        user,
        isAuthenticated: !!user,
        isLoading,
        login,
        logout,
        changePassword,
      }}
    >
      {children}
    </AuthContext.Provider>
  )
}

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

// Re-export User type for convenience
export type { User } from '../services/authApi'
