/**
 * Synor Hosting SDK Client
 * Decentralized web hosting with domain management, DNS, and deployments.
 */

import type {
  HostingConfig,
  Domain,
  DomainRecord,
  RegisterDomainOptions,
  DomainAvailability,
  DnsRecord,
  DnsZone,
  Deployment,
  DeployOptions,
  DeploymentStats,
  Certificate,
  ProvisionSslOptions,
  SiteConfig,
  AnalyticsData,
  AnalyticsOptions,
} from './types';
import { HostingError } from './types';

const DEFAULT_CONFIG = {
  endpoint: 'https://hosting.synor.io/v1',
  network: 'mainnet' as const,
  timeout: 60000,
  retries: 3,
  debug: false,
};

/**
 * Synor Hosting SDK Client
 *
 * Provides domain registration, DNS management, site deployment, and SSL provisioning.
 *
 * @example
 * ```typescript
 * const hosting = new SynorHosting({ apiKey: 'your-api-key' });
 *
 * // Register a domain
 * const domain = await hosting.registerDomain('mysite.synor');
 *
 * // Deploy a site from IPFS CID
 * const deployment = await hosting.deploy('Qm...', { domain: 'mysite.synor' });
 *
 * // Provision SSL
 * const cert = await hosting.provisionSsl('mysite.synor');
 * ```
 */
export class SynorHosting {
  private config: Required<HostingConfig>;
  private closed = false;

  constructor(config: HostingConfig) {
    this.config = {
      ...DEFAULT_CONFIG,
      ...config,
    };

    if (!this.config.apiKey) {
      throw new HostingError('API key is required');
    }
  }

  // ==================== Domain Operations ====================

  /**
   * Check domain availability
   */
  async checkAvailability(name: string): Promise<DomainAvailability> {
    return this.request<DomainAvailability>(`/domains/check/${encodeURIComponent(name)}`);
  }

  /**
   * Register a new domain
   */
  async registerDomain(name: string, options?: RegisterDomainOptions): Promise<Domain> {
    return this.request<Domain>('/domains', {
      method: 'POST',
      body: { name, ...options },
    });
  }

  /**
   * Get domain information
   */
  async getDomain(name: string): Promise<Domain> {
    return this.request<Domain>(`/domains/${encodeURIComponent(name)}`);
  }

  /**
   * List all domains
   */
  async listDomains(): Promise<Domain[]> {
    const response = await this.request<{ domains: Domain[] }>('/domains');
    return response.domains;
  }

  /**
   * Update domain record (for IPFS/IPNS resolution)
   */
  async updateDomainRecord(name: string, record: DomainRecord): Promise<Domain> {
    return this.request<Domain>(`/domains/${encodeURIComponent(name)}/record`, {
      method: 'PUT',
      body: record,
    });
  }

  /**
   * Resolve a domain to its record
   */
  async resolveDomain(name: string): Promise<DomainRecord> {
    return this.request<DomainRecord>(`/domains/${encodeURIComponent(name)}/resolve`);
  }

  /**
   * Renew a domain
   */
  async renewDomain(name: string, years: number = 1): Promise<Domain> {
    return this.request<Domain>(`/domains/${encodeURIComponent(name)}/renew`, {
      method: 'POST',
      body: { years },
    });
  }

  /**
   * Transfer domain ownership
   */
  async transferDomain(name: string, newOwner: string): Promise<Domain> {
    return this.request<Domain>(`/domains/${encodeURIComponent(name)}/transfer`, {
      method: 'POST',
      body: { new_owner: newOwner },
    });
  }

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

  /**
   * Get DNS zone for a domain
   */
  async getDnsZone(domain: string): Promise<DnsZone> {
    return this.request<DnsZone>(`/dns/${encodeURIComponent(domain)}`);
  }

  /**
   * Set DNS records for a domain
   */
  async setDnsRecords(domain: string, records: DnsRecord[]): Promise<DnsZone> {
    return this.request<DnsZone>(`/dns/${encodeURIComponent(domain)}`, {
      method: 'PUT',
      body: { records },
    });
  }

  /**
   * Add a DNS record
   */
  async addDnsRecord(domain: string, record: DnsRecord): Promise<DnsZone> {
    return this.request<DnsZone>(`/dns/${encodeURIComponent(domain)}/records`, {
      method: 'POST',
      body: record,
    });
  }

  /**
   * Delete a DNS record
   */
  async deleteDnsRecord(domain: string, recordType: string, name: string): Promise<DnsZone> {
    return this.request<DnsZone>(
      `/dns/${encodeURIComponent(domain)}/records/${recordType}/${encodeURIComponent(name)}`,
      { method: 'DELETE' }
    );
  }

  // ==================== Deployment Operations ====================

  /**
   * Deploy a site from CID
   */
  async deploy(cid: string, options?: DeployOptions): Promise<Deployment> {
    return this.request<Deployment>('/deployments', {
      method: 'POST',
      body: { cid, ...options },
    });
  }

  /**
   * Get deployment by ID
   */
  async getDeployment(id: string): Promise<Deployment> {
    return this.request<Deployment>(`/deployments/${encodeURIComponent(id)}`);
  }

  /**
   * List deployments
   */
  async listDeployments(domain?: string): Promise<Deployment[]> {
    const query = domain ? `?domain=${encodeURIComponent(domain)}` : '';
    const response = await this.request<{ deployments: Deployment[] }>(`/deployments${query}`);
    return response.deployments;
  }

  /**
   * Rollback to a previous deployment
   */
  async rollback(domain: string, deploymentId: string): Promise<Deployment> {
    return this.request<Deployment>(`/deployments/${encodeURIComponent(deploymentId)}/rollback`, {
      method: 'POST',
      body: { domain },
    });
  }

  /**
   * Delete a deployment
   */
  async deleteDeployment(id: string): Promise<void> {
    await this.request(`/deployments/${encodeURIComponent(id)}`, { method: 'DELETE' });
  }

  /**
   * Get deployment stats
   */
  async getDeploymentStats(id: string, period: string = '24h'): Promise<DeploymentStats> {
    return this.request<DeploymentStats>(
      `/deployments/${encodeURIComponent(id)}/stats?period=${period}`
    );
  }

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

  /**
   * Provision SSL certificate
   */
  async provisionSsl(domain: string, options?: ProvisionSslOptions): Promise<Certificate> {
    return this.request<Certificate>(`/ssl/${encodeURIComponent(domain)}`, {
      method: 'POST',
      body: options || {},
    });
  }

  /**
   * Get certificate status
   */
  async getCertificate(domain: string): Promise<Certificate> {
    return this.request<Certificate>(`/ssl/${encodeURIComponent(domain)}`);
  }

  /**
   * Renew SSL certificate
   */
  async renewCertificate(domain: string): Promise<Certificate> {
    return this.request<Certificate>(`/ssl/${encodeURIComponent(domain)}/renew`, {
      method: 'POST',
    });
  }

  /**
   * Delete/revoke SSL certificate
   */
  async deleteCertificate(domain: string): Promise<void> {
    await this.request(`/ssl/${encodeURIComponent(domain)}`, { method: 'DELETE' });
  }

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

  /**
   * Get site configuration
   */
  async getSiteConfig(domain: string): Promise<SiteConfig> {
    return this.request<SiteConfig>(`/sites/${encodeURIComponent(domain)}/config`);
  }

  /**
   * Update site configuration
   */
  async updateSiteConfig(domain: string, config: Partial<SiteConfig>): Promise<SiteConfig> {
    return this.request<SiteConfig>(`/sites/${encodeURIComponent(domain)}/config`, {
      method: 'PATCH',
      body: config,
    });
  }

  /**
   * Purge CDN cache
   */
  async purgeCache(domain: string, paths?: string[]): Promise<{ purged: number }> {
    return this.request<{ purged: number }>(`/sites/${encodeURIComponent(domain)}/cache`, {
      method: 'DELETE',
      body: paths ? { paths } : undefined,
    });
  }

  // ==================== Analytics ====================

  /**
   * Get site analytics
   */
  async getAnalytics(domain: string, options?: AnalyticsOptions): Promise<AnalyticsData> {
    const params = new URLSearchParams();
    if (options?.period) params.set('period', options.period);
    if (options?.startDate) params.set('start', options.startDate);
    if (options?.endDate) params.set('end', options.endDate);

    const query = params.toString() ? `?${params}` : '';
    return this.request<AnalyticsData>(`/sites/${encodeURIComponent(domain)}/analytics${query}`);
  }

  // ==================== Lifecycle ====================

  /**
   * Close the client
   */
  close(): void {
    this.closed = true;
  }

  /**
   * Check if the client is closed
   */
  isClosed(): boolean {
    return this.closed;
  }

  /**
   * Health check
   */
  async healthCheck(): Promise<boolean> {
    try {
      const response = await this.request<{ status: string }>('/health');
      return response.status === 'healthy';
    } catch {
      return false;
    }
  }

  /**
   * Internal request method
   */
  private async request<T>(
    path: string,
    options: { method?: string; body?: unknown } = {}
  ): Promise<T> {
    if (this.closed) {
      throw new HostingError('Client has been closed');
    }

    const { method = 'GET', body } = options;
    let lastError: Error | null = null;

    for (let attempt = 0; attempt < this.config.retries; attempt++) {
      try {
        const controller = new AbortController();
        const timeoutId = setTimeout(() => controller.abort(), this.config.timeout);

        const response = await fetch(`${this.config.endpoint}${path}`, {
          method,
          headers: {
            'Authorization': `Bearer ${this.config.apiKey}`,
            'Content-Type': 'application/json',
            'X-SDK-Version': 'js/0.1.0',
          },
          body: body ? JSON.stringify(body) : undefined,
          signal: controller.signal,
        });

        clearTimeout(timeoutId);

        if (!response.ok) {
          const errorData = await response.json().catch(() => ({}));
          throw new HostingError(
            errorData.message || errorData.error || `HTTP ${response.status}`,
            errorData.code,
            response.status
          );
        }

        const text = await response.text();
        return text ? JSON.parse(text) : ({} as T);
      } catch (error) {
        lastError = error instanceof Error ? error : new Error(String(error));

        if (this.config.debug) {
          console.error(`Attempt ${attempt + 1} failed:`, lastError.message);
        }

        if (attempt < this.config.retries - 1) {
          await new Promise(resolve => setTimeout(resolve, (attempt + 1) * 1000));
        }
      }
    }

    throw lastError || new HostingError('Unknown error after retries');
  }
}
