"""``apiprobe run`` — execute a suite + print the report."""

from __future__ import annotations

import asyncio
import importlib
import json
import sys
from typing import TYPE_CHECKING

from mgf.apiprobe.cli._default_suite import build_default_suite
from mgf.apiprobe.config import ApiprobeSettings
from mgf.apiprobe.core.finding import Severity, severity_at_least
from mgf.apiprobe.core.suite import Suite
from mgf.apiprobe.exceptions import ApiprobeConfigError
from mgf.apiprobe.reporting.human import HumanReportWriter
from mgf.apiprobe.reporting.json_writer import JsonReportWriter
from mgf.apiprobe.transport.httpx_transport import HttpxTransport

if TYPE_CHECKING:
    from mgf.apiprobe.core.report import Report


def handle_run(
    *,
    suite_path: str | None,
    format: str,  # noqa: A002 — CLI uses 'format'
) -> int:
    """Execute the suite and print the report.

    Returns the exit code: 0 if no findings meet
    ``settings.fail_on_severity``, non-zero otherwise.
    """
    suite = _load_suite(suite_path)
    report = asyncio.run(_run_suite(suite))
    _print_report(report, format=format)
    return _exit_code(report)


def _load_suite(suite_path: str | None) -> Suite:
    """Resolve the suite from the CLI arg.

    None → bundled httpbin self-test. ``module:attr`` → load from
    Python module. ``module`` (no attr) → look for ``suite`` attribute.
    """
    if suite_path is None:
        return build_default_suite()

    if ":" in suite_path:
        module_name, attr_name = suite_path.rsplit(":", 1)
    else:
        module_name, attr_name = suite_path, "suite"

    try:
        module = importlib.import_module(module_name)
    except ImportError as exc:
        raise ApiprobeConfigError(
            f"--suite {suite_path!r}: cannot import module {module_name!r}: {exc}"
        ) from exc

    suite = getattr(module, attr_name, None)
    if suite is None:
        raise ApiprobeConfigError(
            f"--suite {suite_path!r}: module {module_name!r} has no attribute {attr_name!r}"
        )
    if not isinstance(suite, Suite):
        raise ApiprobeConfigError(
            f"--suite {suite_path!r}: {module_name}.{attr_name} is not a Suite "
            f"(got {type(suite).__name__})"
        )
    return suite


async def _run_suite(suite: Suite) -> Report:
    """Run the suite using a freshly-constructed httpx transport."""
    async with HttpxTransport(settings=suite.settings) as transport:
        return await suite.run(transport=transport)


def _print_report(report: Report, *, format: str) -> None:  # noqa: A002
    """Render the report and print to stdout."""
    if format == "json":
        writer = JsonReportWriter()
        print(json.dumps(writer.write(report), indent=2))
    else:
        text_writer = HumanReportWriter()
        sys.stdout.write(text_writer.write(report))


def _exit_code(report: Report) -> int:
    """Translate report → process exit code.

    Non-zero if any finding has severity ≥
    ``settings.fail_on_severity``. Default threshold is CRITICAL, so a
    suite with only WARN findings still exits 0 (visible but non-
    blocking) — the consumer's CI configuration tightens via the
    setting.
    """
    settings = ApiprobeSettings()
    threshold_str = settings.fail_on_severity
    try:
        threshold = Severity(threshold_str)
    except ValueError:
        # Misconfigured threshold — fail loud.
        return 2
    if any(severity_at_least(f.severity, threshold) for f in report.findings):
        return 1
    return 0


__all__ = ["handle_run"]
