import type { AuthManager } from '../core/auth-manager'
import type { Session } from '../types'

export interface ApiConfig {
  baseUrl?: string
  defaultHeaders?: Record<string, string>
  timeout?: number
  retryAttempts?: number
  retryDelay?: number
}

export interface ApiResponse<T = any> {
  data: T
  status: number
  statusText: string
  headers: Record<string, string>
}

export interface ApiError {
  message: string
  status: number
  code?: string
  details?: any
}

export class AuthAwareApiClient {
  private authManager: AuthManager
  private config: ApiConfig
  private baseUrl: string

  constructor(authManager: AuthManager, config: ApiConfig = {}) {
    this.authManager = authManager
    this.config = {
      timeout: 30000,
      retryAttempts: 3,
      retryDelay: 1000,
      ...config,
    }
    this.baseUrl = config.baseUrl || (typeof window !== 'undefined' ? window.location.origin : '')
  }

  private async getAuthHeaders(): Promise<Record<string, string>> {
    const session = this.authManager.getSession()
    const headers: Record<string, string> = {
      'Content-Type': 'application/json',
      ...this.config.defaultHeaders,
    }

    if (session?.accessToken) {
      headers['Authorization'] = `Bearer ${session.accessToken}`
    }

    return headers
  }

  private async refreshTokenIfNeeded(): Promise<boolean> {
    const session = this.authManager.getSession()
    
    if (!session) {
      return false
    }

    const now = Date.now() / 1000
    const timeUntilExpiry = session.expiresAt - now
    
    if (timeUntilExpiry < 300) { // Refresh if less than 5 minutes remaining
      try {
        const newSession = await this.authManager.refreshSession()
        return !!newSession
      } catch (error) {
        return false
      }
    }
    
    return true
  }

  private async makeRequest<T>(
    endpoint: string,
    options: RequestInit = {}
  ): Promise<ApiResponse<T>> {
    const url = `${this.baseUrl}${endpoint}`
    const headers = await this.getAuthHeaders()

    const requestOptions: RequestInit = {
      ...options,
      headers: {
        ...headers,
        ...options.headers,
      },
    }

    let attempt = 0
    const maxAttempts = this.config.retryAttempts || 3

    while (attempt < maxAttempts) {
      try {
        const controller = new AbortController()
        const timeoutId = setTimeout(() => controller.abort(), this.config.timeout)

        const response = await fetch(url, {
          ...requestOptions,
          signal: controller.signal,
        })

        clearTimeout(timeoutId)

        if (response.status === 401) {
          const refreshed = await this.refreshTokenIfNeeded()
          if (refreshed && attempt < maxAttempts - 1) {
            const newHeaders = await this.getAuthHeaders()
            requestOptions.headers = {
              ...newHeaders,
              ...options.headers,
            }
            attempt++
            continue
          }
        }

        if (!response.ok) {
          throw new Error(`HTTP ${response.status}: ${response.statusText}`)
        }

        const data = await response.json()
        
        return {
          data,
          status: response.status,
          statusText: response.statusText,
          headers: Object.fromEntries(response.headers.entries()),
        }
      } catch (error) {
        if (attempt === maxAttempts - 1) {
          throw this.createApiError(error, endpoint)
        }
        
        await this.delay(this.config.retryDelay || 1000)
        attempt++
      }
    }

    throw this.createApiError(new Error('Max retry attempts reached'), endpoint)
  }

  private createApiError(error: any, endpoint: string): ApiError {
    return {
      message: error.message || 'An error occurred',
      status: error.status || 500,
      code: error.code || 'UNKNOWN_ERROR',
      details: {
        endpoint,
        originalError: error,
      },
    }
  }

  private delay(ms: number): Promise<void> {
    return new Promise(resolve => setTimeout(resolve, ms))
  }

  async get<T>(endpoint: string, options: RequestInit = {}): Promise<ApiResponse<T>> {
    return this.makeRequest<T>(endpoint, {
      ...options,
      method: 'GET',
    })
  }

  async post<T>(endpoint: string, data?: any, options: RequestInit = {}): Promise<ApiResponse<T>> {
    return this.makeRequest<T>(endpoint, {
      ...options,
      method: 'POST',
      body: data ? JSON.stringify(data) : undefined,
    })
  }

  async put<T>(endpoint: string, data?: any, options: RequestInit = {}): Promise<ApiResponse<T>> {
    return this.makeRequest<T>(endpoint, {
      ...options,
      method: 'PUT',
      body: data ? JSON.stringify(data) : undefined,
    })
  }

  async patch<T>(endpoint: string, data?: any, options: RequestInit = {}): Promise<ApiResponse<T>> {
    return this.makeRequest<T>(endpoint, {
      ...options,
      method: 'PATCH',
      body: data ? JSON.stringify(data) : undefined,
    })
  }

  async delete<T>(endpoint: string, options: RequestInit = {}): Promise<ApiResponse<T>> {
    return this.makeRequest<T>(endpoint, {
      ...options,
      method: 'DELETE',
    })
  }
}

export function createAuthAwareApiClient(authManager: AuthManager, config?: ApiConfig): AuthAwareApiClient {
  return new AuthAwareApiClient(authManager, config)
}