#!/usr/bin/env python3
import json
import os
import signal
import subprocess
import time
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
from pathlib import Path

BASE_DIR = Path(__file__).resolve().parent
PID_FILE = BASE_DIR / "run" / "opendartboard.pid"
LOG_FILE = BASE_DIR / "run" / "opendartboard.log"
HOST = "0.0.0.0"
PORT = 8090


def process_running(pid: int) -> bool:
    try:
        os.kill(pid, 0)
        return True
    except OSError:
        return False


def status_payload() -> dict:
    pid = None
    running = False
    if PID_FILE.exists():
        try:
            pid = int(PID_FILE.read_text().strip())
            running = process_running(pid)
        except (ValueError, OSError):
            pid = None
            running = False

    return {
        "ok": True,
        "running": running,
        "pid": pid if running else None,
    }


def tail_log(lines: int = 25) -> str:
    if not LOG_FILE.exists():
        return ""

    try:
        raw = LOG_FILE.read_bytes()[-12000:]
    except OSError:
        return ""

    text = raw.replace(b"\x00", b"").decode("utf-8", errors="replace")
    return "\n".join(text.splitlines()[-lines:])


def run_script(script_name: str, settle_seconds: float = 0) -> dict:
    script = BASE_DIR / script_name
    try:
        result = subprocess.run(
            [str(script)],
            cwd=str(BASE_DIR),
            text=True,
            capture_output=True,
            timeout=25,
            check=False,
        )
    except Exception as exc:
        return {
            "ok": False,
            "code": 1,
            "output": str(exc),
            **status_payload(),
        }

    output = "\n".join(part for part in [result.stdout.strip(), result.stderr.strip()] if part)
    deadline = time.time() + settle_seconds
    if result.returncode == 0 and script_name.startswith("start"):
        while time.time() < deadline:
            time.sleep(0.5)

    payload = status_payload()
    ok = result.returncode == 0

    if script_name.startswith("start") and ok and not payload["running"]:
        log_tail = tail_log()
        if log_tail:
            output = "\n\nSenaste logg:\n" + log_tail if not output else output + "\n\nSenaste logg:\n" + log_tail
        ok = False

    return {
        "ok": ok,
        "code": result.returncode,
        "output": output,
        **payload,
    }


def control_page() -> str:
    return """<!doctype html>
<html lang="sv">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width,initial-scale=1">
  <title>OpenDartboard kontroll</title>
  <style>
    html,body{margin:0;height:100%;background:#050607;color:#eef3f5;font-family:Arial,sans-serif;overflow:hidden}
    body{display:grid;grid-template-rows:1fr}
    .topbar{position:fixed;top:0;left:0;right:0;z-index:5;height:58px;background:linear-gradient(180deg,rgba(5,6,7,.94),rgba(5,6,7,.62));border-bottom:1px solid rgba(255,255,255,.08);display:flex;align-items:center;justify-content:center;pointer-events:none}
    .bar{display:flex;gap:8px;align-items:center;padding:8px 10px;background:rgba(17,22,26,.88);border:1px solid rgba(255,255,255,.16);border-radius:8px;box-shadow:0 8px 24px rgba(0,0,0,.42);pointer-events:auto}
    .state{min-width:110px;font-size:13px;text-align:center;color:#c9d2d8}.ok{color:#46c37b}.bad{color:#e46d6d}.warn{color:#e4b74f}
    button,a{min-width:82px;border:1px solid #3a4a54;border-radius:6px;padding:8px 11px;background:#24313a;color:#fff;text-decoration:none;cursor:pointer;font-weight:700;text-align:center}
    button.start{background:#1f6f43;border-color:#2e965d}button.stop{background:#743033;border-color:#9a4549}
    button:hover,a:hover{filter:brightness(1.12)}button:disabled{opacity:.55;cursor:wait;filter:none}
    .stage{display:grid;place-items:start center;background:#050607;overflow:hidden}
    img{width:100vw;height:calc(100vh - 58px);margin-top:58px;object-fit:contain;object-position:top center;display:block}
    @media(max-width:620px){.topbar{height:96px}.bar{flex-wrap:wrap;justify-content:center}.state{width:100%;min-width:0}img{height:calc(100vh - 96px);margin-top:96px}}
  </style>
</head>
<body>
  <div class="topbar"><div class="bar">
    <span id="state" class="state warn">kollar...</span>
    <button id="start" class="start" type="button">Starta</button>
    <button id="stop" class="stop" type="button">Stäng av</button>
    <a id="raw" href="http://192.168.10.10:8081/">Raw stream</a>
  </div></div>
  <div class="stage"><img id="stream" alt="OpenDartboard stream"></div>
  <script>
    const host = location.hostname;
    const base = `${location.protocol}//${host}:${location.port || 8090}`;
    const stream = document.getElementById("stream");
    const state = document.getElementById("state");
    const start = document.getElementById("start");
    const stop = document.getElementById("stop");
    document.getElementById("raw").href = `http://${host}:8081/`;

    function refreshStream() {
      stream.src = `http://${host}:8081/stream.mjpg?t=${Date.now()}`;
    }

    function setStatus(data) {
      state.textContent = data.running ? `igång (${data.pid})` : "stoppad";
      state.className = `state ${data.running ? "ok" : "bad"}`;
      start.disabled = Boolean(data.running);
      stop.disabled = !data.running;
      if (data.running && !stream.src) refreshStream();
    }

    async function status() {
      try {
        const res = await fetch(`${base}/status`, { cache: "no-store" });
        setStatus(await res.json());
      } catch (error) {
        state.textContent = "kontrollfel";
        state.className = "state bad";
        start.disabled = false;
        stop.disabled = true;
      }
    }

    async function action(name) {
      start.disabled = true;
      stop.disabled = true;
      state.textContent = name === "start" ? "startar..." : "stoppar...";
      state.className = "state warn";
      await fetch(`${base}/${name}`, { method: "POST" });
      if (name === "start") refreshStream();
      setTimeout(status, 1200);
    }

    start.onclick = () => action("start");
    stop.onclick = () => action("stop");
    refreshStream();
    status();
    setInterval(status, 5000);
  </script>
</body>
</html>
"""


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

    def send_json(self, payload: dict, status: int = 200):
        data = json.dumps(payload).encode("utf-8")
        self.send_response(status)
        self.send_header("Content-Type", "application/json; charset=utf-8")
        self.send_header("Cache-Control", "no-store")
        self.send_header("Access-Control-Allow-Origin", "*")
        self.send_header("Access-Control-Allow-Methods", "GET, POST, OPTIONS")
        self.send_header("Access-Control-Allow-Headers", "Content-Type")
        self.send_header("Content-Length", str(len(data)))
        self.end_headers()
        self.wfile.write(data)

    def send_html(self, body: str, status: int = 200):
        data = body.encode("utf-8")
        self.send_response(status)
        self.send_header("Content-Type", "text/html; charset=utf-8")
        self.send_header("Cache-Control", "no-store")
        self.send_header("Content-Length", str(len(data)))
        self.end_headers()
        self.wfile.write(data)

    def do_OPTIONS(self):
        self.send_response(204)
        self.send_header("Access-Control-Allow-Origin", "*")
        self.send_header("Access-Control-Allow-Methods", "GET, POST, OPTIONS")
        self.send_header("Access-Control-Allow-Headers", "Content-Type")
        self.end_headers()

    def do_GET(self):
        if self.path.startswith("/status"):
            self.send_json(status_payload())
            return
        if self.path == "/" or self.path.startswith("/?"):
            self.send_html(control_page())
            return
        self.send_json({"ok": False, "error": "Unknown endpoint"}, 404)

    def do_POST(self):
        if self.path.startswith("/start"):
            self.send_json(run_script("start-dartmachine.sh", settle_seconds=6))
            return
        if self.path.startswith("/stop"):
            self.send_json(run_script("stop-dartmachine.sh"))
            return
        if self.path.startswith("/status"):
            self.send_json(status_payload())
            return
        self.send_json({"ok": False, "error": "Unknown endpoint"}, 404)


if __name__ == "__main__":
    (BASE_DIR / "run").mkdir(exist_ok=True)
    ThreadingHTTPServer((HOST, PORT), Handler).serve_forever()
