import { Injectable, Logger } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { exec } from 'child_process';
import { promisify } from 'util';
import { MetricSnapshotRepository } from '../repositories/metric-snapshot.repository';
import { MetricSnapshot } from '../../domain/entities/metric-snapshot.entity';

const execAsync = promisify(exec);

interface ServerTarget {
  id: string;
  host: string;
  sshPort?: number;
}

@Injectable()
export class SshCollector {
  private readonly logger = new Logger(SshCollector.name);
  private readonly sshUser: string;
  private readonly sshKeyPath: string;
  private readonly sshTimeoutSeconds: number;

  constructor(
    private readonly metricRepo: MetricSnapshotRepository,
    private readonly configService: ConfigService,
  ) {
    this.sshUser = this.configService.get<string>('SSH_USER', 'it0');
    this.sshKeyPath = this.configService.get<string>('SSH_KEY_PATH', '/etc/it0/ssh/id_ed25519');
    this.sshTimeoutSeconds = this.configService.get<number>('SSH_TIMEOUT_SECONDS', 10);
  }

  async collect(server: ServerTarget): Promise<MetricSnapshot[]> {
    const port = server.sshPort ?? 22;
    const sshCmd = [
      'ssh',
      '-o', 'StrictHostKeyChecking=no',
      '-o', `ConnectTimeout=${this.sshTimeoutSeconds}`,
      '-i', this.sshKeyPath,
      '-p', String(port),
      `${this.sshUser}@${server.host}`,
      `"uptime && free -m && df -h /"`,
    ].join(' ');

    try {
      const { stdout } = await execAsync(sshCmd, { timeout: (this.sshTimeoutSeconds + 5) * 1000 });
      const snapshots = this.parseOutput(server.id, stdout);

      for (const snapshot of snapshots) {
        await this.metricRepo.save(snapshot);
      }

      this.logger.debug(`Collected ${snapshots.length} metrics from ${server.host}`);
      return snapshots;
    } catch (error: any) {
      this.logger.error(`SSH collection failed for ${server.host}: ${error.message}`);
      return [];
    }
  }

  private parseOutput(serverId: string, output: string): MetricSnapshot[] {
    const now = new Date();
    const snapshots: MetricSnapshot[] = [];
    const lines = output.split('\n').map((l) => l.trim()).filter(Boolean);

    // Parse uptime load average (last token like "0.5, 0.3, 0.1")
    const uptimeLine = lines.find((l) => l.includes('load average'));
    if (uptimeLine) {
      const match = uptimeLine.match(/load average:\s*([\d.]+)/);
      if (match) {
        const snap = new MetricSnapshot();
        snap.serverId = serverId;
        snap.metricType = 'cpu_load';
        snap.value = parseFloat(match[1]);
        snap.recordedAt = now;
        snapshots.push(snap);
      }
    }

    // Parse free -m output: "Mem:  total  used  free ..."
    const memLine = lines.find((l) => l.startsWith('Mem:'));
    if (memLine) {
      const parts = memLine.split(/\s+/);
      const total = parseFloat(parts[1]);
      const used = parseFloat(parts[2]);
      if (total > 0) {
        const snap = new MetricSnapshot();
        snap.serverId = serverId;
        snap.metricType = 'memory_used';
        snap.value = Math.round((used / total) * 100);
        snap.recordedAt = now;
        snapshots.push(snap);
      }
    }

    // Parse df -h output: filesystem  size  used  avail  use%  mount
    const dfLine = lines.find((l) => l.includes('%') && l.endsWith('/'));
    if (dfLine) {
      const match = dfLine.match(/(\d+)%/);
      if (match) {
        const snap = new MetricSnapshot();
        snap.serverId = serverId;
        snap.metricType = 'disk_usage';
        snap.value = parseInt(match[1], 10);
        snap.recordedAt = now;
        snapshots.push(snap);
      }
    }

    return snapshots;
  }
}
