import { exec } from 'child_process';
import { promisify } from 'util';

const execAsync = promisify(exec);

export interface BannedIP {
  ip: string;
  banTime: string;
  failCount: number;
}

export interface Fail2BanStats {
  currentlyBanned: number;
  totalBanned: number;
  currentlyFailed: number;
  totalFailed: number;
  bannedIPs: BannedIP[];
}

/**
 * Récupère le statut de Fail2Ban pour SSH
 */
export async function getFail2BanStatus(): Promise<Fail2BanStats> {
  try {
    // Vérifier si Fail2Ban est disponible
    try {
      await execAsync('which fail2ban-client');
    } catch (e) {
      // Fail2Ban n'est pas installé (environnement sandbox), retourner des valeurs par défaut
      console.log('[Fail2Ban] Service non disponible (environnement sandbox)');
      return {
        currentlyBanned: 0,
        totalBanned: 0,
        currentlyFailed: 0,
        totalFailed: 0,
        bannedIPs: [],
      };
    }

    const { stdout } = await execAsync('sudo fail2ban-client status sshd');
    
    const stats: Fail2BanStats = {
      currentlyBanned: 0,
      totalBanned: 0,
      currentlyFailed: 0,
      totalFailed: 0,
      bannedIPs: [],
    };

    // Parser le statut
    const currentlyBannedMatch = stdout.match(/Currently banned:\s+(\d+)/);
    const totalBannedMatch = stdout.match(/Total banned:\s+(\d+)/);
    const currentlyFailedMatch = stdout.match(/Currently failed:\s+(\d+)/);
    const totalFailedMatch = stdout.match(/Total failed:\s+(\d+)/);
    const bannedIPsMatch = stdout.match(/Banned IP list:\s+(.+)/);

    if (currentlyBannedMatch) stats.currentlyBanned = parseInt(currentlyBannedMatch[1]);
    if (totalBannedMatch) stats.totalBanned = parseInt(totalBannedMatch[1]);
    if (currentlyFailedMatch) stats.currentlyFailed = parseInt(currentlyFailedMatch[1]);
    if (totalFailedMatch) stats.totalFailed = parseInt(totalFailedMatch[1]);

    // Récupérer les IPs bannies avec détails
    if (bannedIPsMatch && bannedIPsMatch[1].trim()) {
      const ips = bannedIPsMatch[1].trim().split(/\s+/);
      
      for (const ip of ips) {
        try {
          // Récupérer les détails de chaque IP bannie
          const { stdout: ipInfo } = await execAsync(`sudo fail2ban-client get sshd actionban | grep -A 10 "${ip}" || echo ""`);
          
          stats.bannedIPs.push({
            ip,
            banTime: new Date().toISOString(), // Approximation, Fail2Ban ne stocke pas la date exacte facilement
            failCount: 3, // Valeur par défaut basée sur maxretry
          });
        } catch (err) {
          // Si on ne peut pas récupérer les détails, on ajoute quand même l'IP
          stats.bannedIPs.push({
            ip,
            banTime: new Date().toISOString(),
            failCount: 3,
          });
        }
      }
    }

    return stats;
  } catch (error) {
    console.error('[Fail2Ban] Erreur lors de la récupération du statut:', error);
    throw new Error('Impossible de récupérer le statut Fail2Ban');
  }
}

/**
 * Débannit une IP spécifique
 */
export async function unbanIP(ip: string): Promise<boolean> {
  try {
    await execAsync(`sudo fail2ban-client set sshd unbanip ${ip}`);
    console.log(`[Fail2Ban] IP ${ip} débannie avec succès`);
    return true;
  } catch (error) {
    console.error(`[Fail2Ban] Erreur lors du débannissement de ${ip}:`, error);
    return false;
  }
}

/**
 * Récupère l'historique des bannissements depuis les logs
 */
export async function getBanHistory(limit: number = 50): Promise<Array<{ ip: string; date: string; action: string }>> {
  try {
    // Vérifier si Fail2Ban est disponible
    try {
      await execAsync('which fail2ban-client');
    } catch (e) {
      // Fail2Ban n'est pas installé (environnement sandbox)
      console.log('[Fail2Ban] Service non disponible (environnement sandbox)');
      return [];
    }

    const { stdout } = await execAsync(`sudo grep "Ban" /var/log/fail2ban.log | tail -${limit}`);
    
    const history: Array<{ ip: string; date: string; action: string }> = [];
    const lines = stdout.trim().split('\n');

    for (const line of lines) {
      // Format: 2026-01-18 19:00:00,123 fail2ban.actions [12345]: NOTICE [sshd] Ban 1.2.3.4
      const match = line.match(/(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}).*\[(sshd)\] (Ban|Unban) ([\d.]+)/);
      
      if (match) {
        history.push({
          date: match[1],
          action: match[3],
          ip: match[4],
        });
      }
    }

    return history.reverse(); // Plus récent en premier
  } catch (error) {
    console.error('[Fail2Ban] Erreur lors de la récupération de l\'historique:', error);
    return [];
  }
}

/**
 * Compte le nombre d'IPs bannies dans la dernière heure
 */
export async function countRecentBans(hours: number = 1): Promise<number> {
  try {
    // Vérifier si Fail2Ban est disponible
    try {
      await execAsync('which fail2ban-client');
    } catch (e) {
      // Fail2Ban n'est pas installé (environnement sandbox)
      console.log('[Fail2Ban] Service non disponible (environnement sandbox)');
      return 0;
    }

    const since = new Date();
    since.setHours(since.getHours() - hours);
    const sinceStr = since.toISOString().slice(0, 19).replace('T', ' ');

    const { stdout } = await execAsync(`sudo grep "Ban" /var/log/fail2ban.log | grep -v "Unban" | awk '{print $1, $2}' | awk -v since="${sinceStr}" '$0 >= since' | wc -l`);
    
    return parseInt(stdout.trim()) || 0;
  } catch (error) {
    console.error('[Fail2Ban] Erreur lors du comptage des bannissements récents:', error);
    return 0;
  }
}
