#!/usr/bin/env python3
"""ExecStartPre validator for overdeck-seat-proxy@.service — parses seat-proxy.env as inert data."""

from __future__ import annotations

import os
import re
import stat
import sys

SEAT_PROXY_LIB = "/var/lib/overdeck/seat-proxy"
SEAT_PROXY_USER = "overdeck-seat-proxy"
ALLOWED_KEYS = ("CCP_CODEX_MODEL", "CCP_CONFIG_DIR", "PORT", "CCP_BIND_ADDRESS")
CONTROL_RE = re.compile(r"[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]")
LINE_RE = re.compile(r"^([A-Z][A-Z0-9_]*)=(.*)\Z")
GPT_MODEL_RE = re.compile(r"^gpt-[A-Za-z0-9][A-Za-z0-9._-]*$")
PORT_RE = re.compile(r"^[1-9][0-9]{0,4}$")
BIND_ADDRESS_RE = re.compile(
    r"^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}"
    r"(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$"
)


def fail(message: str) -> None:
    print(f"overdeck-seat-proxy-validate: {message}", file=sys.stderr)
    raise SystemExit(1)


def parse_env_file(path: str) -> dict[str, str]:
    try:
        raw = open(path, encoding="utf-8", newline="").read()
    except OSError:
        fail("env-unreadable")
    if CONTROL_RE.search(raw):
        fail("env-control-char")
    if "$(" in raw or "`" in raw:
        fail("env-command-substitution")
    values: dict[str, str] = {}
    for lineno, line in enumerate(raw.splitlines(), start=1):
        if not line.strip():
            continue
        if line.lstrip().startswith("#"):
            fail(f"env-comment:{lineno}")
        if CONTROL_RE.search(line):
            fail(f"env-control-char:{lineno}")
        match = LINE_RE.match(line)
        if not match:
            fail(f"env-malformed:{lineno}")
        key, raw_value = match.group(1), match.group(2)
        if key not in ALLOWED_KEYS:
            fail(f"env-unknown-key:{key}")
        if key in values:
            fail(f"env-duplicate-key:{key}")
        if raw_value == "":
            fail(f"env-empty-value:{key}")
        if raw_value.startswith("'"):
            fail(f"env-unsafe-quoting:{key}")
        if raw_value.startswith('"'):
            if not raw_value.endswith('"') or len(raw_value) < 2:
                fail(f"env-unsafe-quoting:{key}")
            inner = raw_value[1:-1]
            if '"' in inner or "\\" in inner:
                fail(f"env-unsafe-quoting:{key}")
            value = inner
        else:
            if any(ch in raw_value for ch in " \t\"'\\#"):
                fail(f"env-unsafe-quoting:{key}")
            value = raw_value
        if CONTROL_RE.search(value):
            fail(f"env-control-char:{key}")
        if "$(" in value or "`" in value:
            fail(f"env-command-substitution:{key}")
        values[key] = value
    for key in ALLOWED_KEYS:
        if key not in values:
            fail(f"env-missing-key:{key}")
    validate_parsed_values(values)
    return values


def validate_parsed_values(values: dict[str, str], *, config_dir: str | None = None) -> None:
    if not GPT_MODEL_RE.fullmatch(values["CCP_CODEX_MODEL"]):
        fail("ccp-model-invalid")
    validate_port(values["PORT"])
    validate_bind_address(values["CCP_BIND_ADDRESS"])
    if config_dir is not None:
        expected = config_dir if config_dir.endswith("/") else f"{config_dir}/"
        got = values["CCP_CONFIG_DIR"]
        normalized = got if got.endswith("/") else f"{got}/"
        if normalized != expected:
            fail("ccp-config-dir-mismatch")


def check_mode(path: str, expected_mode: int, label: str) -> None:
    try:
        mode = stat.S_IMODE(os.stat(path).st_mode)
    except OSError:
        fail(f"{label}-missing")
    if mode != expected_mode:
        fail(f"{label}-mode")


def check_owner(path: str, expected: str, label: str) -> None:
    try:
        st = os.stat(path)
    except OSError:
        fail(f"{label}-missing")
    try:
        import grp
        import pwd

        owner = pwd.getpwuid(st.st_uid).pw_name
        group = grp.getgrgid(st.st_gid).gr_name
    except KeyError:
        fail(f"{label}-owner")
    if f"{owner}:{group}" != expected:
        fail(f"{label}-owner")


def validate_bind_address(address: str) -> None:
    if not BIND_ADDRESS_RE.fullmatch(address):
        fail("bind-address-invalid")
    if address.startswith("127."):
        fail("bind-address-loopback")


def validate_port(port: str) -> None:
    if not PORT_RE.fullmatch(port):
        fail("port-invalid")
    number = int(port, 10)
    if number < 1 or number > 65535:
        fail("port-invalid")


def main(argv: list[str]) -> None:
    if len(argv) == 3 and argv[1] == "--parse-only":
        values = parse_env_file(argv[2])
        for key in ALLOWED_KEYS:
            print(f"{key}={values[key]}")
        return

    if len(argv) != 2:
        fail("missing-instance")
    instance = argv[1]
    if not re.fullmatch(r"[A-Za-z0-9][A-Za-z0-9._-]*", instance):
        fail("invalid-instance")

    config_dir = os.path.join(SEAT_PROXY_LIB, instance)
    env_file = os.path.join(config_dir, "seat-proxy.env")
    if not os.path.isfile(env_file):
        fail("env-missing")

    check_owner(env_file, "root:root", "env")
    check_mode(env_file, 0o600, "env")
    if not os.path.isdir(config_dir):
        fail("config-dir-missing")
    check_owner(config_dir, f"{SEAT_PROXY_USER}:{SEAT_PROXY_USER}", "config-dir")
    check_mode(config_dir, 0o700, "config-dir")

    values = parse_env_file(env_file)
    validate_parsed_values(values, config_dir=config_dir)

    proxy_bin = "/usr/local/bin/claude-code-proxy"
    if not os.path.isfile(proxy_bin) or not os.access(proxy_bin, os.X_OK):
        fail("proxy-binary-missing")


if __name__ == "__main__":
    main(sys.argv)
