#!/bin/bash
# =============================================================================
# runtime-services.sh -- Service Monitor & Control
# =============================================================================
# Category:    runtime
# Bird:        cormorant
# Tier:        1
# Node-aware:  yes (auto-detects installed services)
# =============================================================================

TOOL_NAME="runtime-services"
TOOL_VERSION="2.0.0"
TOOL_TIER=1
TOOL_BIRD="cormorant"
TOOL_LOG="/opt/binary/logs/tools/${TOOL_NAME}.log"
TOOL_CONFIG="/etc/binary/${TOOL_NAME}.conf"

SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
if [[ -f "${SCRIPT_DIR}/common.sh" ]]; then
    source "${SCRIPT_DIR}/common.sh"
elif [[ -f "${SCRIPT_DIR}/../common.sh" ]]; then
    source "${SCRIPT_DIR}/../common.sh"
else
    echo "FATAL: common.sh not found" >&2; exit 1
fi

export BIRD_ID="${TOOL_BIRD}"

# All possible services — auto-skipped if not installed
ALL_SERVICES=(sshd nginx pdns postfix dovecot mariadb php-fpm fail2ban chronyd caddy crowdsec ollama)

# Restart allowlist (Phase 2 write)
RESTART_ALLOW=(nginx php-fpm mariadb postfix dovecot pdns fail2ban caddy crowdsec ollama)

# Keep-list: services NEVER disabled by cleanup
KEEP_SERVICES=(
    sshd ssh chronyd crond cron systemd-journald systemd-logind
    dbus polkit NetworkManager firewalld fail2ban
    nginx caddy mariadb pdns postfix dovecot php-fpm
    crowdsec auditd ollama
    phoenix-gateway phoenix-orchestrator gateway orchestrator
    phoenix-dove phoenix-eagle phoenix-swan phoenix-falcon
    phoenix-owl phoenix-hawk phoenix-heron phoenix-pigeon
    phoenix-kestrel phoenix-cormorant phoenix-raven phoenix-sparrow
)

[ -f "$TOOL_CONFIG" ] && source "$TOOL_CONFIG" 2>/dev/null

CHANGES_MADE=""
_change() { CHANGES_MADE="${CHANGES_MADE}  ${1}\n"; echo "  [CHANGED] ${1}"; }

# =============================================================================
# NODE AWARENESS
# =============================================================================

_is_svc() { systemctl cat "${1}.service" &>/dev/null; }

_installed=()
_detect() {
    _installed=()
    for svc in "${ALL_SERVICES[@]}"; do
        _is_svc "$svc" && _installed+=("$svc")
    done
}

# =============================================================================
# --send  (check all + optional restart of dead services)
# =============================================================================

_do_send_impl() {
    local fix=0
    [[ "${1:-}" == "--fix" ]] && fix=1

    _detect
    CHANGES_MADE=""
    local ok=0 fail=0 fixed=0 down_list=""

    echo "=== Service Check: $(ts_local) ==="
    echo "  Node: ${NODE_ID}"
    echo "  Installed: ${_installed[*]}"
    echo ""

    for svc in "${_installed[@]}"; do
        local state
        state=$(systemctl is-active "$svc" 2>/dev/null)
        if [ "$state" = "active" ]; then
            echo "  [OK] ${svc}"
            ok=$((ok + 1))
        else
            echo "  [DOWN] ${svc} (${state})"
            fail=$((fail + 1))
            down_list="${down_list} ${svc}"

            if [ "$fix" -eq 1 ]; then
                systemctl restart "$svc" 2>/dev/null
                sleep 2
                local new_state
                new_state=$(systemctl is-active "$svc" 2>/dev/null)
                if [ "$new_state" = "active" ]; then
                    _change "restart: ${svc} recovered"
                    fixed=$((fixed + 1))
                else
                    echo "    [STILL DOWN] ${svc}"
                fi
            fi
        fi
    done

    echo ""
    echo "  Listening ports:"
    ss -tlnp 2>/dev/null | grep -oP ':\K[0-9]+(?= )' | sort -n | uniq | tr '\n' ' '
    echo ""

    echo ""
    echo "  Online: ${ok}  Down: ${fail}  Fixed: ${fixed}"
    [ -n "$down_list" ] && echo "  Down:${down_list}"

    tool_audit "send" "ok=${ok} fail=${fail} fixed=${fixed} node=${NODE_ID}"
    return $fail
}

# =============================================================================
# --status  (JSON pill)
# =============================================================================

_do_status_impl() {
    _detect
    local ok=0 fail=0 down_list=""

    for svc in "${_installed[@]}"; do
        local state
        state=$(systemctl is-active "$svc" 2>/dev/null)
        if [ "$state" = "active" ]; then
            ok=$((ok + 1))
        else
            fail=$((fail + 1))
            down_list="${down_list} ${svc}"
        fi
    done

    local status="healthy"
    [ "$fail" -gt 0 ] && status="degraded"
    [ "$fail" -ge 3 ] && status="down"

    cat <<EOF
{
    "tool": "${TOOL_NAME}",
    "node": "${NODE_ID}",
    "status": "${status}",
    "services_up": ${ok},
    "services_total": $((ok + fail)),
    "services_down": "${down_list}",
    "checked_at": "$(date -u +%Y-%m-%dT%H:%M:%SZ)"
}
EOF
}

# =============================================================================
# --digest
# =============================================================================

_do_digest_impl() {
    echo "=== Service Report: ${NODE_ID} ==="
    echo "  Tool: ${TOOL_NAME} v${TOOL_VERSION}"
    echo ""
    _do_send_impl
    echo ""
    echo "--- All Running ---"
    systemctl list-units --type=service --state=running --no-pager 2>/dev/null | head -25
}

# =============================================================================
# --flush  (SAFE: only truncate logs/cache)
# =============================================================================

_do_flush_impl() {
    echo "=== Flush: ${NODE_ID} ==="
    [ -f "$TOOL_LOG" ] && truncate -s 0 "$TOOL_LOG" && echo "  [CHANGED] Truncated tool log"
    echo "Flush complete on ${NODE_ID}"
}

# =============================================================================
# --prune-services  (DANGEROUS: disable non-essential services, veil-gated)
# =============================================================================

_do_prune_services_impl() {
    echo "=== Service Prune: ${NODE_ID} ==="
    CHANGES_MADE=""

    local keep_pattern
    keep_pattern=$(IFS='|'; echo "${KEEP_SERVICES[*]}")

    local disabled=0 protected=0
    while IFS= read -r unit; do
        local name
        name="${unit%.service}"
        [ -z "$name" ] && continue

        if echo "$name" | grep -qE "^(${keep_pattern})$"; then
            protected=$((protected + 1))
        else
            systemctl disable --now "${name}.service" 2>/dev/null
            _change "disabled: ${name}"
            disabled=$((disabled + 1))
        fi
    done < <(systemctl list-unit-files --type=service --state=enabled \
              --no-legend --no-pager 2>/dev/null | awk '{print $1}')

    echo ""
    echo "  Protected: ${protected}  Disabled: ${disabled}"
    tool_audit "prune_services" "protected=${protected} disabled=${disabled} node=${NODE_ID}"
}

# =============================================================================
# --containers  (Podman/Docker container lifecycle)
# =============================================================================

_do_containers_impl() {
    local action="${1:-status}"
    echo "=== Containers: ${NODE_ID} ==="

    local engine=""
    if command -v podman &>/dev/null; then engine="podman"
    elif command -v docker &>/dev/null; then engine="docker"
    else echo "  No container engine found"; return 0; fi

    case "$action" in
        status|list)
            echo "  669427: ${engine}"
            echo "  Running:"
            $engine ps --format "    {{.Names}}  {{.Status}}  {{.Image}}" 2>/dev/null || echo "    (none)"
            echo ""
            echo "  All:"
            $engine ps -a --format "    {{.Names}}  {{.Status}}" 2>/dev/null | head -20
            echo ""
            echo "  Images: $($engine images -q 2>/dev/null | wc -l)"
            echo "  Disk: $($engine system df 2>/dev/null | tail -1 | awk '{print $4}' || echo unknown)"
            ;;
        health)
            echo "  669427: ${engine}"
            local c
            for c in $($engine ps -q 2>/dev/null); do
                local name state
                name=$($engine inspect --format '{{.Name}}' "$c" 2>/dev/null | sed 's|^/||')
                state=$($engine inspect --format '{{.State.Status}}' "$c" 2>/dev/null)
                local health="none"
                $engine inspect --format '{{.State.Health.Status}}' "$c" 2>/dev/null && health=$($engine inspect --format '{{.State.Health.Status}}' "$c" 2>/dev/null)
                echo "  ${name}: state=${state} health=${health}"
            done
            ;;
        restart)
            local target="${2:-}"
            [ -z "$target" ] && { echo "Usage: --containers restart <name>"; return 1; }
            $engine restart "$target" 2>/dev/null
            local new_state; new_state=$($engine inspect --format '{{.State.Status}}' "$target" 2>/dev/null)
            tool_audit "container_restart" "container=${target} state=${new_state} node=${NODE_ID}"
            echo "  [CHANGED] ${target}: ${new_state}"
            ;;
        logs)
            local target="${2:-}"
            [ -z "$target" ] && { echo "Usage: --containers logs <name>"; return 1; }
            $engine logs --tail 50 "$target" 2>/dev/null
            ;;
        prune)
            $engine system prune -f 2>/dev/null
            tool_audit "container_prune" "node=${NODE_ID}"
            echo "  [CHANGED] Pruned unused containers/images"
            ;;
        *)
            echo "  Actions: status|health|restart <name>|logs <name>|prune"
            ;;
    esac
}

# =============================================================================
# --cron  (Cron/scheduler management)
# =============================================================================

_do_cron_impl() {
    local action="${1:-status}"
    echo "=== Cron: ${NODE_ID} ==="

    case "$action" in
        status|list)
            for u in root snowdaddy automation phoenix; do
                local ct; ct=$(crontab -l -u "$u" 2>/dev/null | grep -v '^#' | grep -c '[^ ]' || echo 0)
                [ "$ct" -gt 0 ] && {
                    echo "  ${u}: ${ct} job(s)"
                    crontab -l -u "$u" 2>/dev/null | grep -v '^#' | grep '[^ ]' | sed 's/^/    /'
                }
            done
            ;;
        verify)
            echo "  Checking for stale cron references..."
            local stale=0
            for u in root snowdaddy automation phoenix; do
                crontab -l -u "$u" 2>/dev/null | grep -v '^#' | grep '[^ ]' | while read -r line; do
                    local script; script=$(echo "$line" | grep -oP '/opt/binary/tools/\S+' || true)
                    if [ -n "$script" ] && [ ! -f "$script" ]; then
                        echo "  [WARN] ${u}: references missing ${script}"
                        stale=$((stale + 1))
                    fi
                done
            done
            [ "$stale" -eq 0 ] && echo "  [OK] No stale references found"
            ;;
        *)
            echo "  Actions: status|list|verify"
            ;;
    esac
}

# =============================================================================
# --self-heal  (auto-detect and restart failed services)
# =============================================================================

_do_self_heal_impl() {
    echo "=== Self-Heal: ${NODE_ID} ==="
    _detect
    local healed=0
    for svc in "${_installed[@]}"; do
        local state; state=$(systemctl is-active "$svc" 2>/dev/null)
        if [ "$state" != "active" ]; then
            echo "  [DOWN] ${svc} — attempting restart..."
            systemctl restart "$svc" 2>/dev/null
            sleep 2
            local new_state; new_state=$(systemctl is-active "$svc" 2>/dev/null)
            if [ "$new_state" = "active" ]; then
                _change "healed: ${svc}"
                healed=$((healed + 1))
            else
                echo "  [FAIL] ${svc} did not recover"
            fi
        fi
    done
    [ "$healed" -eq 0 ] && echo "  [OK] All services healthy — nothing to heal"
    tool_audit "self_heal" "healed=${healed} node=${NODE_ID}"
}

# =============================================================================
# HELP
# =============================================================================

tool_help() {
    echo "${TOOL_NAME} v${TOOL_VERSION} -- Service Monitor & Control"
    echo ""
    echo "Actions:"
    echo "  --send [--fix]        Check services (--fix restarts dead ones)"
    echo "  --status              JSON pill for dashboard"
    echo "  --digest              Full report"
    echo "  --flush               Truncate logs only (safe)"
    echo "  --containers <act>    Container lifecycle (status|health|restart|logs|prune)"
    echo "  --cron <act>          Cron management (status|list|verify)"
    echo "  --self-heal           Auto-restart failed services"
    echo "  --prune-services      Disable non-essential services (DANGEROUS/veil-gated)"
    echo ""
    echo "DB: bird=${TOOL_BIRD} | tier=${TOOL_TIER} | node=${NODE_ID}"
}

# =============================================================================
# WRAPPERS + ROUTER
# =============================================================================

do_send()   { tool_wrap "send"   _do_send_impl "$@"; }
do_status() { tool_wrap "status" _do_status_impl "$@"; }
do_digest() { tool_wrap "digest" _do_digest_impl "$@"; }
do_flush()  { tool_wrap "flush"  _do_flush_impl "$@"; }

tool_init
trap 'tool_complete' EXIT

case "${1:---help}" in
    --send)           shift; do_send "$@" ;;
    --status)         shift; do_status "$@" ;;
    --digest)         shift; do_digest "$@" ;;
    --flush)          shift; do_flush "$@" ;;
    --containers)     shift; _do_containers_impl "$@" ;;
    --cron)           shift; _do_cron_impl "$@" ;;
    --self-heal)      _do_self_heal_impl ;;
    --prune-services) _do_prune_services_impl ;;
    --help)           tool_help ;;
    *)                handle_standard_command "$@" ;;
esac

