import type { ApiError } from '@/types/api'

/**
 * BoostBot API Client
 * Handles all REST API communication with the WordPress backend
 */
class ApiClient {
  private baseUrl: string
  private nonce: string
  private isRefreshingNonce = false
  private refreshNoncePromise: Promise<string> | null = null
  
  constructor() {
    this.baseUrl = window.boostbotAdmin?.apiUrl || '/wp-json/boostbot/v1/'
    this.nonce = window.boostbotAdmin?.nonce || ''
  }
  
  /**
   * Build full URL with query parameters
   */
  private buildUrl(endpoint: string, params?: Record<string, unknown>): string {
    // Remove leading slash from endpoint to avoid double slashes
    const cleanEndpoint = endpoint.startsWith('/') ? endpoint.slice(1) : endpoint
    // Ensure baseUrl ends with slash
    const base = this.baseUrl.endsWith('/') ? this.baseUrl : this.baseUrl + '/'
    const url = new URL(base + cleanEndpoint, window.location.origin)
    
    if (params) {
      Object.entries(params).forEach(([key, value]) => {
        if (value !== undefined && value !== null && value !== '') {
          url.searchParams.append(key, String(value))
        }
      })
    }
    
    return url.toString()
  }
  
  /**
   * Get request headers
   */
  private getHeaders(): HeadersInit {
    return {
      'Content-Type': 'application/json',
      'X-WP-Nonce': this.nonce,
    }
  }
  
  /**
   * Refresh the nonce when it expires (403 errors)
   */
  private async refreshNonce(): Promise<string> {
    // If already refreshing, wait for the existing promise
    if (this.isRefreshingNonce && this.refreshNoncePromise) {
      return this.refreshNoncePromise
    }
    
    this.isRefreshingNonce = true
    this.refreshNoncePromise = (async () => {
      try {
        const url = this.buildUrl('nonce/refresh')
        const response = await fetch(url, {
          method: 'GET',
          credentials: 'same-origin',
        })
        
        if (!response.ok) {
          throw new Error('Failed to refresh nonce')
        }
        
        const data = await response.json()
        if (data.success && data.nonce) {
          this.nonce = data.nonce
          // Also update the global window object
          if (window.boostbotAdmin) {
            window.boostbotAdmin.nonce = data.nonce
          }
          console.log('🔄 Nonce refreshed successfully')
          return this.nonce
        }
        
        throw new Error('Invalid nonce response')
      } finally {
        this.isRefreshingNonce = false
        this.refreshNoncePromise = null
      }
    })()
    
    return this.refreshNoncePromise
  }
  
  /**
   * Handle API response with automatic nonce refresh on 403
   */
  private async handleResponse<T>(
    response: Response, 
    retryFn?: () => Promise<Response>,
    hasRetried = false
  ): Promise<T> {
    // If we get a 403 and haven't retried yet, try refreshing the nonce
    if (response.status === 403 && !hasRetried && retryFn) {
      console.log('⚠️ Got 403, attempting to refresh nonce...')
      try {
        await this.refreshNonce()
        // Retry the request with the new nonce
        const newResponse = await retryFn()
        return this.handleResponse<T>(newResponse, undefined, true)
      } catch (refreshError) {
        console.error('Failed to refresh nonce:', refreshError)
        // Fall through to regular error handling
      }
    }
    
    if (!response.ok) {
      // Some WP errors respond with empty body or non-JSON. Prefer parsing JSON,
      // but fall back to raw text so callers can see the real message.
      const raw = await response.text().catch(() => '')

      let parsed: any = null
      if (raw) {
        try {
          parsed = JSON.parse(raw)
        } catch {
          parsed = null
        }
      }

      const error: ApiError =
        parsed && typeof parsed === 'object' && typeof parsed.message === 'string'
          ? parsed
          : {
              code: 'http_error',
              message: raw || `Request failed (${response.status})`,
              data: { status: response.status },
            }

      throw error
    }

    // Some endpoints might return empty body (204 or similar)
    const okRaw = await response.text().catch(() => '')
    if (!okRaw) return {} as T
    try {
      return JSON.parse(okRaw) as T
    } catch {
      // Last-resort: keep behavior predictable
      return {} as T
    }
  }
  
  /**
   * GET request
   */
  async get<T>(endpoint: string, params?: Record<string, unknown>): Promise<T> {
    const url = this.buildUrl(endpoint, params)
    
    const makeRequest = () => fetch(url, {
      method: 'GET',
      headers: this.getHeaders(),
      credentials: 'same-origin',
    })
    
    const response = await makeRequest()
    return this.handleResponse<T>(response, makeRequest)
  }
  
  /**
   * POST request
   */
  async post<T>(endpoint: string, data?: unknown): Promise<T> {
    const url = this.buildUrl(endpoint)
    
    const makeRequest = () => fetch(url, {
      method: 'POST',
      headers: this.getHeaders(),
      credentials: 'same-origin',
      body: data ? JSON.stringify(data) : undefined,
    })
    
    const response = await makeRequest()
    return this.handleResponse<T>(response, makeRequest)
  }
  
  /**
   * PUT request
   */
  async put<T>(endpoint: string, data?: unknown): Promise<T> {
    const url = this.buildUrl(endpoint)
    
    const makeRequest = () => fetch(url, {
      method: 'PUT',
      headers: this.getHeaders(),
      credentials: 'same-origin',
      body: data ? JSON.stringify(data) : undefined,
    })
    
    const response = await makeRequest()
    return this.handleResponse<T>(response, makeRequest)
  }
  
  /**
   * DELETE request
   */
  async delete<T>(endpoint: string, data?: unknown): Promise<T> {
    const url = this.buildUrl(endpoint)
    
    const makeRequest = () => fetch(url, {
      method: 'DELETE',
      headers: this.getHeaders(),
      credentials: 'same-origin',
      body: data ? JSON.stringify(data) : undefined,
    })
    
    const response = await makeRequest()
    return this.handleResponse<T>(response, makeRequest)
  }

  /**
   * Upload file (multipart/form-data)
   */
  async upload<T>(endpoint: string, formData: FormData): Promise<T> {
    const url = this.buildUrl(endpoint)
    
    const makeRequest = () => fetch(url, {
      method: 'POST',
      headers: {
        'X-WP-Nonce': this.nonce,
        // Don't set Content-Type - browser will set it with boundary
      },
      credentials: 'same-origin',
      body: formData,
    })
    
    const response = await makeRequest()
    return this.handleResponse<T>(response, makeRequest)
  }
}

// Export singleton instance
export const apiClient = new ApiClient()
