#!/usr/bin/env python3
"""On-demand xfreerdp manager. HTTP on port 7001."""
import json
import logging
import os
import subprocess
import threading
import time
from http.server import BaseHTTPRequestHandler, HTTPServer

logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s %(message)s")
log = logging.getLogger("rdp-manager")

DISPLAY = os.environ.get("DISPLAY", ":1")
RDP_HOST = os.environ.get("RDP_HOST", "")
RDP_PORT = os.environ.get("RDP_PORT", "3389")
RDP_USER = os.environ.get("RDP_USER", "")
RDP_PASSWORD = os.environ.get("RDP_PASSWORD", "")
RDP_DOMAIN = os.environ.get("RDP_DOMAIN", "")
RDP_SECURITY = os.environ.get("RDP_SECURITY", "")

STATE_FILE = "/tmp/rdp_state.json"

_lock = threading.Lock()
_proc: subprocess.Popen | None = None
_should_be_connected = False


def _save_state():
    try:
        with open(STATE_FILE, "w") as f:
            json.dump({"should_be_connected": _should_be_connected}, f)
    except Exception:
        pass


def _load_state() -> bool:
    try:
        with open(STATE_FILE) as f:
            return json.load(f).get("should_be_connected", False)
    except Exception:
        return False


def _build_args():
    args = [
        "xfreerdp",
        f"/v:{RDP_HOST}:{RDP_PORT}",
        "/cert:ignore",
        "/f",
        "/dynamic-resolution",
        "/gfx-h264:avc444",
        "/network:auto",
        "+clipboard",
    ]
    if RDP_SECURITY:
        args.append(f"/sec:{RDP_SECURITY}")
    if RDP_USER:
        args.append(f"/u:{RDP_USER}")
    if RDP_PASSWORD:
        args.append(f"/p:{RDP_PASSWORD}")
    if RDP_DOMAIN:
        args.append(f"/d:{RDP_DOMAIN}")
    return args


def _launch():
    global _proc
    env = dict(os.environ)
    env["DISPLAY"] = DISPLAY
    log_file = open("/tmp/xfreerdp.log", "a")
    _proc = subprocess.Popen(_build_args(), stdout=log_file, stderr=log_file, env=env)
    log.info("xfreerdp started pid=%s target=%s:%s", _proc.pid, RDP_HOST, RDP_PORT)
    return _proc


def _monitor_loop():
    while True:
        time.sleep(5)
        with _lock:
            if not _should_be_connected:
                continue
            if _proc is None or _proc.poll() is not None:
                log.info("xfreerdp exited unexpectedly, reconnecting in 3s")
                time.sleep(3)
                _launch()


def _anti_idle_loop():
    env = {**os.environ, "DISPLAY": DISPLAY}
    toggle = False
    while True:
        time.sleep(60)
        with _lock:
            active = _should_be_connected and _proc is not None and _proc.poll() is None
        if not active:
            continue
        try:
            r = subprocess.run(
                ["xdotool", "search", "--name", "FreeRDP"],
                env=env, capture_output=True, timeout=5,
            )
            win_id = r.stdout.decode().strip().splitlines()[0] if r.stdout.strip() else ""
            if win_id:
                x, y = (5, 80) if toggle else (6, 81)
                subprocess.run(
                    ["xdotool", "mousemove", "--window", win_id, str(x), str(y)],
                    env=env, capture_output=True, timeout=5,
                )
                subprocess.run(
                    ["xdotool", "click", "--window", win_id, "1"],
                    env=env, capture_output=True, timeout=5,
                )
                subprocess.run(
                    ["xdotool", "key", "--window", win_id, "--clearmodifiers", "shift"],
                    env=env, capture_output=True, timeout=5,
                )
                toggle = not toggle
                log.debug("anti_idle click window=%s pos=%s,%s", win_id, x, y)
            else:
                log.debug("anti_idle: xfreerdp window not found")
        except Exception as e:
            log.debug("anti_idle error: %s", e)


threading.Thread(target=_monitor_loop, daemon=True).start()
threading.Thread(target=_anti_idle_loop, daemon=True).start()


class Handler(BaseHTTPRequestHandler):
    def log_message(self, fmt, *args):
        pass

    def _json(self, code, data):
        body = json.dumps(data).encode()
        self.send_response(code)
        self.send_header("Content-Type", "application/json")
        self.send_header("Content-Length", str(len(body)))
        self.end_headers()
        self.wfile.write(body)

    def do_GET(self):
        if self.path == "/health":
            with _lock:
                connected = _proc is not None and _proc.poll() is None
                pid = _proc.pid if connected else None
            self._json(200, {
                "connected": connected,
                "pid": pid,
                "target": f"{RDP_HOST}:{RDP_PORT}",
                "should_be_connected": _should_be_connected,
            })
        else:
            self._json(404, {"error": "not found"})

    def do_POST(self):
        global _proc, _should_be_connected
        if self.path == "/connect":
            with _lock:
                _should_be_connected = True
                _save_state()
                if _proc is not None and _proc.poll() is None:
                    self._json(200, {"ok": True, "pid": _proc.pid, "already": True})
                    return
                proc = _launch()
            self._json(200, {"ok": True, "pid": proc.pid})
        elif self.path == "/disconnect":
            with _lock:
                _should_be_connected = False
                _save_state()
                if _proc is not None:
                    _proc.terminate()
                    try:
                        _proc.wait(timeout=5)
                    except subprocess.TimeoutExpired:
                        _proc.kill()
                    _proc = None
            log.info("xfreerdp disconnected")
            self._json(200, {"ok": True})
        else:
            self._json(404, {"error": "not found"})


if __name__ == "__main__":
    if not RDP_HOST:
        log.warning("RDP_HOST not set — connect calls will fail")
    if _load_state():
        log.info("restoring state: reconnecting xfreerdp")
        _should_be_connected = True
        _launch()
    log.info("manager started on :7001")
    HTTPServer(("0.0.0.0", 7001), Handler).serve_forever()
