"""Read-only detection of installed runtimes, services and databases.

Detection is by presence + version only. We never start, stop, reload or
configure anything, and never read database contents or credentials.
"""

from __future__ import annotations

import shutil
from typing import Any

from discovery.system import run_readonly
from logger import get_logger

log = get_logger("discovery.services")


def _present(binary: str) -> bool:
    return shutil.which(binary) is not None


def _version(cmd: list[str]) -> str | None:
    output = run_readonly(cmd)
    if not output:
        return None
    return output.strip().splitlines()[0] if output.strip() else None


def systemd_services() -> list[dict[str, Any]]:
    """List loaded systemd service units (read-only)."""
    output = run_readonly(
        ["systemctl", "list-units", "--type=service", "--all", "--no-legend", "--no-pager"]
    )
    services: list[dict[str, Any]] = []
    if not output:
        return services
    for line in output.splitlines():
        fields = line.split(maxsplit=4)
        if len(fields) >= 4 and fields[0].endswith(".service"):
            services.append(
                {
                    "unit": fields[0],
                    "load": fields[1],
                    "active": fields[2],
                    "sub": fields[3],
                }
            )
    return services


def web_servers() -> dict[str, Any]:
    return {
        "nginx": {
            "present": _present("nginx"),
            "version": _version(["nginx", "-v"]),
        },
        "apache": {
            "present": _present("apache2") or _present("httpd"),
            "version": _version(["apache2", "-v"]) or _version(["httpd", "-v"]),
        },
    }


def language_runtimes() -> dict[str, Any]:
    return {
        "php": {"present": _present("php"), "version": _version(["php", "--version"])},
        "node": {"present": _present("node"), "version": _version(["node", "--version"])},
        "python": {
            "present": _present("python3") or _present("python"),
            "version": _version(["python3", "--version"]) or _version(["python", "--version"]),
        },
    }


def containers() -> dict[str, Any]:
    """Docker presence and a read-only list of containers if available."""
    result: dict[str, Any] = {"docker": {"present": _present("docker"), "containers": []}}
    if not result["docker"]["present"]:
        return result
    result["docker"]["version"] = _version(["docker", "--version"])
    # Read-only listing; format avoids needing the daemon to be reachable to
    # produce a clean parse error vs. crashing.
    output = run_readonly(
        ["docker", "ps", "-a", "--format", "{{.Names}}\t{{.Image}}\t{{.Status}}"]
    )
    if output:
        for line in output.splitlines():
            parts = line.split("\t")
            if len(parts) == 3:
                result["docker"]["containers"].append(
                    {"name": parts[0], "image": parts[1], "status": parts[2]}
                )
    return result


def databases() -> dict[str, Any]:
    """Detect database engines by client/server binary presence only.

    SECURITY: we never connect to a database, run queries, or read data files.
    """
    return {
        "mysql": {
            "present": _present("mysql") or _present("mysqld") or _present("mariadbd"),
            "version": _version(["mysql", "--version"]),
        },
        "mariadb": {
            "present": _present("mariadb") or _present("mariadbd"),
            "version": _version(["mariadb", "--version"]),
        },
        "postgresql": {
            "present": _present("psql") or _present("postgres"),
            "version": _version(["psql", "--version"]),
        },
        "redis": {
            "present": _present("redis-server") or _present("redis-cli"),
            "version": _version(["redis-server", "--version"]),
        },
    }


def cron_jobs() -> list[dict[str, Any]]:
    """List user crontab entries (read-only). System cron dirs left as TODO."""
    jobs: list[dict[str, Any]] = []
    output = run_readonly(["crontab", "-l"])
    if not output:
        return jobs
    for line in output.splitlines():
        stripped = line.strip()
        if stripped and not stripped.startswith("#"):
            jobs.append({"source": "user_crontab", "entry": stripped})
    return jobs


def certbot_available() -> bool:
    """Return True if certbot binary is installed and executable."""
    return _present("certbot")
