"""Run a real local service and SDK query, then close both owners.

Run ``uv run --extra service --extra http python examples/service.py``.
Add ``--cli`` (and the ``cli`` extra) to call the remote CLI in real subprocesses.
The temporary file is an explicitly trusted demonstration, not production RPKI
data. The listener binds an OS-selected loopback port and stops automatically.
"""

from __future__ import annotations

import argparse
import json
import socket
import sys
from datetime import UTC, datetime
from pathlib import Path
from tempfile import TemporaryDirectory

import anyio
import uvicorn

from rpkiparrot.config import (
    ClientConfig,
    FreshnessPolicy,
    JsonFileSourceConfig,
    ServiceConfig,
    SourceGroupConfig,
)
from rpkiparrot.models import PayloadKind
from rpkiparrot.remote import RemoteClient
from rpkiparrot.service import create_app


async def main(path: Path, cli: bool = False) -> None:
    """The host manages the server task and passes actual listener settings."""
    source = JsonFileSourceConfig(
        id="local",
        path=path,
        format="routinator-json",
        freshness=FreshnessPolicy(max_age=60),
    )
    with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as listener:
        listener.bind(("127.0.0.1", 0))
        listener.listen()
        listener.setblocking(False)
        service = ServiceConfig(port=listener.getsockname()[1])
        app = create_app(
            service,
            client_config=ClientConfig(
                groups=(SourceGroupConfig(id="primary", priority=0, sources=(source,)),)
            ),
        )
        server = uvicorn.Server(
            uvicorn.Config(
                app,
                host=service.host,
                port=service.port,
                workers=service.workers,
                loop="asyncio",
                lifespan="on",
                proxy_headers=False,
                access_log=False,
                log_config=None,
            )
        )
        stopped = anyio.Event()

        async def serve() -> None:
            try:
                await server.serve(sockets=[listener])
            finally:
                stopped.set()

        async with anyio.create_task_group() as tasks:
            tasks.start_soon(serve)
            try:
                with anyio.fail_after(10):
                    while not server.started and not stopped.is_set():
                        await anyio.sleep(0.01)
                    if not server.started:
                        raise RuntimeError("service startup failed")
                    async with RemoteClient(f"http://{service.host}:{service.port}") as remote:
                        reference = await remote.wait_ready(required=(PayloadKind.VRP,), timeout=5)
                        result = await remote.validate_origin(
                            "192.0.2.0/24", 64496, snapshot=reference
                        )
                        print(result.status.value)
                        if cli:
                            command = [
                                sys.executable,
                                "-I",
                                "-m",
                                "rpkiparrot",
                                "--remote",
                                f"http://{service.host}:{service.port}",
                                "--format",
                                "json",
                            ]
                            for arguments in (
                                ["status", "--required", "vrp"],
                                ["validate", "origin", "192.0.2.0/24", "64496"],
                                ["query", "vrps", "--family", "4"],
                            ):
                                completed = await anyio.run_process([*command, *arguments])
                                document = json.loads(completed.stdout)
                                assert document["error"] is None and not completed.stderr
                                if arguments[0] == "validate":
                                    assert document["data"]["status"] == result.status.value
                                print(completed.stdout.decode(), end="")
                        async with remote.watch() as subscription:
                            print("watch observes", subscription.initial_status.lifecycle)
            finally:
                server.should_exit = True
                await stopped.wait()


if __name__ == "__main__":
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument("--cli", action="store_true", help="also exercise installed remote CLI")
    arguments = parser.parse_args()
    with TemporaryDirectory() as directory:
        path = Path(directory) / "source.json"
        path.write_text(
            json.dumps(
                {
                    "metadata": {"generated": int(datetime.now(UTC).timestamp())},
                    "roas": [{"prefix": "192.0.2.0/24", "maxLength": 24, "asn": "AS64496"}],
                    "aspas": [],
                }
            ),
            encoding="utf-8",
        )
        anyio.run(main, path, arguments.cli, backend="asyncio")
