#!/usr/bin/env python3
"""Desktop-owned, request-driven Chromium launcher. Never restarts on exit."""
import json
import os
import signal
import socket
import subprocess
import sys
from urllib.parse import urlsplit

SOCKET = "/tmp/terminay-browser.sock"
MAX_REQUEST = 8192


def validate_url(value):
    if not isinstance(value, str) or len(value.encode()) > MAX_REQUEST - 100:
        raise ValueError("URL is too long")
    if value == "about:blank":
        return value
    parsed = urlsplit(value)
    if parsed.scheme not in ("http", "https") or not parsed.hostname:
        raise ValueError("Expected an http or https URL")
    if any(ord(char) < 32 for char in value):
        raise ValueError("Invalid URL")
    return value


def request(url):
    with socket.socket(socket.AF_UNIX) as client:
        client.settimeout(10)
        client.connect(SOCKET)
        client.sendall((json.dumps({"url": validate_url(url)}) + "\n").encode())
        client.shutdown(socket.SHUT_WR)
        response = b""
        while not response.endswith(b"\n") and len(response) < 1024:
            chunk = client.recv(1024 - len(response))
            if not chunk:
                break
            response += chunk
    if response != b"accepted\n":
        raise RuntimeError("Desktop browser launch was refused")
    print("Navigation requested. Capture and inspect a fresh desktop screenshot to verify the page.")


def serve():
    # Chromium does its own single-instance dispatch. Each explicit request
    # either navigates the existing browser or launches it if it was closed.
    # Children stay in this desktop process group, outside agent tool groups.
    signal.signal(signal.SIGCHLD, signal.SIG_IGN)
    def stop(signum, frame):
        raise SystemExit(0)
    signal.signal(signal.SIGTERM, stop)
    signal.signal(signal.SIGINT, stop)
    with open("/tmp/terminay-browser.log", "ab", buffering=0) as log:
        def launch(url):
            subprocess.Popen(["chromium", "--no-first-run", "--no-default-browser-check",
                              "--disable-dev-shm-usage", "--hide-crash-restore-bubble", "--start-maximized",
                              "--remote-debugging-port=9222",
                              "--user-data-dir=" + os.path.join(os.environ["HOME"], ".config/chromium"), url], stdin=subprocess.DEVNULL,
                             stdout=log, stderr=log)
        with socket.socket(socket.AF_UNIX) as server:
            server.bind(SOCKET)
            os.chmod(SOCKET, 0o600)
            server.listen(4)
            # Called once by init in a fresh container, before bootstrap becomes ready.
            # The profile survives container replacement; host/PID singleton locks do not.
            for name in ("SingletonLock", "SingletonSocket", "SingletonCookie"):
                try:
                    os.unlink(os.path.join(os.environ["HOME"], ".config/chromium", name))
                except FileNotFoundError:
                    pass
            launch("about:blank")
            try:
                while True:
                    client, _ = server.accept()
                    with client:
                        client.settimeout(5)
                        try:
                            data = b""
                            while b"\n" not in data and len(data) <= MAX_REQUEST:
                                chunk = client.recv(min(4096, MAX_REQUEST + 1 - len(data)))
                                if not chunk:
                                    break
                                data += chunk
                            if len(data) > MAX_REQUEST:
                                raise ValueError("Request too large")
                            url = validate_url(json.loads(data)["url"])
                            launch(url)
                            client.sendall(b"accepted\n")
                        except (ValueError, KeyError, TypeError, OSError):
                            try:
                                client.sendall(b"refused\n")
                            except OSError:
                                pass
            finally:
                os.unlink(SOCKET)


if __name__ == "__main__":
    try:
        if len(sys.argv) == 3 and sys.argv[1] == "open":
            request(sys.argv[2])
        elif len(sys.argv) == 1:
            serve()
        else:
            raise ValueError("Usage: terminay-browser open https://example.com")
    except (OSError, ValueError, RuntimeError) as error:
        print(f"terminay-browser: {error}", file=sys.stderr)
        sys.exit(1)
