'use client'

import {
  createContext,
  useContext,
  useState,
  useEffect,
  useCallback,
  useRef,
  type ReactNode,
} from 'react'
import type { BrainfishRESTAuth } from '../types'
import { authStorage, fetchDeviceId } from './auth-storage'

/** Security scheme info from OpenAPI spec */
export interface SpecSecurityScheme {
  name: string
  type: 'basic' | 'bearer' | 'api-key' | 'oauth2' | 'none'
  auth: BrainfishRESTAuth
  description?: string
}

interface AuthContextValue {
  /** Current global authentication configuration */
  globalAuth: BrainfishRESTAuth | null
  /** Whether global auth is configured and active */
  isAuthenticated: boolean
  /** Set the global authentication */
  setGlobalAuth: (auth: BrainfishRESTAuth) => void
  /** Clear the global authentication */
  clearAuth: () => void
  /** Get effective auth for an endpoint (global or override) */
  getEffectiveAuth: (endpointAuth: BrainfishRESTAuth) => BrainfishRESTAuth
  /** Check if using global auth for an endpoint */
  isUsingGlobalAuth: (endpointAuth: BrainfishRESTAuth) => boolean
  /** Security schemes from the OpenAPI spec */
  specSchemes: SpecSecurityScheme[]
  /** Default auth recommended by the spec */
  specDefaultAuth: BrainfishRESTAuth | null
}

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

const DEFAULT_AUTH: BrainfishRESTAuth = { authType: 'none', authActive: true }

interface AuthProviderProps {
  children: ReactNode
}

export function AuthProvider({ children }: AuthProviderProps) {
  const [globalAuth, setGlobalAuthState] = useState<BrainfishRESTAuth | null>(null)
  const [isInitialized, setIsInitialized] = useState(false)
  const [specSchemes, setSpecSchemes] = useState<SpecSecurityScheme[]>([])
  const [specDefaultAuth, setSpecDefaultAuth] = useState<BrainfishRESTAuth | null>(null)
  const deviceIdRef = useRef<string | null>(null)

  // Load auth from localStorage on mount (async with encryption)
  useEffect(() => {
    async function initializeAuth() {
      try {
        // Fetch device ID from backend (required for encryption)
        const deviceId = await fetchDeviceId()
        deviceIdRef.current = deviceId

        // Load encrypted auth from storage
        const stored = await authStorage.load(deviceId)
        if (stored) {
          setGlobalAuthState(stored)
        }

        // Fetch security schemes from the spec
        const schemesResponse = await fetch('/api/auth-schemes')
        if (schemesResponse.ok) {
          const data = await schemesResponse.json()
          setSpecSchemes(data.schemes || [])
          setSpecDefaultAuth(data.defaultAuth || null)
        }
      } catch (error) {
        console.error('[AuthProvider] Failed to initialize auth:', error)
        // Clear any potentially corrupted data
        authStorage.clear()
      } finally {
        setIsInitialized(true)
      }
    }

    initializeAuth()
  }, [])

  const setGlobalAuth = useCallback(async (auth: BrainfishRESTAuth) => {
    if (!deviceIdRef.current) {
      console.error('[AuthProvider] Cannot save auth: no device ID')
      return
    }
    setGlobalAuthState(auth)
    await authStorage.save(auth, deviceIdRef.current)
  }, [])

  const clearAuth = useCallback(() => {
    setGlobalAuthState(null)
    authStorage.clear()
  }, [])

  /**
   * Get effective auth for an endpoint
   * - If endpoint has 'inherit' auth type, use global auth
   * - If endpoint has 'none' auth type and global is set, use global
   * - Otherwise use endpoint's own auth
   */
  const getEffectiveAuth = useCallback(
    (endpointAuth: BrainfishRESTAuth): BrainfishRESTAuth => {
      // If endpoint explicitly wants to inherit, use global
      if (endpointAuth.authType === 'inherit') {
        return globalAuth || DEFAULT_AUTH
      }

      // If endpoint has no auth but global is configured, suggest global
      // but don't override - this allows explicit "no auth" endpoints
      if (endpointAuth.authType === 'none' && globalAuth && globalAuth.authType !== 'none') {
        // Return global auth but the UI should indicate it's using global
        return globalAuth
      }

      // Use endpoint's own auth
      return endpointAuth
    },
    [globalAuth]
  )

  /**
   * Check if an endpoint is using global auth
   */
  const isUsingGlobalAuth = useCallback(
    (endpointAuth: BrainfishRESTAuth): boolean => {
      if (!globalAuth || globalAuth.authType === 'none') {
        return false
      }

      // Using global if inheriting or if endpoint has no auth
      return (
        endpointAuth.authType === 'inherit' ||
        endpointAuth.authType === 'none'
      )
    },
    [globalAuth]
  )

  const isAuthenticated =
    globalAuth !== null && globalAuth.authType !== 'none'

  const value: AuthContextValue = {
    globalAuth,
    isAuthenticated,
    setGlobalAuth,
    clearAuth,
    getEffectiveAuth,
    isUsingGlobalAuth,
    specSchemes,
    specDefaultAuth,
  }

  // Don't render until we've checked sessionStorage
  if (!isInitialized) {
    return null
  }

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

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

/**
 * Get a human-readable label for auth type
 */
export function getAuthTypeLabel(auth: BrainfishRESTAuth | null | undefined): string {
  if (!auth || !auth.authType) {
    return 'Not configured'
  }
  
  switch (auth.authType) {
    case 'none':
      return 'No Auth'
    case 'inherit':
      return 'Inherited'
    case 'basic':
      return 'Basic Auth'
    case 'bearer':
      return 'Bearer Token'
    case 'api-key':
      return `API Key (${auth.key || 'key'})`
    case 'oauth-2':
      return 'OAuth 2.0'
    default:
      return 'Not configured'
  }
}

/**
 * Get a masked preview of auth value
 */
export function getAuthPreview(auth: BrainfishRESTAuth): string {
  switch (auth.authType) {
    case 'none':
    case 'inherit':
      return ''
    case 'basic':
      return auth.username ? `${auth.username}:****` : ''
    case 'bearer':
      return auth.token ? `${auth.token.slice(0, 8)}••••` : ''
    case 'api-key':
      return auth.value ? `${auth.value.slice(0, 4)}••••` : ''
    case 'oauth-2':
      return auth.grantTypeInfo.token
        ? `${auth.grantTypeInfo.token.slice(0, 8)}••••`
        : ''
    default:
      return ''
  }
}
