"""Bridge-Einstieg, Protokoll v1 (docs/BRIDGE-PROTOKOLL.md).

Aufruf durch das Telegram-Plugin, feste Argumentliste, keine Shell:
    /usr/bin/python3 -I -m aria_router.bridge_cli --config /abs/gateway.json
stdin: genau ein JSON-Objekt (max. 1 MiB). stdout: genau eine JSON-Zeile.
Exit 0 bei jeder JSON-Antwort (auch handling=error); Exit 2 nur bei
falschem Aufruf. Das Plugin wendet bei handling=error seine
router_failure_mode-Regel an (siehe docs/OFFENE-ENTSCHEIDUNGEN.md).

    ... --config X --status   -> Health/Budget/Schlüsselstatus (ohne Secrets)
"""
from __future__ import annotations

import argparse
import json
import sys
from typing import BinaryIO, Callable, Optional, TextIO

from .config import ConfigurationError, load_gateway_config
from .gateway import PROTOCOL_VERSION, Gateway, GatewayRequest, RequestError

MAX_STDIN_BYTES = 1024 * 1024
REPORT_FIELDS = frozenset({"protocol", "type", "job_id", "nonce", "ok"})


def error_response(code: str, *, shadow: bool = False) -> dict:
    return {
        "protocol": PROTOCOL_VERSION, "handling": "error", "code": code, "job_id": None, "backend": None,
        "status": None, "reply_text": None, "forward": None, "flags": [], "shadow": shadow,
        "planned_handling": None, "privacy": None, "delivered": False,
    }


def _emit(stdout: TextIO, payload: dict) -> None:
    stdout.write(json.dumps(payload, ensure_ascii=False, separators=(",", ":")) + "\n")
    stdout.flush()


def run(argv, stdin: BinaryIO, stdout: TextIO,
        gateway_factory: Optional[Callable[..., Gateway]] = None) -> int:
    parser = argparse.ArgumentParser(prog="aria_router.bridge_cli", add_help=True)
    parser.add_argument("--config", required=True)
    parser.add_argument("--status", action="store_true")
    try:
        args = parser.parse_args(argv)
    except SystemExit:
        return 2
    try:
        config = load_gateway_config(args.config)
        gateway = (gateway_factory or Gateway)(config)
    except ConfigurationError as exc:
        sys.stderr.write(f"aria-v3 bridge: configuration rejected: {exc}\n")
        _emit(stdout, error_response("CONFIG_UNAVAILABLE"))
        return 0
    if args.status:
        _emit(stdout, gateway.status())
        return 0
    raw = stdin.read(MAX_STDIN_BYTES + 1)
    if len(raw) > MAX_STDIN_BYTES:
        _emit(stdout, error_response("INPUT_LIMIT"))
        return 0
    try:
        data = json.loads(raw.decode("utf-8"))
    except (UnicodeDecodeError, ValueError):
        _emit(stdout, error_response("INVALID_JSON"))
        return 0
    if not isinstance(data, dict):
        _emit(stdout, error_response("INVALID_REQUEST"))
        return 0
    if data.get("protocol") != PROTOCOL_VERSION or type(data.get("protocol")) is not int:
        _emit(stdout, error_response("UNSUPPORTED_PROTOCOL"))
        return 0
    kind = data.get("type", "inbound")
    if kind == "delivery_report":
        if set(data) != REPORT_FIELDS or not isinstance(data["job_id"], str) \
                or not isinstance(data["nonce"], str) or type(data["ok"]) is not bool:
            _emit(stdout, error_response("INVALID_REQUEST"))
            return 0
        accepted = gateway.report_delivery(job_id=data["job_id"], nonce=data["nonce"], ok=data["ok"])
        _emit(stdout, {"protocol": PROTOCOL_VERSION, "handling": "ack",
                       "code": "REPORTED" if accepted else "UNKNOWN_CLEARANCE", "delivered": False})
        return 0
    if kind != "inbound":
        _emit(stdout, error_response("INVALID_REQUEST"))
        return 0
    mode = data.get("mode")
    if mode not in ("shadow", "execute"):
        _emit(stdout, error_response("INVALID_MODE"))
        return 0
    payload = {k: v for k, v in data.items() if k not in ("protocol", "type", "mode")}
    try:
        request = GatewayRequest.from_bridge(payload, max_text_bytes=config.limits.max_text_bytes,
                                             max_attachments=config.limits.max_attachments)
    except RequestError as exc:
        gateway.log_rejected_request(exc.code, shadow=mode == "shadow")
        _emit(stdout, error_response(exc.code, shadow=mode == "shadow"))
        return 0
    response = gateway.handle(request) if mode == "execute" else gateway.shadow(request)
    _emit(stdout, response.to_dict())
    return 0


def main() -> None:
    sys.exit(run(sys.argv[1:], sys.stdin.buffer, sys.stdout))


if __name__ == "__main__":
    main()
