#!/usr/bin/env python3
"""
IDS - Cleanup Detections Script
================================
Automatizza la pulizia delle detections e lo sblocco degli IP secondo le regole:
1. Cancella detections non anomale dopo 48 ore
2. Sblocca IP bloccati se non più anomali dopo 2 ore

Esecuzione: Ogni ora via cron/systemd timer
"""

import os
import sys
import logging
from datetime import datetime, timedelta
import psycopg2
from psycopg2.extras import RealDictCursor
from dotenv import load_dotenv

# Setup logging
logging.basicConfig(
    level=logging.INFO,
    format='[%(asctime)s] %(levelname)s: %(message)s',
    handlers=[
        logging.FileHandler('/var/log/ids/cleanup.log'),
        logging.StreamHandler(sys.stdout)
    ]
)
logger = logging.getLogger(__name__)

# Load environment
load_dotenv()

def get_db_connection():
    """Connessione al database PostgreSQL"""
    return psycopg2.connect(
        host=os.getenv('PGHOST', 'localhost'),
        port=int(os.getenv('PGPORT', 5432)),
        user=os.getenv('PGUSER'),
        password=os.getenv('PGPASSWORD'),
        database=os.getenv('PGDATABASE')
    )

def cleanup_old_detections(conn, hours=48):
    """
    Cancella detections vecchie di più di N ore.
    
    Logica: Se un IP è stato rilevato ma dopo 48 ore non è più
    considerato anomalo (non appare in nuove detections), eliminalo.
    """
    cursor = conn.cursor(cursor_factory=RealDictCursor)
    
    cutoff_time = datetime.now() - timedelta(hours=hours)
    
    # Conta detections da eliminare
    cursor.execute("""
        SELECT COUNT(*) as count 
        FROM detections 
        WHERE detected_at < %s
          AND blocked = false
    """, (cutoff_time,))
    
    count = cursor.fetchone()['count']
    
    if count > 0:
        logger.info(f"Trovate {count} detections da eliminare (più vecchie di {hours}h)")
        
        # Elimina
        cursor.execute("""
            DELETE FROM detections 
            WHERE detected_at < %s
              AND blocked = false
        """, (cutoff_time,))
        
        conn.commit()
        logger.info(f"✅ Eliminate {cursor.rowcount} detections vecchie")
    else:
        logger.info(f"Nessuna detection da eliminare (soglia: {hours}h)")
    
    cursor.close()
    return count

def unblock_old_ips(conn, hours=2):
    """
    Sblocca IP bloccati da più di N ore.
    
    Logica: Se un IP è stato bloccato ma dopo 2 ore non è più
    anomalo (nessuna nuova detection), sbloccalo dal DB.
    
    NOTA: Questo NON rimuove l'IP dalle firewall list dei router MikroTik.
    Per quello serve chiamare l'API /unblock-ip del ML backend.
    """
    cursor = conn.cursor(cursor_factory=RealDictCursor)
    
    cutoff_time = datetime.now() - timedelta(hours=hours)
    
    # Trova IP bloccati da più di N ore senza nuove detections
    cursor.execute("""
        SELECT d.source_ip, d.blocked_at, d.anomaly_type, d.risk_score
        FROM detections d
        WHERE d.blocked = true
          AND d.blocked_at < %s
          AND NOT EXISTS (
              SELECT 1 FROM detections d2
              WHERE d2.source_ip = d.source_ip
                AND d2.detected_at > %s
          )
    """, (cutoff_time, cutoff_time))
    
    ips_to_unblock = cursor.fetchall()
    
    if ips_to_unblock:
        logger.info(f"Trovati {len(ips_to_unblock)} IP da sbloccare (bloccati da più di {hours}h)")
        
        for ip_data in ips_to_unblock:
            ip = ip_data['source_ip']
            logger.info(f"  - {ip} (tipo: {ip_data['anomaly_type']}, score: {ip_data['risk_score']})")
            
            # Aggiorna DB - SOLO i record bloccati da più di N ore
            # NON sbloccate record recenti dello stesso IP!
            cursor.execute("""
                UPDATE detections
                SET blocked = false, blocked_at = NULL
                WHERE source_ip = %s
                  AND blocked = true
                  AND blocked_at < %s
            """, (ip, cutoff_time))
        
        conn.commit()
        logger.info(f"✅ Sbloccati {len(ips_to_unblock)} IP nel database")
        logger.warning("⚠️  ATTENZIONE: IP ancora presenti nelle firewall list MikroTik!")
        logger.info("💡 Per rimuoverli dai router, usa: curl -X POST http://localhost:8000/unblock-ip -d '{\"ip_address\": \"X.X.X.X\"}'")
    else:
        logger.info(f"Nessun IP da sbloccare (soglia: {hours}h)")
    
    cursor.close()
    return len(ips_to_unblock)

def main():
    """Esecuzione cleanup completo"""
    logger.info("=" * 60)
    logger.info("CLEANUP DETECTIONS - Avvio")
    logger.info("=" * 60)
    
    try:
        conn = get_db_connection()
        logger.info("✅ Connesso al database")
        
        # 1. Cleanup detections vecchie (48h)
        logger.info("\n[1/2] Cleanup detections vecchie...")
        deleted_count = cleanup_old_detections(conn, hours=48)
        
        # 2. Sblocco IP vecchi (2h)
        logger.info("\n[2/2] Sblocco IP vecchi...")
        unblocked_count = unblock_old_ips(conn, hours=2)
        
        conn.close()
        
        logger.info("\n" + "=" * 60)
        logger.info("CLEANUP COMPLETATO")
        logger.info(f"  - Detections eliminate: {deleted_count}")
        logger.info(f"  - IP sbloccati (DB): {unblocked_count}")
        logger.info("=" * 60)
        
        return 0
        
    except Exception as e:
        logger.error(f"❌ Errore durante cleanup: {e}", exc_info=True)
        return 1

if __name__ == "__main__":
    sys.exit(main())
