"""Command-line interface for validating and running edge-AI pipelines."""

from __future__ import annotations

import argparse
import asyncio
import signal
import sys
from pathlib import Path

from pydantic import ValidationError

from cmvr_edge_ai.application import (
    EdgeAIApplication,
    create_default_model_registry,
    create_default_registry,
    validate_application,
)
from cmvr_edge_ai.compiler import PipelineCompileError
from cmvr_edge_ai.config import ConfigLoadError, load_config
from cmvr_edge_ai.observability import configure_logging


def build_parser() -> argparse.ArgumentParser:
    parser = argparse.ArgumentParser(prog="cmvr-edge-ai")
    subparsers = parser.add_subparsers(dest="command", required=True)

    validate = subparsers.add_parser("validate", help="validate config and DAG")
    _add_config_arguments(validate)

    run = subparsers.add_parser("run", help="run enabled pipelines")
    _add_config_arguments(run)
    run.add_argument("--log-level", default="INFO")
    run.add_argument("--log-format", choices=("text", "json"), default="text")

    subparsers.add_parser("plugins", help="list registered plugins")
    subparsers.add_parser("models", help="list registered detection models")
    return parser


def _add_config_arguments(parser: argparse.ArgumentParser) -> None:
    parser.add_argument("--config", "-c", type=Path, required=True)
    parser.add_argument(
        "--pipeline",
        action="append",
        dest="pipelines",
        help="select a pipeline; repeat for multiple pipelines",
    )


def main(argv: list[str] | None = None) -> int:
    args = build_parser().parse_args(argv)
    try:
        model_registry = create_default_model_registry()
        registry = create_default_registry(model_registry=model_registry)
        if args.command == "plugins":
            for plugin_spec in registry.specs():
                print(
                    f"{plugin_spec.plugin_id}\t{plugin_spec.kind.value}\t"
                    f"{plugin_spec.description}"
                )
            return 0
        if args.command == "models":
            for model_spec in model_registry.specs():
                labels = ",".join(model_spec.supported_labels)
                print(
                    f"{model_spec.model_id}\t{model_spec.name}\t"
                    f"{model_spec.backend}\t{labels}"
                )
            return 0

        config = load_config(args.config)
        if args.command == "validate":
            compiled = validate_application(config, registry, args.pipelines)
            names = ", ".join(item.pipeline_id for item in compiled)
            print(f"configuration is valid; pipelines: {names}")
            return 0

        configure_logging(args.log_level, args.log_format)
        return asyncio.run(_run_application(config, registry, args.pipelines))
    except (ConfigLoadError, ValidationError, PipelineCompileError, ValueError) as exc:
        print(f"configuration error: {exc}", file=sys.stderr)
        return 2
    except KeyboardInterrupt:
        return 130
    except Exception as exc:
        print(f"runtime error: {exc}", file=sys.stderr)
        return 1


async def _run_application(config, registry, pipeline_ids) -> int:  # type: ignore[no-untyped-def]
    application = EdgeAIApplication(config, registry)
    stop_event = asyncio.Event()
    loop = asyncio.get_running_loop()
    for signum in (signal.SIGINT, signal.SIGTERM):
        try:
            loop.add_signal_handler(signum, stop_event.set)
        except NotImplementedError:
            pass

    await application.start(pipeline_ids)
    wait_task = asyncio.create_task(application.wait(), name="application-wait")
    signal_task = asyncio.create_task(stop_event.wait(), name="application-signal")
    try:
        done, _ = await asyncio.wait(
            (wait_task, signal_task), return_when=asyncio.FIRST_COMPLETED
        )
        if wait_task in done:
            await wait_task
        else:
            await application.stop(graceful=True)
            wait_task.cancel()
            await asyncio.gather(wait_task, return_exceptions=True)
    finally:
        signal_task.cancel()
        await asyncio.gather(signal_task, return_exceptions=True)
        await application.stop(graceful=True)
    return 0


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