/**
 * API Client for communicating with Onebox daemon
 *
 * Provides methods for CLI commands to interact with running daemon via HTTP API
 */

import type {
  IService,
  IRegistry,
  IDnsRecord,
  ISslCertificate,
  IServiceDeployOptions,
} from '../types.ts';
import { getErrorMessage } from '../utils/error.ts';

export class OneboxApiClient {
  private baseUrl: string;
  private token?: string;

  constructor(port = 3000) {
    this.baseUrl = `http://localhost:${port}`;
  }

  /**
   * Check if daemon is reachable
   */
  async isReachable(): Promise<boolean> {
    try {
      const response = await fetch(`${this.baseUrl}/api/status`, {
        signal: AbortSignal.timeout(5000), // 5 second timeout
      });
      return response.ok;
    } catch {
      return false;
    }
  }

  // ============ Service Operations ============

  async deployService(config: IServiceDeployOptions): Promise<IService> {
    return await this.request<IService>('POST', '/api/services', config);
  }

  async removeService(name: string): Promise<void> {
    await this.request('DELETE', `/api/services/${name}`);
  }

  async startService(name: string): Promise<void> {
    await this.request('POST', `/api/services/${name}/start`);
  }

  async stopService(name: string): Promise<void> {
    await this.request('POST', `/api/services/${name}/stop`);
  }

  async restartService(name: string): Promise<void> {
    await this.request('POST', `/api/services/${name}/restart`);
  }

  async listServices(): Promise<IService[]> {
    return await this.request<IService[]>('GET', '/api/services');
  }

  async getServiceLogs(name: string, limit = 1000): Promise<string[]> {
    const result = await this.request<{ logs: string[] }>(
      'GET',
      `/api/services/${name}/logs?limit=${limit}`
    );
    return result.logs;
  }

  // ============ Registry Operations ============

  async addRegistry(url: string, username: string, password: string): Promise<void> {
    await this.request('POST', '/api/registries', { url, username, password });
  }

  async removeRegistry(url: string): Promise<void> {
    await this.request('DELETE', `/api/registries/${encodeURIComponent(url)}`);
  }

  async listRegistries(): Promise<IRegistry[]> {
    return await this.request<IRegistry[]>('GET', '/api/registries');
  }

  // ============ DNS Operations ============

  async addDnsRecord(domain: string): Promise<void> {
    await this.request('POST', '/api/dns', { domain });
  }

  async removeDnsRecord(domain: string): Promise<void> {
    await this.request('DELETE', `/api/dns/${domain}`);
  }

  async listDnsRecords(): Promise<IDnsRecord[]> {
    return await this.request<IDnsRecord[]>('GET', '/api/dns');
  }

  async syncDns(): Promise<void> {
    await this.request('POST', '/api/dns/sync');
  }

  // ============ SSL Operations ============

  async renewCertificate(domain?: string): Promise<void> {
    const path = domain ? `/api/ssl/renew/${domain}` : '/api/ssl/renew';
    await this.request('POST', path);
  }

  async listCertificates(): Promise<ISslCertificate[]> {
    return await this.request<ISslCertificate[]>('GET', '/api/ssl');
  }

  async forceRenewCertificate(domain: string): Promise<void> {
    await this.request('POST', `/api/ssl/renew/${domain}?force=true`);
  }

  // ============ Nginx Operations ============

  async reloadNginx(): Promise<void> {
    await this.request('POST', '/api/nginx/reload');
  }

  async testNginx(): Promise<{ success: boolean; output: string }> {
    return await this.request('POST', '/api/nginx/test');
  }

  async getNginxStatus(): Promise<{ status: string }> {
    return await this.request('GET', '/api/nginx/status');
  }

  // ============ Config Operations ============

  async getSettings(): Promise<Record<string, string>> {
    return await this.request<Record<string, string>>('GET', '/api/config');
  }

  async setSetting(key: string, value: string): Promise<void> {
    await this.request('POST', '/api/config', { key, value });
  }

  // ============ System Operations ============

  async getStatus(): Promise<{
    services: { total: number; running: number; stopped: number };
    uptime: number;
  }> {
    return await this.request('GET', '/api/status');
  }

  // ============ Helper Methods ============

  /**
   * Make HTTP request to daemon
   */
  private async request<T = unknown>(
    method: string,
    path: string,
    body?: unknown
  ): Promise<T> {
    const url = `${this.baseUrl}${path}`;

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

    if (this.token) {
      headers['Authorization'] = `Bearer ${this.token}`;
    }

    const options: RequestInit = {
      method,
      headers,
      signal: AbortSignal.timeout(30000), // 30 second timeout
    };

    if (body) {
      options.body = JSON.stringify(body);
    }

    try {
      const response = await fetch(url, options);

      if (!response.ok) {
        const errorData = await response.json().catch(() => ({ message: response.statusText }));
        throw new Error(errorData.message || `HTTP ${response.status}: ${response.statusText}`);
      }

      // For DELETE and some POST requests, there might be no content
      if (response.status === 204 || response.headers.get('content-length') === '0') {
        return undefined as T;
      }

      return await response.json();
    } catch (error) {
      if (error instanceof Error && error.name === 'TimeoutError') {
        throw new Error('Request timed out. Daemon might be unresponsive.');
      }
      throw error;
    }
  }

  /**
   * Set authentication token
   */
  setToken(token: string): void {
    this.token = token;
  }
}
