/**
 * API Client Configuration
 */
export interface ClientConfig {
  baseUrl: string;
  apiKey?: string;
  bearerToken?: string;
  headers?: Record<string, string>;
  onError?: (error: Error) => void;
}

/**
 * Create a typed API client
 *
 * @example
 * const client = createClient({
 *   baseUrl: 'https://api.example.com',
 *   apiKey: 'your-api-key',
 * });
 *
 * const users = await client.get('/users');
 * const newUser = await client.post('/users', { name: 'John' });
 */
export function createClient(config: ClientConfig) {
  const { baseUrl, apiKey, bearerToken, headers = {}, onError } = config;

  async function request<T>(
    method: string,
    path: string,
    options: {
      body?: unknown;
      params?: Record<string, string | number | boolean | undefined>;
      headers?: Record<string, string>;
    } = {}
  ): Promise<T> {
    const url = new URL(path, baseUrl);

    // Add query params
    if (options.params) {
      for (const [key, value] of Object.entries(options.params)) {
        if (value !== undefined) {
          url.searchParams.set(key, String(value));
        }
      }
    }

    // Build headers
    const requestHeaders: Record<string, string> = {
      'Content-Type': 'application/json',
      ...headers,
      ...options.headers,
    };

    if (apiKey) {
      requestHeaders['X-API-Key'] = apiKey;
    }
    if (bearerToken) {
      requestHeaders['Authorization'] = `Bearer ${bearerToken}`;
    }

    const response = await fetch(url.toString(), {
      method,
      headers: requestHeaders,
      body: options.body ? JSON.stringify(options.body) : undefined,
    });

    if (!response.ok) {
      const error = new Error(`API error: ${response.status}`);
      if (onError) {
        onError(error);
      }
      throw error;
    }

    // Handle no-content responses
    if (response.status === 204) {
      return undefined as T;
    }

    return response.json();
  }

  return {
    get: <T>(path: string, params?: Record<string, string | number | boolean | undefined>) =>
      request<T>('GET', path, { params }),
    post: <T>(path: string, body?: unknown) =>
      request<T>('POST', path, { body }),
    put: <T>(path: string, body?: unknown) =>
      request<T>('PUT', path, { body }),
    patch: <T>(path: string, body?: unknown) =>
      request<T>('PATCH', path, { body }),
    delete: <T>(path: string) =>
      request<T>('DELETE', path),
  };
}
