import type {
  GustoConfig,
  GustoError,
  PaginationParams,
  PaginatedResponse,
} from '../types/index.js';

export class GustoClient {
  private apiToken: string;
  private baseUrl: string;

  constructor(config: GustoConfig) {
    this.apiToken = config.apiToken;
    this.baseUrl = config.baseUrl || 'https://api.gusto.com/v1';
  }

  private getHeaders(): HeadersInit {
    return {
      'Authorization': `Bearer ${this.apiToken}`,
      'Content-Type': 'application/json',
      'Accept': 'application/json',
    };
  }

  private async handleResponse<T>(response: Response): Promise<T> {
    if (!response.ok) {
      const errorBody = await response.text();
      let errorMessage = `Gusto API error: ${response.status} ${response.statusText}`;
      
      try {
        const errorJson = JSON.parse(errorBody);
        errorMessage = errorJson.message || errorJson.error || errorMessage;
      } catch {
        if (errorBody) {
          errorMessage = errorBody;
        }
      }

      const error: GustoError = {
        message: errorMessage,
        status: response.status,
      };
      throw error;
    }

    const text = await response.text();
    if (!text) {
      return {} as T;
    }

    try {
      return JSON.parse(text) as T;
    } catch {
      return text as unknown as T;
    }
  }

  async get<T>(endpoint: string, params?: Record<string, any>): Promise<T> {
    const url = new URL(`${this.baseUrl}${endpoint}`);
    
    if (params) {
      Object.entries(params).forEach(([key, value]) => {
        if (value !== undefined && value !== null) {
          url.searchParams.append(key, String(value));
        }
      });
    }

    const response = await fetch(url.toString(), {
      method: 'GET',
      headers: this.getHeaders(),
    });

    return this.handleResponse<T>(response);
  }

  async post<T>(endpoint: string, data?: any): Promise<T> {
    const response = await fetch(`${this.baseUrl}${endpoint}`, {
      method: 'POST',
      headers: this.getHeaders(),
      body: data ? JSON.stringify(data) : undefined,
    });

    return this.handleResponse<T>(response);
  }

  async put<T>(endpoint: string, data?: any): Promise<T> {
    const response = await fetch(`${this.baseUrl}${endpoint}`, {
      method: 'PUT',
      headers: this.getHeaders(),
      body: data ? JSON.stringify(data) : undefined,
    });

    return this.handleResponse<T>(response);
  }

  async delete<T>(endpoint: string): Promise<T> {
    const response = await fetch(`${this.baseUrl}${endpoint}`, {
      method: 'DELETE',
      headers: this.getHeaders(),
    });

    return this.handleResponse<T>(response);
  }

  // Pagination helper
  async getPaginated<T>(
    endpoint: string,
    params?: PaginationParams & Record<string, any>
  ): Promise<PaginatedResponse<T>> {
    const page = params?.page || 1;
    const per = params?.per || 25;
    
    const queryParams = {
      ...params,
      page,
      per,
    };

    const response = await this.get<T[]>(endpoint, queryParams);
    
    // Gusto returns arrays directly; we'll wrap in pagination metadata
    return {
      data: response,
      page,
      per,
    };
  }

  // Fetch all pages automatically
  async getAllPaginated<T>(
    endpoint: string,
    params?: Record<string, any>
  ): Promise<T[]> {
    const allResults: T[] = [];
    let page = 1;
    const per = 100; // Max per page

    while (true) {
      const response = await this.getPaginated<T>(endpoint, {
        ...params,
        page,
        per,
      });

      allResults.push(...response.data);

      // If we got fewer results than requested, we're done
      if (response.data.length < per) {
        break;
      }

      page++;
    }

    return allResults;
  }
}
