// Proxmox VE Integration Service
// Integration with Proxmox VE API for container deployment

import { retryWithBackoff } from '@/shared/utils/retry';
import { logger } from '@/infrastructure/monitoring/logger';
import { proxmoxCircuitBreaker } from '@/shared/utils/circuit-breaker';

export interface ProxmoxConfig {
  host: string;
  port: number;
  username: string;
  password: string;
  realm?: string;
}

export interface ContainerSpec {
  vmid: number;
  hostname: string;
  cores: number;
  memory: number;
  disk: number;
  network: {
    ip: string;
    gateway: string;
    vlan?: number;
  };
  template: string;
  config: any;
}

export interface DeploymentResult {
  success: boolean;
  containers: Array<{
    name: string;
    vmid: number;
    status: string;
    ip?: string;
  }>;
  errors?: string[];
}

export class ProxmoxVEIntegration {
  private config: ProxmoxConfig;
  private apiBaseUrl: string;
  private token?: string;
  private tokenExpiry?: Date;

  constructor(config: ProxmoxConfig) {
    this.config = {
      realm: 'pam',
      ...config,
    };
    this.apiBaseUrl = `https://${config.host}:${config.port || 8006}/api2/json`;
  }

  /**
   * Authenticate with Proxmox VE
   */
  async authenticate(): Promise<void> {
    await proxmoxCircuitBreaker.execute(async () => {
      return await retryWithBackoff(
        async () => {
          const response = await fetch(`${this.apiBaseUrl}/access/ticket`, {
            method: 'POST',
            headers: {
              'Content-Type': 'application/x-www-form-urlencoded',
            },
            body: new URLSearchParams({
              username: `${this.config.username}@${this.config.realm}`,
              password: this.config.password,
            }),
          });

          if (!response.ok) {
            throw new Error(`Authentication failed: ${response.status} ${response.statusText}`);
          }

          const data = await response.json();
          if (data.data?.ticket) {
            this.token = data.data.ticket;
            this.tokenExpiry = new Date(Date.now() + 3600000); // 1 hour
            logger.info('Proxmox VE authentication successful', {
              host: this.config.host,
              username: this.config.username,
            });
          } else {
            throw new Error('Invalid authentication response');
          }
        },
        {
          maxRetries: 3,
          initialDelayMs: 1000,
          onRetry: (attempt, error) => {
            logger.warn('Proxmox authentication retry', {
              attempt,
              error: error.message,
            });
          },
        }
      );
    });
  }

  /**
   * Create LXC container
   */
  async createContainer(spec: ContainerSpec, node: string = 'pve'): Promise<{ vmid: number; status: string }> {
    await this.ensureAuthenticated();

    const containerConfig: any = {
      vmid: spec.vmid,
      hostname: spec.hostname,
      cores: spec.cores,
      memory: spec.memory,
      net0: `name=eth0,bridge=vmbr0,ip=${spec.network.ip}/24,gw=${spec.network.gateway}`,
      rootfs: `local-lvm:${spec.disk}`,
      ostype: 'ubuntu',
      template: spec.template,
      ...spec.config,
    };

    if (spec.network.vlan) {
      containerConfig.net0 = `name=eth0,bridge=vmbr0,ip=${spec.network.ip}/24,gw=${spec.network.gateway},tag=${spec.network.vlan}`;
    }

    const response = await this.proxmoxRequest('POST', `/nodes/${node}/lxc`, containerConfig);

    return {
      vmid: spec.vmid,
      status: 'created',
    };
  }

  /**
   * Configure container network
   */
  async configureContainerNetwork(vmid: number, network: ContainerSpec['network'], node: string = 'pve'): Promise<void> {
    await this.ensureAuthenticated();

    const networkConfig: any = {
      net0: `name=eth0,bridge=vmbr0,ip=${network.ip}/24,gw=${network.gateway}`,
    };

    if (network.vlan) {
      networkConfig.net0 = `name=eth0,bridge=vmbr0,ip=${network.ip}/24,gw=${network.gateway},tag=${network.vlan}`;
    }

    await this.proxmoxRequest('PUT', `/nodes/${node}/lxc/${vmid}/config`, networkConfig);
  }

  /**
   * Start container
   */
  async startContainer(vmid: number, node: string = 'pve'): Promise<void> {
    await this.ensureAuthenticated();

    await this.proxmoxRequest('POST', `/nodes/${node}/lxc/${vmid}/status/start`, {});
  }

  /**
   * Get container status
   */
  async getContainerStatus(vmid: number, node: string = 'pve'): Promise<{ status: string; ip?: string }> {
    await this.ensureAuthenticated();

    const response = await this.proxmoxRequest('GET', `/nodes/${node}/lxc/${vmid}/status/current`, null);
    
    const status = response.data?.status || 'unknown';
    const ip = response.data?.net0?.ip || undefined;

    return {
      status,
      ip,
    };
  }

  /**
   * Deploy IRU containers
   */
  async deployIRUContainers(
    containers: ContainerSpec[],
    node?: string
  ): Promise<DeploymentResult> {
    await this.ensureAuthenticated();

    const results: DeploymentResult['containers'] = [];
    const errors: string[] = [];

    for (const container of containers) {
      try {
        // Create container
        const created = await this.createContainer(container);
        
        // Configure network
        await this.configureContainerNetwork(created.vmid, container.network);
        
        // Start container
        await this.startContainer(created.vmid);
        
        // Get status
        const status = await this.getContainerStatus(created.vmid);

        results.push({
          name: container.hostname,
          vmid: created.vmid,
          status: status.status,
          ip: status.ip,
        });
      } catch (error) {
        errors.push(`Failed to deploy ${container.hostname}: ${error instanceof Error ? error.message : 'Unknown error'}`);
      }
    }

    return {
      success: errors.length === 0,
      containers: results,
      errors: errors.length > 0 ? errors : undefined,
    };
  }

  /**
   * Ensure authenticated
   */
  private async ensureAuthenticated(): Promise<void> {
    if (!this.token || !this.tokenExpiry || this.tokenExpiry < new Date()) {
      await this.authenticate();
    }
  }

  /**
   * Make Proxmox VE API request with retry
   */
  private async proxmoxRequest(method: string, path: string, body: any): Promise<any> {
    await this.ensureAuthenticated();

    return await proxmoxCircuitBreaker.execute(async () => {
      return await retryWithBackoff(
        async () => {
          const url = `${this.apiBaseUrl}${path}`;
          const options: RequestInit = {
            method,
            headers: {
              'Cookie': `PVEAuthCookie=${this.token}`,
            },
          };

          if (body && method !== 'GET') {
            options.headers = {
              ...options.headers,
              'Content-Type': 'application/json',
            };
            options.body = JSON.stringify(body);
          }

          const response = await fetch(url, options);

          if (!response.ok) {
            const errorText = await response.text();
            throw new Error(`Proxmox API error: ${response.status} ${response.statusText} - ${errorText}`);
          }

          return await response.json();
        },
        {
          maxRetries: 3,
          initialDelayMs: 1000,
          onRetry: (attempt, error) => {
            logger.warn('Proxmox API request retry', {
              method,
              path,
              attempt,
              error: error.message,
            });
          },
        }
      );
    });
  }
}

// Validate required Proxmox environment variables
function getProxmoxConfig(): ProxmoxConfig {
  const host = process.env.PROXMOX_HOST;
  const username = process.env.PROXMOX_USERNAME;
  const password = process.env.PROXMOX_PASSWORD;

  if (process.env.NODE_ENV === 'production') {
    if (!host) {
      throw new Error('PROXMOX_HOST environment variable is required in production');
    }
    if (!username) {
      throw new Error('PROXMOX_USERNAME environment variable is required in production');
    }
    if (!password) {
      throw new Error('PROXMOX_PASSWORD environment variable is required in production');
    }
  }

  return {
    host: host || 'localhost',
    port: parseInt(process.env.PROXMOX_PORT || '8006'),
    username: username || 'root',
    password: password || '',
    realm: process.env.PROXMOX_REALM || 'pam',
  };
}

export const proxmoxVEIntegration = new ProxmoxVEIntegration(getProxmoxConfig());
