#!/usr/bin/env python3
"""
Conservative VNC orphan cleanup — NOT for active sessions.

Only removes duplicate handlers when:
  - 2+ clients on port 5900, AND
  - the older one has been up for 90+ seconds

The periodic 15s aggressive watchdog caused reconnect loops.
"""

import os
import signal
import subprocess
import time

VNC_PORT = "5900"
LOG_FILE = "/tmp/vnc-watchdog.log"
MIN_AGE_SECONDS = 90


def log(msg):
    with open(LOG_FILE, "a") as f:
        f.write(f"{time.strftime('%H:%M:%S')} {msg}\n")


def process_start_epoch(pid):
    try:
        result = subprocess.run(
            ["ps", "-p", str(pid), "-o", "lstart="],
            capture_output=True,
            text=True,
            timeout=3,
        )
        if not result.stdout.strip():
            return None
        return time.mktime(
            time.strptime(result.stdout.strip(), "%a %b %d %H:%M:%S %Y")
        )
    except Exception:
        return None


def is_protected(pid):
    try:
        result = subprocess.run(
            ["ps", "-p", str(pid), "-o", "command="],
            capture_output=True,
            text=True,
            timeout=3,
        )
        if "screensharingd" in result.stdout:
            return True
    except Exception:
        pass
    return False


def get_handlers():
    handlers = []
    try:
        result = subprocess.run(
            ["lsof", "-i", f":{VNC_PORT}", "-n", "-P"],
            capture_output=True,
            text=True,
            timeout=5,
        )
        for line in result.stdout.strip().split("\n")[1:]:
            if "ESTABLISHED" not in line or "->" not in line:
                continue
            parts = line.split()
            if len(parts) < 2:
                continue
            pid = int(parts[1])
            if is_protected(pid):
                continue
            started = process_start_epoch(pid)
            handlers.append({"pid": pid, "started": started or 0})
    except Exception:
        pass
    return handlers


def cleanup():
    handlers = get_handlers()
    if len(handlers) < 2:
        return

    handlers.sort(key=lambda h: h["pid"], reverse=True)
    newest = handlers[0]
    now = time.time()

    for stale in handlers[1:]:
        age = now - stale["started"] if stale["started"] else 0
        if age < MIN_AGE_SECONDS:
            continue
        try:
            os.kill(stale["pid"], signal.SIGTERM)
            log(
                f"Orphan cleanup: killed PID {stale['pid']} "
                f"(age {int(age)}s, kept PID {newest['pid']})"
            )
        except (ProcessLookupError, PermissionError):
            pass


if __name__ == "__main__":
    cleanup()
