#!/usr/bin/env python3
import sys
import os
import time
import subprocess
import json
import syslog
import socket
from datetime import datetime, timedelta

CONFIG_PATH = "/etc/ssh/ssh_alerts_config.json"
STATE_FILE = "/tmp/system_resources_state.json"
ALERT_THRESHOLD = 85.0
COOLDOWN_SECONDS = 3600  # Remind every 1 hour if resource stays above threshold

def log_to_syslog(message, is_error=False):
    level = syslog.LOG_ERR if is_error else syslog.LOG_INFO
    syslog.openlog(ident="system_resources_monitor", logoption=syslog.LOG_PID, facility=syslog.LOG_DAEMON)
    syslog.syslog(level, message)
    syslog.closelog()

def get_vps_identity():
    # Attempt to auto-detect WireGuard IP
    try:
        output = subprocess.check_output(
            ['ip', '-o', '-4', 'addr', 'show', 'dev', 'wg0'],
            stderr=subprocess.DEVNULL
        ).decode('utf-8')
        for line in output.split('\n'):
            parts = line.split()
            if len(parts) >= 4:
                return f"VPS 3 ({parts[3].split('/')[0]})"
    except Exception:
        pass
    
    # Fallback to general IP/hostname
    try:
        return f"VPS ({socket.gethostname()})"
    except Exception:
        return "VPS (10.99.0.3)"

def get_cpu_usage():
    def read_cpu_times():
        with open('/proc/stat', 'r') as f:
            first_line = f.readline()
        parts = first_line.split()
        times = [float(x) for x in parts[1:9]]
        idle = times[3] + times[4]  # idle + iowait
        total = sum(times)
        return idle, total

    try:
        idle1, total1 = read_cpu_times()
        time.sleep(1.0)
        idle2, total2 = read_cpu_times()
        
        idle_delta = idle2 - idle1
        total_delta = total2 - total1
        
        if total_delta > 0:
            return (1.0 - idle_delta / total_delta) * 100.0
    except Exception as e:
        log_to_syslog(f"Erro ao obter uso de CPU: {e}", is_error=True)
    return 0.0

def get_memory_usage():
    try:
        mem_info = {}
        with open('/proc/meminfo', 'r') as f:
            for line in f:
                parts = line.split(':')
                if len(parts) == 2:
                    key = parts[0].strip()
                    val = parts[1].strip().split()[0]
                    mem_info[key] = int(val)
        
        total = mem_info.get('MemTotal', 0)
        available = mem_info.get('MemAvailable', 0)
        if total > 0:
            used = total - available
            return (used / total) * 100.0
    except Exception as e:
        log_to_syslog(f"Erro ao obter uso de memoria: {e}", is_error=True)
    return 0.0

def get_disk_usage():
    try:
        output = subprocess.check_output(['df', '-Pl', '/']).decode('utf-8')
        lines = output.strip().split('\n')
        if len(lines) >= 2:
            parts = lines[-1].split()
            if len(parts) >= 5:
                pct_str = parts[4]
                if pct_str.endswith('%'):
                    return float(pct_str[:-1])
    except Exception as e:
        log_to_syslog(f"Erro ao obter uso de disco: {e}", is_error=True)
    return 0.0

def get_top_processes():
    try:
        ps_output = subprocess.check_output(
            ['ps', '-eo', 'pid,%mem,%cpu,cmd', '--sort=-%mem'],
            stderr=subprocess.DEVNULL
        ).decode('utf-8', errors='ignore').strip().split('\n')
        return ps_output[:6]  # Header + top 5 processes
    except Exception as e:
        log_to_syslog(f"Erro ao obter processos ofensores: {e}", is_error=True)
        return []

def load_state():
    if os.path.exists(STATE_FILE):
        try:
            with open(STATE_FILE, "r") as f:
                return json.load(f)
        except Exception:
            pass
    return {}

def save_state(state):
    try:
        with open(STATE_FILE, "w") as f:
            json.dump(state, f)
    except Exception as e:
        log_to_syslog(f"Erro ao salvar estado de alertas: {e}", is_error=True)

def send_telegram_alert(token, chat_id, vps_identity, alerts_active, top_processes):
    try:
        url = f"https://api.telegram.org/bot{token}/sendMessage"
        
        alerts_text = ""
        for resource, val in alerts_active.items():
            alerts_text += f"• *{resource}:* `{val:.1f}%` (Limite: {ALERT_THRESHOLD:.1f}%)\n"
            
        proc_text = ""
        if top_processes:
            proc_text = "\n🔥 *Top 5 Processos Consumindo mais RAM:*\n```\n"
            proc_text += "\n".join(top_processes)
            proc_text += "\n```"
            
        text = (
            f"🚨 *ALERTA DE USO DE RECURSOS CRÍTICOS*\n\n"
            f"• *Servidor:* `{vps_identity}`\n"
            f"{alerts_text}"
            f"{proc_text}\n"
            f"📅 _Registrado automaticamente pelo monitor do sistema._"
        )
        
        payload = json.dumps({
            "chat_id": chat_id,
            "text": text,
            "parse_mode": "Markdown"
        }).encode("utf-8")
        
        req = urllib.request.Request(
            url,
            data=payload,
            headers={
                'Content-Type': 'application/json',
                'User-Agent': 'Mozilla/5.0'
            }
        )
        import urllib.request
        with urllib.request.urlopen(req, timeout=5) as r:
            res = json.loads(r.read().decode())
            return res.get("ok", False)
    except Exception as e:
        log_to_syslog(f"Erro ao enviar alerta para o Telegram: {e}", is_error=True)
        return False

def send_telegram_resolved(token, chat_id, vps_identity, resolved_resources):
    try:
        url = f"https://api.telegram.org/bot{token}/sendMessage"
        
        resolved_text = ""
        for resource, val in resolved_resources.items():
            resolved_text += f"• *{resource}:* `{val:.1f}%` (Normalizado)\n"
            
        text = (
            f"✅ *RECURSOS NORMALIZADOS*\n\n"
            f"• *Servidor:* `{vps_identity}`\n"
            f"{resolved_text}\n"
            f"📅 _O servidor voltou a operar dentro dos limites de segurança._"
        )
        
        payload = json.dumps({
            "chat_id": chat_id,
            "text": text,
            "parse_mode": "Markdown"
        }).encode("utf-8")
        
        req = urllib.request.Request(
            url,
            data=payload,
            headers={
                'Content-Type': 'application/json',
                'User-Agent': 'Mozilla/5.0'
            }
        )
        import urllib.request
        with urllib.request.urlopen(req, timeout=5) as r:
            res = json.loads(r.read().decode())
            return res.get("ok", False)
    except Exception as e:
        log_to_syslog(f"Erro ao enviar resolucao para o Telegram: {e}", is_error=True)
        return False

def create_calendar_event(vps_identity, alerts_active, top_processes, calendar_id):
    try:
        from google.oauth2 import service_account
        from googleapiclient.discovery import build
        
        creds = service_account.Credentials.from_service_account_file(
            '/etc/ssh/gcal_key.json',
            scopes=['https://www.googleapis.com/auth/calendar']
        )
        
        service = build('calendar', 'v3', credentials=creds)
        
        now = datetime.utcnow()
        end_time = now + timedelta(minutes=30)
        
        start_iso = now.isoformat() + 'Z'
        end_iso = end_time.isoformat() + 'Z'
        
        resources_str = ", ".join(alerts_active.keys())
        summary = f"{vps_identity}: 🚨 Estresse de Sistema - {resources_str}"
        
        description = (
            f"=== 🖥️ ALERTA DE RECURSOS DO SISTEMA ===\n\n"
            f"• Servidor: {vps_identity}\n"
            f"• Recursos acima do limite (>={ALERT_THRESHOLD}%):\n"
        )
        for res, val in alerts_active.items():
            description += f"  - {res}: {val:.1f}%\n"
            
        if top_processes:
            description += f"\n--- TOP PROCESSOS POR MEMÓRIA ---\n"
            description += "\n".join(top_processes)
            
        description += f"\n\nRegistrado automaticamente em {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}."
        
        event_body = {
            'summary': summary,
            'description': description,
            'start': {'dateTime': start_iso, 'timeZone': 'UTC'},
            'end': {'dateTime': end_iso, 'timeZone': 'UTC'},
            'colorId': "11",  # Vermelho Bold
            'reminders': {
                'useDefault': False,
                'overrides': [{'method': 'popup', 'minutes': 0}]
            }
        }
        
        event = service.events().insert(
            calendarId=calendar_id,
            body=event_body
        ).execute()
        return True
    except Exception as e:
        log_to_syslog(f"Erro ao criar evento de estresse no Google Calendar: {e}", is_error=True)
        return False

def main():
    # Load config
    if not os.path.exists(CONFIG_PATH):
        log_to_syslog(f"Arquivo de configuracao nao encontrado: {CONFIG_PATH}", is_error=True)
        return
        
    try:
        with open(CONFIG_PATH, "r") as f:
            config = json.load(f)
    except Exception as e:
        log_to_syslog(f"Erro ao carregar configuracoes: {e}", is_error=True)
        return
        
    telegram_token = config.get("telegram_token")
    telegram_chat_id = config.get("telegram_chat_id")
    calendar_id = config.get("calendar_id")
    
    if not telegram_token or not telegram_chat_id:
        log_to_syslog("Telegram credentials missing in config.", is_error=True)
        return

    # Check metrics
    cpu = get_cpu_usage()
    mem = get_memory_usage()
    disk = get_disk_usage()
    
    metrics = {
        "CPU": cpu,
        "Memoria": mem,
        "Disco (/)": disk
    }
    
    vps_identity = get_vps_identity()
    state = load_state()
    now = time.time()
    
    alerts_to_send = {}
    resolved_to_send = {}
    
    state_changed = False
    
    for resource, val in metrics.items():
        is_above = val >= ALERT_THRESHOLD
        prev_alert_time = state.get(resource, 0)
        
        if is_above:
            # Check if we should notify: either first time or after cooldown
            if prev_alert_time == 0 or (now - prev_alert_time >= COOLDOWN_SECONDS):
                alerts_to_send[resource] = val
                state[resource] = now
                state_changed = True
        else:
            # If it was previously in alert state, send resolution message
            if prev_alert_time > 0:
                resolved_to_send[resource] = val
                state[resource] = 0
                state_changed = True

    if state_changed:
        save_state(state)

    # Process resolutions
    if resolved_to_send:
        log_to_syslog(f"Métricas normalizadas para: {', '.join(resolved_to_send.keys())}")
        send_telegram_resolved(telegram_token, telegram_chat_id, vps_identity, resolved_to_send)

    # Process alerts
    if alerts_to_send:
        log_to_syslog(f"ALERTA: Métricas acima de {ALERT_THRESHOLD}%: " + ", ".join([f"{k} ({v:.1f}%)" for k, v in alerts_to_send.items()]))
        
        top_processes = get_top_processes()
        
        # Send to Telegram
        send_telegram_alert(telegram_token, telegram_chat_id, vps_identity, alerts_to_send, top_processes)
        
        # Log to Calendar
        if calendar_id:
            create_calendar_event(vps_identity, alerts_to_send, top_processes, calendar_id)

if __name__ == "__main__":
    main()
