import re
import subprocess
from logger import get_logger

log = get_logger("actions.service")

# Whitelist of allowed system services that can be managed via API commands.
ALLOWED_SERVICES = {
    "nginx", "apache2", "httpd", "mysql", "mariadb", "mysqld",
    "postgresql", "postgres", "redis", "redis-server", "docker",
    "fail2ban", "ssh", "sshd", "ufw"
}

def validate_service_name(service_name: str | None) -> str:
    """Validate that service_name is present, safe, and whitelisted.

    Raises ValueError if validation fails.
    """
    if not service_name:
        raise ValueError("Service name is required")
    
    # Strictly validate name formatting to prevent shell/argument injection
    if not re.match(r"^[a-zA-Z0-9_\-\.]+$", service_name):
        raise ValueError(f"Invalid service name format: {service_name}")
        
    if service_name not in ALLOWED_SERVICES:
        raise ValueError(f"Service '{service_name}' is not in the whitelist of allowed services")
        
    return service_name

def handle_service_start(payload: dict) -> dict:
    try:
        service_name = validate_service_name(payload.get("service"))
    except ValueError as exc:
        return {"success": False, "error": str(exc)}
    
    log.info("Attempting to start system service: %s", service_name)
    try:
        res = subprocess.run(["systemctl", "start", service_name], capture_output=True, text=True, timeout=30, env={"LC_ALL": "C"}, check=False)
    except (subprocess.SubprocessError, OSError) as exc:
        log.error("Failed to run systemctl start for %s: %s", service_name, exc)
        return {"success": False, "error": f"Command failed: {str(exc)}"}
        
    if res.returncode != 0:
        log.error("Failed to start service %s: %s", service_name, res.stderr.strip())
        return {"success": False, "error": res.stderr.strip() or f"Failed to start {service_name}"}
    
    log.info("Service %s started successfully", service_name)
    return {"success": True, "message": f"Service {service_name} started successfully"}

def handle_service_stop(payload: dict) -> dict:
    try:
        service_name = validate_service_name(payload.get("service"))
    except ValueError as exc:
        return {"success": False, "error": str(exc)}
    
    log.info("Attempting to stop system service: %s", service_name)
    try:
        res = subprocess.run(["systemctl", "stop", service_name], capture_output=True, text=True, timeout=30, env={"LC_ALL": "C"}, check=False)
    except (subprocess.SubprocessError, OSError) as exc:
        log.error("Failed to run systemctl stop for %s: %s", service_name, exc)
        return {"success": False, "error": f"Command failed: {str(exc)}"}
        
    if res.returncode != 0:
        log.error("Failed to stop service %s: %s", service_name, res.stderr.strip())
        return {"success": False, "error": res.stderr.strip() or f"Failed to stop {service_name}"}
    
    log.info("Service %s stopped successfully", service_name)
    return {"success": True, "message": f"Service {service_name} stopped successfully"}

def handle_service_restart(payload: dict) -> dict:
    try:
        service_name = validate_service_name(payload.get("service"))
    except ValueError as exc:
        return {"success": False, "error": str(exc)}
    
    log.info("Attempting to restart system service: %s", service_name)
    try:
        res = subprocess.run(["systemctl", "restart", service_name], capture_output=True, text=True, timeout=30, env={"LC_ALL": "C"}, check=False)
    except (subprocess.SubprocessError, OSError) as exc:
        log.error("Failed to run systemctl restart for %s: %s", service_name, exc)
        return {"success": False, "error": f"Command failed: {str(exc)}"}
        
    if res.returncode != 0:
        log.error("Failed to restart service %s: %s", service_name, res.stderr.strip())
        return {"success": False, "error": res.stderr.strip() or f"Failed to restart {service_name}"}
    
    log.info("Service %s restarted successfully", service_name)
    return {"success": True, "message": f"Service {service_name} restarted successfully"}

def handle_service_status(payload: dict) -> dict:
    try:
        service_name = validate_service_name(payload.get("service"))
    except ValueError as exc:
        return {"success": False, "error": str(exc)}
    
    log.info("Attempting to query status for system service: %s", service_name)
    try:
        res = subprocess.run(["systemctl", "show", "--property=ActiveState,SubState,MainPID,MemoryCurrent,ActiveEnterTimestampMonotonic", service_name], capture_output=True, text=True, timeout=30, env={"LC_ALL": "C"}, check=False)
    except (subprocess.SubprocessError, OSError) as exc:
        log.error("Failed to run systemctl show for %s: %s", service_name, exc)
        return {"success": False, "error": f"Command failed: {str(exc)}"}

    if res.returncode != 0:
        log.error("Failed to query status for service %s: %s", service_name, res.stderr.strip())
        return {"success": False, "error": res.stderr.strip() or f"Failed to query status for {service_name}"}

    props: dict[str, str] = {}
    for line in res.stdout.splitlines():
        key, sep, value = line.partition("=")
        if sep:
            props[key] = value.strip()

    active_state = props.get("ActiveState", "unknown")
    sub_state = props.get("SubState", "unknown")

    # Additive detail fields — only reported when systemd provides real values.
    pid = None
    if props.get("MainPID", "").isdigit() and int(props["MainPID"]) > 0:
        pid = int(props["MainPID"])

    memory_bytes = None
    if props.get("MemoryCurrent", "").isdigit():
        memory_bytes = int(props["MemoryCurrent"])

    # ActiveEnterTimestampMonotonic is microseconds since boot; /proc/uptime's
    # first field is seconds since boot on the same clock, so the difference
    # is the service's time in the active state.
    uptime_seconds = None
    enter_mono = props.get("ActiveEnterTimestampMonotonic", "")
    if active_state == "active" and enter_mono.isdigit() and int(enter_mono) > 0:
        try:
            with open("/proc/uptime", "r", encoding="utf-8") as handle:
                boot_uptime = float(handle.readline().split()[0])
            uptime_seconds = max(0.0, round(boot_uptime - int(enter_mono) / 1_000_000, 1))
        except (OSError, ValueError, IndexError):
            uptime_seconds = None

    return {
        "success": True,
        "status": active_state,
        "sub_state": sub_state,
        "pid": pid,
        "memory_bytes": memory_bytes,
        "uptime_seconds": uptime_seconds,
        "message": f"Service {service_name} status is {active_state} ({sub_state})"
    }

