// Authentication context for sharing auth state across the app

import { createContext, useContext, useState, useCallback, useEffect, ReactNode } from 'react'
import * as authApi from '@/api/auth'
import apiClient from '@/api/client'
import type { AuthTokens, User } from '@/types'

const ACCESS_TOKEN_KEY = 'smartbotic_access_token'
const REFRESH_TOKEN_KEY = 'smartbotic_refresh_token'
const TOKEN_EXPIRY_KEY = 'smartbotic_token_expiry'

interface AuthContextValue {
  isAuthenticated: boolean
  isLoading: boolean
  user: User | null
  error: string | null
  login: (email: string, password: string) => Promise<void>
  logout: () => Promise<void>
  clearError: () => void
}

const AuthContext = createContext<AuthContextValue | null>(null)

interface AuthProviderProps {
  children: ReactNode
}

export function AuthProvider({ children }: AuthProviderProps) {
  const [isAuthenticated, setIsAuthenticated] = useState(() => {
    return !!localStorage.getItem(ACCESS_TOKEN_KEY)
  })
  const [isLoading, setIsLoading] = useState(true)
  const [user, setUser] = useState<User | null>(null)
  const [error, setError] = useState<string | null>(null)

  const clearError = useCallback(() => {
    setError(null)
  }, [])

  const saveTokens = useCallback((tokens: AuthTokens) => {
    localStorage.setItem(ACCESS_TOKEN_KEY, tokens.access_token)
    localStorage.setItem(REFRESH_TOKEN_KEY, tokens.refresh_token)
    // Calculate expiry time (subtract 60 seconds for buffer)
    const expiryTime = Date.now() + (tokens.expires_in - 60) * 1000
    localStorage.setItem(TOKEN_EXPIRY_KEY, expiryTime.toString())
    apiClient.setAccessToken(tokens.access_token)
  }, [])

  const clearTokens = useCallback(() => {
    localStorage.removeItem(ACCESS_TOKEN_KEY)
    localStorage.removeItem(REFRESH_TOKEN_KEY)
    localStorage.removeItem(TOKEN_EXPIRY_KEY)
    apiClient.setAccessToken(null)
  }, [])

  const refreshTokens = useCallback(async (): Promise<boolean> => {
    const refreshToken = localStorage.getItem(REFRESH_TOKEN_KEY)
    if (!refreshToken) {
      return false
    }

    try {
      const tokens = await authApi.refreshToken(refreshToken)
      saveTokens(tokens)
      return true
    } catch {
      clearTokens()
      setIsAuthenticated(false)
      setUser(null)
      return false
    }
  }, [saveTokens, clearTokens])

  const login = useCallback(
    async (email: string, password: string) => {
      setIsLoading(true)
      setError(null)
      try {
        const tokens = await authApi.login({ email, password })
        saveTokens(tokens)
        setIsAuthenticated(true)
        // Fetch user info after successful login
        try {
          const userInfo = await apiClient.get<User>('/auth/me')
          setUser(userInfo)
        } catch {
          // User info fetch failed but login succeeded
        }
      } catch (err) {
        const message = err instanceof Error ? err.message : 'Login failed'
        setError(message)
        throw err
      } finally {
        setIsLoading(false)
      }
    },
    [saveTokens]
  )

  const logout = useCallback(async () => {
    setIsLoading(true)
    try {
      const refreshToken = localStorage.getItem(REFRESH_TOKEN_KEY)
      if (refreshToken) {
        await authApi.logout(refreshToken).catch(() => {
          // Ignore logout API errors
        })
      }
    } finally {
      clearTokens()
      setIsAuthenticated(false)
      setUser(null)
      setIsLoading(false)
    }
  }, [clearTokens])

  // Initialize auth state on mount
  useEffect(() => {
    const initAuth = async () => {
      const accessToken = localStorage.getItem(ACCESS_TOKEN_KEY)
      if (!accessToken) {
        setIsLoading(false)
        return
      }

      apiClient.setAccessToken(accessToken)

      // Check if token is expired or about to expire
      const expiryStr = localStorage.getItem(TOKEN_EXPIRY_KEY)
      const expiry = expiryStr ? parseInt(expiryStr, 10) : 0

      if (Date.now() >= expiry) {
        // Token expired, try to refresh
        const refreshed = await refreshTokens()
        if (!refreshed) {
          setIsLoading(false)
          return
        }
      }

      // Fetch user info
      try {
        const userInfo = await apiClient.get<User>('/auth/me')
        setUser(userInfo)
        setIsAuthenticated(true)
      } catch {
        // Token invalid, try to refresh
        const refreshed = await refreshTokens()
        if (refreshed) {
          try {
            const userInfo = await apiClient.get<User>('/auth/me')
            setUser(userInfo)
            setIsAuthenticated(true)
          } catch {
            clearTokens()
            setIsAuthenticated(false)
          }
        }
      } finally {
        setIsLoading(false)
      }
    }

    initAuth()
  }, [refreshTokens, clearTokens])

  // Auto-refresh token before expiry
  useEffect(() => {
    if (!isAuthenticated) {
      return
    }

    const checkAndRefresh = () => {
      const expiryStr = localStorage.getItem(TOKEN_EXPIRY_KEY)
      const expiry = expiryStr ? parseInt(expiryStr, 10) : 0

      // Refresh if token expires in less than 2 minutes
      if (Date.now() >= expiry - 120000) {
        refreshTokens()
      }
    }

    // Check every minute
    const interval = setInterval(checkAndRefresh, 60000)

    return () => clearInterval(interval)
  }, [isAuthenticated, refreshTokens])

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

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

export default AuthContext
