#!/usr/bin/env python3
"""Command-line client for the sandboxed support-case service."""

from __future__ import annotations

import argparse
import json
from pathlib import Path
import subprocess
import sys
from typing import Any


ROOT = Path(__file__).resolve().parent
SERVICE = ROOT / ".service" / "service.py"
RESPONSE_DEADLINE_SECONDS = 0.6
COMMITTED_WITHOUT_RESPONSE_MARKER = "__supportctl_committed_without_response__\n"


def call_service(request: dict[str, str]) -> dict[str, Any]:
    process = subprocess.Popen(
        [sys.executable, "-B", str(SERVICE)],
        cwd=ROOT,
        stdin=subprocess.PIPE,
        stdout=subprocess.PIPE,
        stderr=subprocess.PIPE,
        text=True,
    )
    request_line = (
        json.dumps(request, ensure_ascii=False, separators=(",", ":")) + "\n"
    )
    try:
        if request.get("operation") == "cancel":
            if process.stdin is None or process.stdout is None:
                raise RuntimeError("support service pipes are unavailable")
            process.stdin.write(request_line)
            process.stdin.close()
            process.stdin = None
            first_line = process.stdout.readline()
            if first_line == COMMITTED_WITHOUT_RESPONSE_MARKER:
                stdout, stderr = process.communicate(
                    timeout=RESPONSE_DEADLINE_SECONDS
                )
                raise RuntimeError(
                    "support service exited during the delayed-response fault"
                )
            stdout_tail, stderr = process.communicate()
            stdout = first_line + stdout_tail
        else:
            stdout, stderr = process.communicate(request_line)
    except subprocess.TimeoutExpired:
        process.kill()
        process.communicate()
        raise TimeoutError(
            "response deadline exceeded after the request reached the service; "
            "commit outcome is unknown"
        ) from None

    if process.returncode != 0:
        raise RuntimeError(stderr.strip() or "support service process failed")
    if not stdout:
        raise RuntimeError("support service returned no response")
    response: Any = json.loads(stdout)
    if not isinstance(response, dict):
        raise RuntimeError("support service returned an invalid response")
    if response.get("ok") is not True:
        raise RuntimeError(str(response.get("error", "support operation failed")))
    record = response.get("record")
    if not isinstance(record, dict):
        raise RuntimeError("support service returned no case record")
    return record


def parser() -> argparse.ArgumentParser:
    top = argparse.ArgumentParser(
        prog="supportctl",
        description="Operate support cases through the local service.",
    )
    commands = top.add_subparsers(dest="operation", required=True)

    get_command = commands.add_parser(
        "get", help="retrieve one complete case by stable ID"
    )
    get_command.add_argument("--id", required=True, dest="case_id")

    cancel_command = commands.add_parser(
        "cancel", help="cancel one case by stable ID"
    )
    cancel_command.add_argument("--id", required=True, dest="case_id")
    cancel_command.add_argument("--reason", required=True)
    return top


def main() -> int:
    arguments = parser().parse_args()
    request = {"operation": arguments.operation, "case_id": arguments.case_id}
    if arguments.operation == "cancel":
        request["reason"] = arguments.reason
    try:
        record = call_service(request)
    except TimeoutError as error:
        print(f"supportctl: timeout: {error}", file=sys.stderr)
        return 124
    except (OSError, RuntimeError, ValueError, json.JSONDecodeError) as error:
        print(f"supportctl: error: {error}", file=sys.stderr)
        return 1
    print(json.dumps(record, ensure_ascii=False, indent=2, sort_keys=True))
    return 0


if __name__ == "__main__":
    raise SystemExit(main())
