/**
 * Basecamp 4 API Client
 * Handles authentication, pagination, error handling
 */

import axios, { AxiosInstance, AxiosResponse, AxiosError } from 'axios';
import type {
  BasecampConfig,
  PaginationLinks,
  BasecampError,
} from '../types/index.js';

export class BasecampClient {
  private client: AxiosInstance;
  private accountId: string;
  private userAgent: string;

  constructor(config: BasecampConfig) {
    this.accountId = config.accountId;
    this.userAgent = config.userAgent || 'MCPEngine Basecamp MCP Server (https://github.com/BusyBee3333/mcpengine)';

    this.client = axios.create({
      baseURL: `https://3.basecampapi.com/${this.accountId}`,
      headers: {
        'Authorization': `Bearer ${config.accessToken}`,
        'User-Agent': this.userAgent,
        'Content-Type': 'application/json',
      },
      timeout: 30000,
    });

    // Response interceptor for error handling
    this.client.interceptors.response.use(
      (response) => response,
      (error: AxiosError<BasecampError>) => {
        if (error.response) {
          const status = error.response.status;
          const data = error.response.data;
          
          switch (status) {
            case 401:
              throw new Error('Unauthorized: Invalid or expired access token');
            case 403:
              throw new Error('Forbidden: You do not have permission to access this resource');
            case 404:
              throw new Error('Not Found: The requested resource does not exist');
            case 429:
              throw new Error('Rate Limited: Too many requests, please try again later');
            case 500:
            case 502:
            case 503:
              throw new Error('Basecamp server error: Please try again later');
            default:
              throw new Error(data?.error || data?.message || `API Error: ${status}`);
          }
        } else if (error.request) {
          throw new Error('Network error: Unable to connect to Basecamp API');
        } else {
          throw new Error(`Request failed: ${error.message}`);
        }
      }
    );
  }

  /**
   * Parse pagination links from Link header
   */
  private parseLinkHeader(linkHeader?: string): PaginationLinks {
    if (!linkHeader) return {};

    const links: PaginationLinks = {};
    const parts = linkHeader.split(',');

    for (const part of parts) {
      const match = part.match(/<([^>]+)>;\s*rel="([^"]+)"/);
      if (match) {
        const [, url, rel] = match;
        if (rel === 'next') links.next = url;
        if (rel === 'prev') links.prev = url;
      }
    }

    return links;
  }

  /**
   * GET request with pagination support
   */
  async get<T>(path: string, params?: Record<string, any>): Promise<{ data: T; links: PaginationLinks }> {
    const response = await this.client.get<T>(path, { params });
    const links = this.parseLinkHeader(response.headers['link']);
    return { data: response.data, links };
  }

  /**
   * POST request
   */
  async post<T>(path: string, data?: any): Promise<T> {
    const response = await this.client.post<T>(path, data);
    return response.data;
  }

  /**
   * PUT request
   */
  async put<T>(path: string, data?: any): Promise<T> {
    const response = await this.client.put<T>(path, data);
    return response.data;
  }

  /**
   * PATCH request
   */
  async patch<T>(path: string, data?: any): Promise<T> {
    const response = await this.client.patch<T>(path, data);
    return response.data;
  }

  /**
   * DELETE request
   */
  async delete(path: string): Promise<void> {
    await this.client.delete(path);
  }

  /**
   * GET all pages (auto-pagination)
   */
  async getAllPages<T>(path: string, params?: Record<string, any>): Promise<T[]> {
    const results: T[] = [];
    let currentUrl: string | undefined = path;
    
    while (currentUrl) {
      const response = await this.client.get<T[]>(currentUrl, currentUrl === path ? { params } : undefined);
      const data = response.data;
      
      if (Array.isArray(data)) {
        results.push(...data);
      }
      
      const links = this.parseLinkHeader(response.headers['link']);
      currentUrl = links.next;
    }
    
    return results;
  }

  /**
   * Upload file (multipart/form-data)
   * Note: File uploads require special handling in MCP context
   */
  async uploadFile(path: string, fileData: any): Promise<any> {
    // File upload implementation would require special handling
    // for multipart/form-data in the MCP context
    throw new Error('File upload functionality requires special MCP handling');
  }

  // Helper methods for common endpoints
  getAccountId(): string {
    return this.accountId;
  }

  getUserAgent(): string {
    return this.userAgent;
  }
}
