"""``mgf-cloud doctor`` — app-own-state health checks (OP-02).

Probes the things mgf-cloud needs to function *without making a live cloud
call*: the provider registry resolves, credentials resolve + parse +
``require_complete`` (a clouds.yaml / ``OS_*`` / search-path probe, not an auth
round-trip), the rates override file parses, and the selected provider's SDK
extra is installed. This is deliberately distinct from the live
``mgf-cloud provider check`` (which *does* hit Keystone) — ``doctor`` answers
"is my install + config sane?" offline.

Output is a composed :class:`mgf.common.doctor.DoctorBundle` (text or ``--json``);
the process exit code is the number of FAILED sections (``0`` = all clear), so
the command is scriptable in CI/cron.
"""

from __future__ import annotations

import importlib.util
import json
from typing import TYPE_CHECKING

from mgf.common.doctor import DoctorBundle, ReportSection

from mgf.cloud import __version__
from mgf.cloud.exceptions import ConfigError, CredentialsError
from mgf.cloud.providers import available_providers

if TYPE_CHECKING:
    import argparse

# Each provider's gating extra → the module that proves it is installed.
_PROVIDER_SDK = {
    "infomaniak": ("infomaniak", "openstack"),
    "aws": ("aws", "boto3"),
}


def _check_providers(args: argparse.Namespace) -> ReportSection:
    names = tuple(available_providers())
    if args.provider in names:
        return ReportSection(
            name="providers",
            status="ok",
            details={"available": ", ".join(names), "selected": args.provider},
        )
    return ReportSection(
        name="providers",
        status="fail",
        details={
            "available": ", ".join(names),
            "selected": args.provider,
            "advice": f"unknown provider {args.provider!r}; pick one of the above",
        },
    )


def _check_provider_sdk(args: argparse.Namespace) -> ReportSection:
    extra_module = _PROVIDER_SDK.get(args.provider)
    if extra_module is None or args.provider == "fake":
        return ReportSection(
            name="provider-sdk",
            status="ok",
            details={"provider": args.provider, "note": "no SDK extra required"},
        )
    extra, module = extra_module
    if importlib.util.find_spec(module) is not None:
        return ReportSection(
            name="provider-sdk",
            status="ok",
            details={"provider": args.provider, "module": module},
        )
    return ReportSection(
        name="provider-sdk",
        status="warn",
        details={
            "provider": args.provider,
            "missing": module,
            "advice": f"install with `pip install mgf-cloud[{extra}]`",
        },
    )


def _check_credentials(args: argparse.Namespace) -> ReportSection:
    """Resolve + parse credentials offline (no auth round-trip)."""
    from mgf.cloud import config

    if args.provider == "fake":
        return ReportSection(
            name="credentials",
            status="ok",
            details={"provider": "fake", "note": "fake provider needs no credentials"},
        )
    try:
        creds = config.load(clouds_yaml=args.clouds_yaml, cloud=args.cloud)
    except CredentialsError as exc:
        return ReportSection(
            name="credentials",
            status="fail",
            details={
                "source": exc.source or "(unresolved)",
                "missing": ", ".join(exc.missing) or "(see message)",
                "error": str(exc),
            },
        )
    return ReportSection(
        name="credentials",
        status="ok",
        details={
            "auth_url": creds.auth_url,
            "auth_type": creds.auth_type,
            "region": creds.region_name or "(default)",
        },
    )


def _check_rates(args: argparse.Namespace) -> ReportSection:
    from mgf.cloud import cost

    try:
        rates = cost.rates_from_config(args.rates)
    except ConfigError as exc:
        return ReportSection(
            name="rates",
            status="fail",
            details={"path": str(args.rates), "error": str(exc)},
        )
    return ReportSection(
        name="rates",
        status="ok",
        details={
            "currency": rates.currency,
            "priced_flavors": len(rates.flavor_hourly),
            "source": str(args.rates) if args.rates else "built-in defaults",
        },
    )


def build_bundle(args: argparse.Namespace) -> DoctorBundle:
    """Assemble the full doctor bundle (shared with ``bundle``)."""
    bundle = DoctorBundle(app_name="mgf-cloud", app_version=__version__)
    bundle.add_section(_check_providers(args))
    bundle.add_section(_check_provider_sdk(args))
    bundle.add_section(_check_credentials(args))
    bundle.add_section(_check_rates(args))
    return bundle


def run_doctor(args: argparse.Namespace) -> int:
    """Run the checklist; return the FAILED-section count as the exit code."""
    bundle = build_bundle(args)
    if getattr(args, "as_json", False):
        print(json.dumps(bundle.render_json(), indent=2, sort_keys=True))
    else:
        print(bundle.render_text())
    return sum(1 for s in bundle.sections() if s.status == "fail")


__all__ = ["build_bundle", "run_doctor"]
