import json
import time
import re
from modules.ssh_manager import execute_command
from models import Server, Application

def fetch_server_metrics(server_data):
    """
    Connects to server via SSH and executes native Linux diagnostic commands
    using /proc/meminfo, statvfs, /proc/uptime, /proc/loadavg without any psutil dependency.
    """
    class DummyServer:
        pass
    srv = DummyServer()
    srv.ip = server_data['ip']
    srv.ssh_port = server_data.get('ssh_port', 22)
    srv.ssh_user = server_data.get('ssh_user', 'root')
    srv.auth_type = server_data.get('auth_type', 'key')
    srv.ssh_key_path = server_data.get('ssh_key_path')
    srv.password = server_data.get('password')

    metrics_cmd = """python3 - << 'EOF'
import sys, os, subprocess, json, time, re

def get_sys_info():
    os_info = "Ubuntu Linux"
    if os.path.exists('/etc/os-release'):
        with open('/etc/os-release') as f:
            for line in f:
                if line.startswith('PRETTY_NAME='):
                    os_info = line.split('=')[1].strip().strip('"')
                    break
    
    cpu_cores = os.cpu_count() or 2
    load1 = os.getloadavg()[0] if hasattr(os, 'getloadavg') else 0.1
    cpu_pct = round(min(100.0, (load1 / cpu_cores) * 100), 1)

    mem_total = 0.0
    mem_available = 0.0
    if os.path.exists('/proc/meminfo'):
        with open('/proc/meminfo') as f:
            for line in f:
                if line.startswith('MemTotal:'):
                    mem_total = float(line.split()[1]) / (1024 * 1024)
                elif line.startswith('MemAvailable:'):
                    mem_available = float(line.split()[1]) / (1024 * 1024)
    total_ram = round(mem_total, 2)
    used_ram = round(max(0.0, mem_total - mem_available), 2)
    
    st = os.statvfs('/')
    total_disk = round((st.f_blocks * st.f_frsize) / (1024**3), 2)
    free_disk = round((st.f_bavail * st.f_frsize) / (1024**3), 2)
    used_disk = round(max(0.0, total_disk - free_disk), 2)
    
    uptime_str = "Unknown"
    if os.path.exists('/proc/uptime'):
        with open('/proc/uptime') as f:
            secs = float(f.read().split()[0])
            days = int(secs // 86400)
            hours = int((secs % 86400) // 3600)
            uptime_str = f"{days}d {hours}h"
    
    ports = []
    try:
        res = subprocess.run(['ss', '-tulpn', '-n'], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
        lines = res.stdout.splitlines()
        for line in lines[1:]:
            parts = line.split()
            if len(parts) >= 5:
                proto = parts[0]
                local_addr = parts[4]
                proc_info = parts[6] if len(parts) > 6 else ""
                
                if ':' in local_addr:
                    port_str = local_addr.split(':')[-1]
                    bind_ip = local_addr.rsplit(':', 1)[0]
                    if port_str.isdigit():
                        port = int(port_str)
                        exposure = "public" if bind_ip in ('0.0.0.0', '*', '::') else "internal"
                        
                        proc_name = "Unknown"
                        pid = None
                        if 'users:' in proc_info:
                            try:
                                name_match = proc_info.split('(("')[1].split('"')[0]
                                proc_name = name_match
                                pid_match = proc_info.split('pid=')[1].split(',')[0]
                                pid = int(pid_match)
                            except:
                                pass
                        
                        ports.append({
                            'port': port,
                            'protocol': proto,
                            'bind_ip': bind_ip,
                            'exposure': exposure,
                            'process': proc_name,
                            'pid': pid,
                            'app': 'Desconhecido'
                        })
    except Exception as e:
        pass

    return {
        'os_info': os_info,
        'cpu_cores': cpu_cores,
        'total_ram_gb': total_ram,
        'used_ram_gb': used_ram,
        'total_disk_gb': total_disk,
        'used_disk_gb': used_disk,
        'cpu_usage_pct': cpu_pct,
        'uptime_str': uptime_str,
        'listening_ports': ports
    }

print(json.dumps(get_sys_info()))
EOF
"""
    exit_code, out, err = execute_command(srv, metrics_cmd)
    if exit_code == 0 and out.strip():
        try:
            data = json.loads(out.strip())
            Server.update_metrics(
                server_id=server_data['id'],
                status='online',
                os_info=data['os_info'],
                cpu_cores=data['cpu_cores'],
                total_ram=data['total_ram_gb'],
                used_ram=data['used_ram_gb'],
                total_disk=data['total_disk_gb'],
                used_disk=data['used_disk_gb'],
                cpu_pct=data['cpu_usage_pct'],
                uptime=data['uptime_str'],
                listening_ports=data['listening_ports']
            )
            return True
        except Exception:
            pass
    return False

def check_app_live_health(server_data, app_data):
    """
    Executes a 5-layer diagnostic script via SSH to evaluate:
    1. Systemd Service Status (systemctl is-active)
    2. Gunicorn / Node Process Status
    3. Listening Port (ss -tulpn)
    4. Local HTTP Test (http://127.0.0.1:PORT/) -> code & latency
    5. Public HTTP Test (http://31.97.147.114:PORT/) -> code & latency
    Returns exact states: ONLINE (🟢), INACESSÍVEL EXTERNAMENTE (🟠), DEGRADADO (🟡), OFFLINE (🔴).
    """
    class DummyServer:
        pass
    srv = DummyServer()
    srv.ip = server_data['ip']
    srv.ssh_port = server_data.get('ssh_port', 22)
    srv.ssh_user = server_data.get('ssh_user', 'root')
    srv.auth_type = server_data.get('auth_type', 'key')
    srv.ssh_key_path = server_data.get('ssh_key_path')
    srv.password = server_data.get('password')

    service_name = app_data.get('service_name') or f"{app_data['name'].lower().replace(' ', '_')}.service"
    if not service_name.endswith('.service'):
        service_name = f"{service_name}.service"

    port = app_data['internal_port']
    pub_ip = server_data['ip']

    check_cmd = """python3 - << 'EOF'
import os, sys, subprocess, json, time

service = """ + f'"{service_name}"' + """
port = """ + str(port) + """
pub_ip = """ + f'"{pub_ip}"' + """

# 1. Systemd check
res_sys = subprocess.run(['systemctl', 'is-active', service], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
sys_active = 'active' in res_sys.stdout.strip()

# 2. Port LISTEN check
res_port = subprocess.run(['ss', '-tulpn'], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
port_listening = f":{port}" in res_port.stdout

# 3. Local HTTP check (127.0.0.1)
local_code = 0
local_ms = 0
try:
    res_loc = subprocess.run(['curl', '-s', '-o', '/dev/null', '-w', '%{time_total} %{http_code}', f'http://127.0.0.1:{port}/'], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, timeout=3)
    p = res_loc.stdout.strip().split()
    if len(p) >= 2:
        local_ms = int(float(p[0]) * 1000)
        local_code = int(p[1])
except Exception:
    pass

# 4. Public HTTP check (IP)
public_code = 0
public_ms = 0
try:
    res_pub = subprocess.run(['curl', '-s', '-o', '/dev/null', '-w', '%{time_total} %{http_code}', f'http://{pub_ip}:{port}/'], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, timeout=4)
    p = res_pub.stdout.strip().split()
    if len(p) >= 2:
        public_ms = int(float(p[0]) * 1000)
        public_code = int(p[1])
except Exception:
    pass

print(json.dumps({
    'sys_active': sys_active,
    'port_listening': port_listening,
    'local_code': local_code,
    'local_ms': local_ms,
    'public_code': public_code,
    'public_ms': public_ms
}))
EOF
"""

    exit_code, out, _ = execute_command(srv, check_cmd)
    
    sys_active = False
    port_listening = False
    local_code = 0
    local_ms = 0
    public_code = 0
    public_ms = 0

    if exit_code == 0 and out.strip():
        try:
            d = json.loads(out.strip())
            sys_active = d.get('sys_active', False)
            port_listening = d.get('port_listening', False)
            local_code = d.get('local_code', 0)
            local_ms = d.get('local_ms', 0)
            public_code = d.get('public_code', 0)
            public_ms = d.get('public_ms', 0)
        except Exception:
            pass

    # Strict Status Classification Matrix
    valid_codes = (200, 301, 302, 304, 401, 403)
    
    if local_code in valid_codes and public_code in valid_codes:
        live_status = 'ONLINE'
        status_color = '#4ade80'
        badge_cls = 'badge-online'
        reason = "Aplicação respondendo com sucesso local e publicamente."
    elif local_code in valid_codes and public_code not in valid_codes:
        live_status = 'INACESSÍVEL EXTERNAMENTE'
        status_color = '#f59e0b'
        badge_cls = 'badge-warning'
        reason = f"Aplicação responde localmente (HTTP {local_code}), mas a rota pública (HTTP {public_code}) falhou. Verifique Firewall."
    elif sys_active or port_listening or local_code in (500, 502, 503, 504):
        live_status = 'DEGRADADO'
        status_color = '#f59e0b'
        badge_cls = 'badge-warning'
        reason = f"Processo ativo mas o servidor respondeu erro interno (HTTP local {local_code})."
    else:
        live_status = 'OFFLINE'
        status_color = '#f87171'
        badge_cls = 'badge-offline'
        reason = f"Serviço Systemd ({service_name}) inativo ou porta :{port} sem resposta."

    return {
        'status': live_status,
        'status_color': status_color,
        'badge_cls': badge_cls,
        'reason': reason,
        'systemd_active': sys_active,
        'port_listening': port_listening,
        'local_code': local_code,
        'local_ms': local_ms if local_code > 0 else 0,
        'public_code': public_code,
        'public_ms': public_ms if public_code > 0 else 0,
        'service_name': service_name,
        'port': port,
        'public_ip': pub_ip
    }
