"""Canonical unprivileged client for seat execution descriptor handoff."""

from __future__ import annotations

import argparse
import json
import os
import socket
import sys

from seat_common import SEAT_ID_RE, die as _common_die
from seat_execution_broker import (EXECUTION_ENV_KEYS, GENERATION_RE, MAX_MESSAGE, fd_exec,
                                   receive_capabilities, validate_execution_environment)


def die(message: str, code: int = 1) -> None:
    _common_die(f"overdeck-seat-execution-client: {message}", code)


def narrow_environment(environment: dict[str, str]) -> dict[str, str]:
    env = validate_execution_environment(environment)
    required = {"HOME", "USER", "LOGNAME", "PATH", "OVERDECK_SEAT_ID", "OVERDECK_SEAT_GENERATION"}
    if not required.issubset(env) or set(env) - EXECUTION_ENV_KEYS:
        die("environment-invalid")
    return env


def connect_and_receive(socket_path: str, request: dict[str, object]) -> tuple[dict[str, object], list[int]]:
    payload = (json.dumps(request, separators=(",", ":")) + "\n").encode()
    if len(payload) > MAX_MESSAGE:
        die("request-too-large")
    sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
    try:
        sock.connect(socket_path)
        sock.sendall(payload)
        return receive_capabilities(sock, expected_count=2)
    except OSError:
        die("broker-unavailable")
    finally:
        sock.close()


def main(argv: list[str] | None = None) -> None:
    parser = argparse.ArgumentParser(prog="overdeck-seat-execution-client")
    parser.add_argument("--socket", required=True)
    parser.add_argument("--seat-id", required=True)
    parser.add_argument("--generation", required=True)
    parser.add_argument("--phase", choices=("admission", "runtime"), required=True)
    parser.add_argument("args", nargs=argparse.REMAINDER)
    ns = parser.parse_args(argv)
    if not SEAT_ID_RE.match(ns.seat_id) or not GENERATION_RE.match(ns.generation):
        die("identity-invalid")
    if not ns.socket.startswith("/") or "//" in ns.socket:
        die("socket-invalid")
    args = list(ns.args)
    if args[:1] == ["--"]:
        args = args[1:]
    request = {"seatId": ns.seat_id, "generation": ns.generation, "phase": ns.phase}
    response, fds = connect_and_receive(ns.socket, request)
    environment = response.get("environment")
    expected_response = {**request, "fdCount": 2, "environment": environment}
    if response != expected_response or not isinstance(environment, dict):
        for fd in fds:
            os.close(fd)
        die("response-identity-mismatch")
    try:
        execution_environment = narrow_environment(environment)
    except SystemExit:
        for fd in fds:
            os.close(fd)
        raise
    executable_name = "seat-admission" if ns.phase == "admission" else "seat-runtime"
    try:
        fd_exec(fds[0], fds[1], [executable_name, *args], execution_environment)
    finally:
        for fd in fds:
            os.close(fd)


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