import {
  VspcListResponse,
  VspcOrganization,
  VspcBackupServer,
  VspcBackupJob,
  VspcBackupAgentJob,
  VspcProtectedWorkload,
  VspcRepository,
  VspcBackupAgent,
  VspcAlarm,
} from '@/lib/types/veeam';

export interface VeeamClientConfig {
  baseUrl: string;
  apiKey: string;
}

export class VeeamClient {
  private config: VeeamClientConfig;
  private requestTimestamps: number[] = [];
  private readonly RATE_LIMIT = 100; // requests per minute
  private readonly RATE_LIMIT_WINDOW = 60000; // 1 minute in milliseconds
  private readonly DEFAULT_PAGE_SIZE = 500;

  constructor(config: VeeamClientConfig) {
    this.config = config;
  }

  /**
   * Get authorization headers for VSPC API
   */
  private getAuthHeaders(): HeadersInit {
    return {
      'Authorization': `Bearer ${this.config.apiKey}`,
      'Accept': 'application/json',
      'Content-Type': 'application/json',
    };
  }

  /**
   * Check and enforce rate limiting
   */
  private async checkRateLimit(): Promise<void> {
    const now = Date.now();
    this.requestTimestamps = this.requestTimestamps.filter(
      (timestamp) => now - timestamp < this.RATE_LIMIT_WINDOW
    );

    if (this.requestTimestamps.length >= this.RATE_LIMIT) {
      const oldestInWindow = this.requestTimestamps[0];
      const waitMs = this.RATE_LIMIT_WINDOW - (now - oldestInWindow) + 100;
      console.warn(`Veeam VSPC rate limit reached (${this.RATE_LIMIT}/min), waiting ${waitMs}ms`);
      await new Promise(resolve => setTimeout(resolve, waitMs));
    }

    this.requestTimestamps.push(Date.now());
  }

  /**
   * Make a single API call with error handling and rate limiting
   */
  private async makeApiCall<T>(url: string): Promise<T> {
    await this.checkRateLimit();

    try {
      const response = await fetch(url, {
        method: 'GET',
        headers: this.getAuthHeaders(),
        // VSPC may use self-signed certs
        ...(process.env.NODE_TLS_REJECT_UNAUTHORIZED === '0' ? {} : {}),
      });

      if (!response.ok) {
        const errorText = await response.text();
        let errorDetail = errorText;
        try {
          const errorJson = JSON.parse(errorText);
          errorDetail = JSON.stringify(errorJson.errors || errorJson, null, 2);
        } catch {
          // keep raw text
        }
        console.error(`Veeam VSPC API error: ${response.status} ${response.statusText}`, errorDetail);
        throw new Error(`Veeam VSPC API request failed: ${response.status} ${response.statusText} - ${errorDetail}`);
      }

      const text = await response.text();
      if (!text) {
        return { meta: { pagingInfo: { total: 0, count: 0, offset: 0 } }, data: [] } as T;
      }

      return JSON.parse(text) as T;
    } catch (error) {
      if (error instanceof Error && error.message.startsWith('Veeam VSPC API request failed')) {
        throw error;
      }
      console.error('Veeam VSPC API call failed:', error);
      throw error;
    }
  }

  /**
   * Build a URL with query parameters
   */
  private buildUrl(path: string, params: Record<string, string | number> = {}): string {
    const baseUrl = this.config.baseUrl.replace(/\/$/, '');
    const fullUrl = `${baseUrl}/api/v3${path}`;
    const url = new URL(fullUrl);
    for (const [key, value] of Object.entries(params)) {
      url.searchParams.set(key, String(value));
    }
    return url.toString();
  }

  /**
   * Fetch all pages of a paginated VSPC API endpoint
   */
  private async fetchAllPages<T>(path: string, filter?: string): Promise<T[]> {
    const allItems: T[] = [];
    let offset = 0;
    let total = 0;

    do {
      const params: Record<string, string | number> = {
        offset,
        limit: this.DEFAULT_PAGE_SIZE,
      };
      if (filter) {
        params.filter = filter;
      }

      const url = this.buildUrl(path, params);
      const response = await this.makeApiCall<VspcListResponse<T>>(url);

      const pageData = response.data ?? [];
      if (pageData.length > 0) {
        allItems.push(...pageData);
      }

      total = response.meta?.pagingInfo?.total ?? 0;
      offset += this.DEFAULT_PAGE_SIZE;

      if (pageData.length > 0) {
        console.log(`Veeam VSPC: fetched ${allItems.length}/${total} from ${path}`);
      }

      // Safety: if page returned nothing and we haven't hit total, stop
      if (pageData.length === 0) break;
    } while (offset < total);

    return allItems;
  }

  // ============================================================================
  // Data Fetching Methods
  // ============================================================================

  /**
   * Fetch all organizations (tenants/companies)
   */
  async getOrganizations(): Promise<VspcOrganization[]> {
    console.log('Fetching Veeam VSPC organizations...');
    const orgs = await this.fetchAllPages<VspcOrganization>('/organizations');
    console.log(`Fetched ${orgs.length} Veeam organizations`);
    return orgs;
  }

  /**
   * Fetch all backup servers
   */
  async getBackupServers(): Promise<VspcBackupServer[]> {
    console.log('Fetching Veeam VSPC backup servers...');
    const servers = await this.fetchAllPages<VspcBackupServer>('/infrastructure/backupServers');
    console.log(`Fetched ${servers.length} Veeam backup servers`);
    return servers;
  }

  /**
   * Fetch all backup server jobs (VM-level backup jobs)
   */
  async getBackupJobs(filter?: string): Promise<VspcBackupJob[]> {
    console.log('Fetching Veeam VSPC backup server jobs...');
    const jobs = await this.fetchAllPages<VspcBackupJob>('/infrastructure/backupServers/jobs', filter);
    console.log(`Fetched ${jobs.length} Veeam backup server jobs`);
    return jobs;
  }

  /**
   * Fetch all backup agent jobs (workstation/physical server backup jobs)
   */
  async getBackupAgentJobs(filter?: string): Promise<VspcBackupAgentJob[]> {
    console.log('Fetching Veeam VSPC backup agent jobs...');
    const jobs = await this.fetchAllPages<VspcBackupAgentJob>('/infrastructure/backupAgents/jobs', filter);
    console.log(`Fetched ${jobs.length} Veeam backup agent jobs`);
    return jobs;
  }

  /**
   * Fetch all protected workloads (virtual machines)
   */
  async getProtectedWorkloads(): Promise<VspcProtectedWorkload[]> {
    console.log('Fetching Veeam VSPC protected workloads...');
    const workloads = await this.fetchAllPages<VspcProtectedWorkload>('/protectedWorkloads/virtualMachines');
    console.log(`Fetched ${workloads.length} Veeam protected workloads`);
    return workloads;
  }

  /**
   * Fetch all repositories
   */
  async getRepositories(): Promise<VspcRepository[]> {
    console.log('Fetching Veeam VSPC repositories...');
    const repos = await this.fetchAllPages<VspcRepository>('/infrastructure/backupServers/repositories');
    console.log(`Fetched ${repos.length} Veeam repositories`);
    return repos;
  }

  /**
   * Fetch all backup agents (Veeam agent installs on managed machines)
   */
  async getBackupAgents(): Promise<VspcBackupAgent[]> {
    console.log('Fetching Veeam VSPC backup agents...');
    const agents = await this.fetchAllPages<VspcBackupAgent>('/infrastructure/backupAgents');
    console.log(`Fetched ${agents.length} Veeam backup agents`);
    return agents;
  }

  /**
   * Fetch all active VSPC alarms
   */
  async getActiveAlarms(): Promise<VspcAlarm[]> {
    console.log('Fetching Veeam VSPC active alarms...');
    const alarms = await this.fetchAllPages<VspcAlarm>('/alarms/active');
    console.log(`Fetched ${alarms.length} Veeam active alarms`);
    return alarms;
  }

  /**
   * Test API connectivity by fetching a single organization
   */
  async testConnection(): Promise<boolean> {
    try {
      const url = this.buildUrl('/organizations', { limit: 1 });
      const response = await this.makeApiCall<VspcListResponse<VspcOrganization>>(url);
      const total = response.meta?.pagingInfo?.total ?? 0;
      console.log(`Veeam VSPC connection test successful: ${total} organizations available`);
      return true;
    } catch (error) {
      console.error('Veeam VSPC connection test failed:', error);
      return false;
    }
  }
}
