/**
 * Scraping Service SDK Client
 * 
 * Type-safe client for consuming the scraping service API
 * Provides methods for all endpoints with proper error handling
 */

import type {
  // Core types
  NovelDetails,
  ChapterData,
  NovelSearchResult,
  SiteConfiguration,
  HealthStatus,
  DatabaseInfo,
  DatabaseId,
  
  // Health types
  HealthOverview,
  ProxyHealthStatus,
  BrowserHealthStatus,
  ServiceStatus,
  
  // Request types
  SearchNovelsRequest,
  ScrapeChapterRequest,
  BatchScrapeRequest,
  ValidateUrlRequest,
  TestSiteConfigRequest,
  LatestNovelsRequest,
  PopularNovelsRequest,
  NovelDetailsRequest,
  
  // Response types
  ScrapingServiceResponse,
  PaginatedResponse,
} from './types';

import {
  fetchWithTimeout,
  parseJsonResponse,
  buildFullUrl,
  validateDatabaseId,
  logRequest,
  logResponse,
  withRetry,
  type RetryConfig,
} from './utils';

import {
  ValidationError,
} from './errors';

// ===== Client Configuration =====

export interface ScrapingServiceConfig {
  /** Base URL of the scraping service */
  baseUrl: string;
  
  /** Request timeout in milliseconds */
  timeout?: number;
  
  /** Retry configuration for failed requests */
  retry?: Partial<RetryConfig>;
  
  /** Enable debug logging */
  debug?: boolean;
}

// ===== Main Client Class =====

export class ScrapingServiceClient {
  private readonly config: Required<ScrapingServiceConfig>;

  constructor(config: ScrapingServiceConfig) {
    this.config = {
      timeout: 30000,
      retry: {},
      debug: false,
      ...config,
    };
    
    // Ensure base URL doesn't end with slash
    this.config.baseUrl = this.config.baseUrl.replace(/\/$/, '');
  }

  // ===== Database Operations =====

  /**
   * List available database scrapers
   */
  async listDatabases(): Promise<DatabaseInfo[]> {
    return this.makeRequest<ScrapingServiceResponse<DatabaseInfo[]>>({
      method: 'GET',
      path: '/api/v1/databases',
    }).then(response => response.results);
  }

  /**
   * Search novels in a database
   */
  async searchNovels(
    databaseId: DatabaseId,
    request: SearchNovelsRequest
  ): Promise<PaginatedResponse<NovelSearchResult>> {
    validateDatabaseId(databaseId);
    
    return this.makeRequest<PaginatedResponse<NovelSearchResult>>({
      method: 'GET',
      path: '/api/v1/databases/:id/search',
      params: { id: databaseId },
      query: request,
    });
  }

  /**
   * Get latest novels from a database
   */
  async getLatestNovels(
    databaseId: DatabaseId,
    request: LatestNovelsRequest = {}
  ): Promise<PaginatedResponse<NovelSearchResult>> {
    validateDatabaseId(databaseId);
    
    return this.makeRequest<PaginatedResponse<NovelSearchResult>>({
      method: 'GET',
      path: '/api/v1/databases/:id/latest',
      params: { id: databaseId },
      query: request,
    });
  }

  /**
   * Get popular novels from a database
   */
  async getPopularNovels(
    databaseId: DatabaseId,
    request: PopularNovelsRequest = {}
  ): Promise<PaginatedResponse<NovelSearchResult>> {
    validateDatabaseId(databaseId);
    
    return this.makeRequest<PaginatedResponse<NovelSearchResult>>({
      method: 'GET',
      path: '/api/v1/databases/:id/popular',
      params: { id: databaseId },
      query: request,
    });
  }

  /**
   * Get detailed novel information
   */
  async getNovelDetails(
    databaseId: DatabaseId,
    request: NovelDetailsRequest
  ): Promise<NovelDetails> {
    validateDatabaseId(databaseId);
    
    if (!request.url) {
      throw new ValidationError('Novel URL is required');
    }
    
    return this.makeRequest<ScrapingServiceResponse<NovelDetails>>({
      method: 'GET',
      path: '/api/v1/databases/:id/novel',
      params: { id: databaseId },
      query: request,
    }).then(response => response.results);
  }

  // ===== Chapter Operations =====

  /**
   * Scrape single chapter content
   */
  async scrapeChapter(request: ScrapeChapterRequest): Promise<ChapterData> {
    if (!request.url) {
      throw new ValidationError('Chapter URL is required');
    }
    
    return this.makeRequest<ScrapingServiceResponse<ChapterData>>({
      method: 'POST',
      path: '/api/v1/chapters/scrape',
      body: request,
    }).then(response => response.results);
  }

  /**
   * Scrape multiple chapters in batch
   */
  async scrapeChaptersBatch(request: BatchScrapeRequest): Promise<ChapterData[]> {
    if (!request.urls || request.urls.length === 0) {
      throw new ValidationError('At least one URL is required');
    }
    
    return this.makeRequest<ScrapingServiceResponse<ChapterData[]>>({
      method: 'POST',
      path: '/api/v1/chapters/batch',
      body: request,
    }).then(response => response.results);
  }

  /**
   * Validate if URL is supported for scraping
   */
  async validateChapterUrl(request: ValidateUrlRequest): Promise<{ supported: boolean; hostname: string }> {
    if (!request.url) {
      throw new ValidationError('URL is required');
    }
    
    return this.makeRequest<ScrapingServiceResponse<{ supported: boolean; hostname: string }>>({
      method: 'GET',
      path: '/api/v1/chapters/validate-url',
      query: request,
    }).then(response => response.results);
  }

  // ===== Site Configuration Operations =====

  /**
   * List all site configurations
   */
  async listSiteConfigurations(): Promise<SiteConfiguration[]> {
    return this.makeRequest<ScrapingServiceResponse<SiteConfiguration[]>>({
      method: 'GET',
      path: '/api/v1/sites',
    }).then(response => response.results);
  }

  /**
   * Get site configuration by hostname
   */
  async getSiteConfiguration(hostname: string): Promise<SiteConfiguration> {
    if (!hostname) {
      throw new ValidationError('Hostname is required');
    }
    
    return this.makeRequest<ScrapingServiceResponse<SiteConfiguration>>({
      method: 'GET',
      path: '/api/v1/sites/:hostname',
      params: { hostname },
    }).then(response => response.results);
  }

  /**
   * Update site configuration
   */
  async updateSiteConfiguration(
    hostname: string,
    update: Partial<SiteConfiguration>
  ): Promise<SiteConfiguration> {
    if (!hostname) {
      throw new ValidationError('Hostname is required');
    }
    
    return this.makeRequest<ScrapingServiceResponse<SiteConfiguration>>({
      method: 'PUT',
      path: '/api/v1/sites/:hostname',
      params: { hostname },
      body: update,
    }).then(response => response.results);
  }

  /**
   * Test site configuration
   */
  async testSiteConfiguration(
    hostname: string,
    request: TestSiteConfigRequest
  ): Promise<{ success: boolean; data?: ChapterData }> {
    if (!hostname) {
      throw new ValidationError('Hostname is required');
    }
    
    if (!request.testUrl) {
      throw new ValidationError('Test URL is required');
    }
    
    return this.makeRequest<ScrapingServiceResponse<{ success: boolean; data?: ChapterData }>>({
      method: 'POST',
      path: '/api/v1/sites/:hostname/test',
      params: { hostname },
      body: request,
    }).then(response => response.results);
  }

  // ===== Health Operations =====

  /**
   * Get overall service health status
   * @deprecated Use getHealthOverview() for comprehensive health data
   */
  async getHealthStatus(): Promise<HealthStatus> {
    return this.makeRequest<HealthStatus>({
      method: 'GET',
      path: '/api/v1/health',
    });
  }

  /**
   * Get comprehensive health overview for dashboards
   */
  async getHealthOverview(): Promise<HealthOverview> {
    return this.makeRequest<HealthOverview>({
      method: 'GET',
      path: '/api/v1/health/overview',
    });
  }

  /**
   * Get detailed proxy health status
   */
  async getProxyHealth(): Promise<ProxyHealthStatus> {
    return this.makeRequest<ProxyHealthStatus>({
      method: 'GET',
      path: '/api/v1/health/proxy',
    });
  }

  /**
   * Get browser manager health status
   */
  async getBrowserHealth(): Promise<BrowserHealthStatus> {
    return this.makeRequest<BrowserHealthStatus>({
      method: 'GET',
      path: '/api/v1/health/browser',
    });
  }

  // ===== Private Request Handler =====

  private async makeRequest<T>(options: {
    method: 'GET' | 'POST' | 'PUT' | 'DELETE';
    path: string;
    params?: Record<string, string>;
    query?: Record<string, unknown> | object;
    body?: unknown;
  }): Promise<T> {
    const { method, path, params, query, body } = options;
    
    // Build URL
    const url = buildFullUrl(this.config.baseUrl, path, params, query);
    
    // Prepare request options
    const fetchOptions: RequestInit & { timeout?: number } = {
      method,
      timeout: this.config.timeout,
    };
    
    // Add body for POST/PUT requests
    if (body && (method === 'POST' || method === 'PUT')) {
      fetchOptions.body = JSON.stringify(body);
    }
    
    // Log request if debug enabled
    if (this.config.debug) {
      logRequest(method, url, body);
    }
    
    // Make request with retry logic
    return withRetry(async () => {
      const response = await fetchWithTimeout(url, fetchOptions);
      
      // Log response if debug enabled
      if (this.config.debug) {
        logResponse(url, response.status);
      }
      
      return parseJsonResponse<T>(response);
    }, this.config.retry);
  }
}

// ===== Convenience Factory Function =====

/**
 * Create a new scraping service client
 */
export function createScrapingServiceClient(config: ScrapingServiceConfig): ScrapingServiceClient {
  return new ScrapingServiceClient(config);
}

// ===== Type Exports for Main App =====

export type {
  // Core types
  NovelDetails,
  ChapterData,
  NovelSearchResult,
  SiteConfiguration,
  HealthStatus,
  DatabaseInfo,
  DatabaseId,
  
  // Health types
  HealthOverview,
  ProxyHealthStatus,
  BrowserHealthStatus,
  ServiceStatus,
  ProxyProviderBreakdown,
  
  // Request types
  SearchNovelsRequest,
  ScrapeChapterRequest,
  BatchScrapeRequest,
  ValidateUrlRequest,
  TestSiteConfigRequest,
  LatestNovelsRequest,
  PopularNovelsRequest,
  NovelDetailsRequest,
  
  // Response types
  ScrapingServiceResponse,
  PaginatedResponse,
} from './types'; 