/**
 * HTTP Client implementation for React Native
 * 
 * This implementation properly implements the core SDK's HttpClient interface
 * and uses fetch API which is available in React Native.
 */

/**
 * Request options for HTTP client operations
 * 
 * @interface RequestOptions
 * @property {Record<string, string>} [headers] - HTTP headers to include
 * @property {Record<string, string>} [params] - URL parameters
 * @property {number} [timeout] - Request timeout in milliseconds
 * @property {'json' | 'blob' | 'text' | 'arraybuffer'} [responseType] - Expected response type
 */
export interface RequestOptions {
  headers?: Record<string, string>;
  params?: Record<string, string>;
  timeout?: number;
  responseType?: 'json' | 'blob' | 'text' | 'arraybuffer'; 
}

/**
 * HTTP Client interface for React Native
 * 
 * @interface HttpClient
 * @template T - Response type
 */
export interface HttpClient {
  get<T>(url: string, options?: RequestOptions): Promise<T>;
  post<T>(url: string, body: any, options?: RequestOptions): Promise<T>;
  put<T>(url: string, body: any, options?: RequestOptions): Promise<T>;
  delete<T>(url: string, options?: RequestOptions): Promise<T>;
}

export class ReactNativeHttpClient implements HttpClient {
  private baseURL?: string;

  constructor(baseURL?: string) {
    this.baseURL = baseURL;
  }

  async get<T = any>(url: string, options?: RequestOptions): Promise<T> {
    return this.request<T>('GET', url, undefined, options);
  }

  async post<T = any>(url: string, body?: any, options?: RequestOptions): Promise<T> {
    return this.request<T>('POST', url, body, options);
  }

  async put<T = any>(url: string, body?: any, options?: RequestOptions): Promise<T> {
    return this.request<T>('PUT', url, body, options);
  }

  async delete<T = any>(url: string, options?: RequestOptions): Promise<T> {
    return this.request<T>('DELETE', url, undefined, options);
  }

  private async request<T>(
    method: string,
    url: string,
    body?: any,
    options?: RequestOptions
  ): Promise<T> {
    const fullUrl = this.buildUrl(url, options?.params);
    
    const config: RequestInit = {
      method,
      headers: {
        'Content-Type': 'application/json',
        ...options?.headers,
      },
    };

    // Add timeout if specified
    if (options?.timeout) {
      const controller = new AbortController();
      config.signal = controller.signal;
      setTimeout(() => controller.abort(), options.timeout);
    }

    // Add body for POST/PUT requests
    if (body && (method === 'POST' || method === 'PUT')) {
      config.body = JSON.stringify(body);
    }

    try {
      const response = await fetch(fullUrl, config);
      
      if (!response.ok) {
        // Get raw error response for ErrorUtils to handle
        let errorText = '';
        try {
          errorText = await response.text();
        } catch (e) {
          // Ignore errors when reading error body
        }

        // Create simple error with HTTP status and raw response
        const httpError = new Error(`HTTP ${response.status}: ${response.statusText} - ${errorText}`);
        (httpError as any).status = response.status;
        
        throw httpError;
      }

      // Handle different response types
      switch (options?.responseType) {
        case 'text':
          return response.text() as Promise<T>;
        case 'blob':
          return response.blob() as Promise<T>;
        case 'arraybuffer':
          return response.arrayBuffer() as Promise<T>;
        case 'json':
        default:
          // Check if response has content
          const contentLength = response.headers.get('content-length');
          if (contentLength === '0' || response.status === 204) {
            return undefined as T;
          }
          return response.json();
      }
    } catch (error) {
      if (error instanceof Error && error.name === 'AbortError') {
        throw new Error(`Request timeout after ${options?.timeout}ms`);
      }
      throw error;
    }
  }

  private buildUrl(url: string, params?: Record<string, string>): string {
    const fullUrl = this.baseURL ? `${this.baseURL}${url}` : url;
    
    if (!params || Object.keys(params).length === 0) {
      return fullUrl;
    }

    const urlObj = new URL(fullUrl);
    Object.entries(params).forEach(([key, value]) => {
      urlObj.searchParams.append(key, value);
    });
    
    return urlObj.toString();
  }
}