"""complyform doctor — environment health checks with diagnostic output.

Runs 16 checks against the local environment. Outputs status indicators
and a copyable diagnostic block for bug reports.
"""

from __future__ import annotations

import contextlib
import json
import os
import platform
import shutil
import subprocess
import sys
import time
from dataclasses import dataclass, field
from datetime import UTC, datetime
from pathlib import Path
from typing import TYPE_CHECKING, Annotated, Literal

import typer

from complyform.cli.exit_codes import EXIT_RUNTIME_ERROR, EXIT_SUCCESS
from complyform.cli.main import AppContext, app

if TYPE_CHECKING:
    from rich.console import Console

_MG = "Management"

_SUBPROCESS_TIMEOUT = 5


@dataclass
class DoctorCheckResult:
    """Result of a single doctor check."""

    number: int
    label: str
    status: Literal["pass", "warn", "error", "info"]
    message: str
    fix: str | None = None
    timing_ms: float | None = None


@dataclass
class DoctorResults:
    """Aggregated doctor results."""

    checks: list[DoctorCheckResult] = field(default_factory=list)

    @property
    def passed(self) -> int:
        return sum(1 for c in self.checks if c.status == "pass")

    @property
    def errors(self) -> int:
        return sum(1 for c in self.checks if c.status == "error")

    @property
    def warnings(self) -> int:
        return sum(1 for c in self.checks if c.status == "warn")

    @property
    def info(self) -> int:
        return sum(1 for c in self.checks if c.status == "info")

    @property
    def has_errors(self) -> bool:
        return self.errors > 0


def _run_subprocess(
    cmd: list[str], *, timeout: int = _SUBPROCESS_TIMEOUT
) -> tuple[str, str, int]:
    """Run subprocess with timeout. Returns (stdout, stderr, returncode)."""
    try:
        result = subprocess.run(
            cmd,
            capture_output=True,
            text=True,
            timeout=timeout,
        )
        return result.stdout, result.stderr, result.returncode
    except subprocess.TimeoutExpired:
        return "", "timeout", -1
    except FileNotFoundError:
        return "", "not found", -1
    except OSError as e:
        return "", str(e), -1


def _check_python_version(verbose: bool) -> DoctorCheckResult:
    """Check 1: Python version >= 3.14."""
    t0 = time.monotonic()
    ver = f"{sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro}"
    ok = sys.version_info >= (3, 14)
    elapsed = (time.monotonic() - t0) * 1000
    return DoctorCheckResult(
        number=1,
        label=f"Python {ver}",
        status="pass" if ok else "error",
        message=f"Python {ver}" if ok else f"Python {ver} — requires >= 3.14",
        fix=None if ok else "Install Python 3.14+: https://python.org/downloads/",
        timing_ms=elapsed if verbose else None,
    )


def _check_complyform_version(verbose: bool) -> DoctorCheckResult:
    """Check 2: ComplyForm version (always info)."""
    t0 = time.monotonic()
    from complyform import __version__

    elapsed = (time.monotonic() - t0) * 1000
    return DoctorCheckResult(
        number=2,
        label=f"complyform {__version__}",
        status="info",
        message=f"complyform {__version__}",
        timing_ms=elapsed if verbose else None,
    )


def _check_iac_binary(verbose: bool) -> DoctorCheckResult:
    """Check 2a: IaC binary (tofu/terraform)."""
    t0 = time.monotonic()
    try:
        from complyform.core.iac_binary import resolve_iac_binary

        binary = resolve_iac_binary()
        elapsed = (time.monotonic() - t0) * 1000
        return DoctorCheckResult(
            number=2,
            label=f"{binary.name} {binary.version}",
            status="pass",
            message=f"{binary.name} {binary.version}",
            timing_ms=elapsed if verbose else None,
        )
    except Exception:
        elapsed = (time.monotonic() - t0) * 1000
        return DoctorCheckResult(
            number=2,
            label="IaC binary not found",
            status="warn",
            message="tofu/terraform not found (needed for remediate/validate)",
            fix="Install OpenTofu: https://opentofu.org/docs/intro/install/",
            timing_ms=elapsed if verbose else None,
        )


def _check_checkov(verbose: bool) -> DoctorCheckResult:
    """Check 3: Checkov installed."""
    t0 = time.monotonic()
    path = shutil.which("checkov")
    if path is None:
        elapsed = (time.monotonic() - t0) * 1000
        return DoctorCheckResult(
            number=3,
            label="checkov not found",
            status="error",
            message="checkov not found on PATH",
            fix="pip install checkov",
            timing_ms=elapsed if verbose else None,
        )
    stdout, _, rc = _run_subprocess([path, "--version"])
    ver = stdout.strip() or "unknown"
    elapsed = (time.monotonic() - t0) * 1000
    return DoctorCheckResult(
        number=3,
        label=f"checkov {ver}",
        status="pass" if rc == 0 else "warn",
        message=f"checkov {ver}",
        timing_ms=elapsed if verbose else None,
    )


def _check_cloud_credentials(verbose: bool, cloud: str | None) -> DoctorCheckResult:
    """Check 4: Cloud credentials."""
    t0 = time.monotonic()
    if cloud is None:
        elapsed = (time.monotonic() - t0) * 1000
        return DoctorCheckResult(
            number=4,
            label="Cloud credentials",
            status="info",
            message="No default cloud configured",
            fix="complyform config set default_cloud gcp",
            timing_ms=elapsed if verbose else None,
        )

    if cloud == "gcp":
        stdout, stderr, rc = _run_subprocess(["gcloud", "auth", "print-access-token"])
        if rc == 0:
            # Try to get project
            proj_stdout, _, proj_rc = _run_subprocess(
                ["gcloud", "config", "get-value", "project"]
            )
            project = proj_stdout.strip() if proj_rc == 0 else "unknown"
            elapsed = (time.monotonic() - t0) * 1000
            return DoctorCheckResult(
                number=4,
                label=f"GCP credentials valid (project: {project})",
                status="pass",
                message=f"GCP credentials valid (project: {project})",
                timing_ms=elapsed if verbose else None,
            )
        elapsed = (time.monotonic() - t0) * 1000
        return DoctorCheckResult(
            number=4,
            label="GCP credentials",
            status="warn",
            message="GCP credentials not configured or expired",
            fix="gcloud auth application-default login",
            timing_ms=elapsed if verbose else None,
        )
    elif cloud == "aws":
        stdout, stderr, rc = _run_subprocess(["aws", "sts", "get-caller-identity"])
        elapsed = (time.monotonic() - t0) * 1000
        if rc == 0:
            return DoctorCheckResult(
                number=4,
                label="AWS credentials valid",
                status="pass",
                message="AWS credentials valid",
                timing_ms=elapsed if verbose else None,
            )
        return DoctorCheckResult(
            number=4,
            label="AWS credentials",
            status="warn",
            message="AWS credentials not configured or expired",
            fix="aws configure",
            timing_ms=elapsed if verbose else None,
        )
    elif cloud == "azure":
        stdout, stderr, rc = _run_subprocess(["az", "account", "show"])
        elapsed = (time.monotonic() - t0) * 1000
        if rc == 0:
            return DoctorCheckResult(
                number=4,
                label="Azure credentials valid",
                status="pass",
                message="Azure credentials valid",
                timing_ms=elapsed if verbose else None,
            )
        return DoctorCheckResult(
            number=4,
            label="Azure credentials",
            status="warn",
            message="Azure credentials not configured or expired",
            fix="az login",
            timing_ms=elapsed if verbose else None,
        )

    elapsed = (time.monotonic() - t0) * 1000
    return DoctorCheckResult(
        number=4,
        label=f"Cloud: {cloud}",
        status="info",
        message=f"Unknown cloud provider: {cloud}",
        timing_ms=elapsed if verbose else None,
    )


def _check_license_status(verbose: bool) -> DoctorCheckResult:
    """Check 5: License status."""
    t0 = time.monotonic()
    from complyform.core.auth.license import LicenseManager

    mgr = LicenseManager()
    cache = mgr.load_cache()
    if cache is None:
        elapsed = (time.monotonic() - t0) * 1000
        return DoctorCheckResult(
            number=5,
            label="Community tier",
            status="info",
            message="Community tier (no license)",
            timing_ms=elapsed if verbose else None,
        )

    tier = cache.tier
    exp = cache.expires_at or "never"
    elapsed = (time.monotonic() - t0) * 1000
    return DoctorCheckResult(
        number=5,
        label=f"{tier.title()} tier · expires {exp}",
        status="info",
        message=f"{tier.title()} tier · expires {exp}",
        timing_ms=elapsed if verbose else None,
    )


def _check_profile_bundle(verbose: bool) -> DoctorCheckResult:
    """Check 6: Profile bundle."""
    t0 = time.monotonic()
    version_file = Path.home() / ".complyform" / "profiles" / "VERSION"
    if not version_file.exists():
        elapsed = (time.monotonic() - t0) * 1000
        return DoctorCheckResult(
            number=6,
            label="No profiles installed",
            status="info",
            message=(
                "No profile bundle installed (Community tier uses built-in profiles)"
            ),
            timing_ms=elapsed if verbose else None,
        )

    version = version_file.read_text(encoding="utf-8").strip()
    # Count frameworks
    profiles_dir = Path.home() / ".complyform" / "profiles"
    fw_count = sum(1 for p in profiles_dir.iterdir() if p.is_dir())
    elapsed = (time.monotonic() - t0) * 1000
    return DoctorCheckResult(
        number=6,
        label=f"profiles v{version} ({fw_count} frameworks)",
        status="info",
        message=f"profiles v{version} ({fw_count} frameworks)",
        timing_ms=elapsed if verbose else None,
    )


def _check_cache_status(verbose: bool) -> DoctorCheckResult:
    """Check 7: Cache status."""
    t0 = time.monotonic()
    cache_dir = Path.home() / ".complyform" / "cache"
    if not cache_dir.exists():
        elapsed = (time.monotonic() - t0) * 1000
        return DoctorCheckResult(
            number=7,
            label="Cache: empty",
            status="info",
            message="Cache: empty",
            timing_ms=elapsed if verbose else None,
        )

    count = 0
    total_size = 0
    for entry in cache_dir.iterdir():
        if entry.is_dir():
            count += 1
            for f in entry.iterdir():
                if f.is_file():
                    total_size += f.stat().st_size
    if total_size < 1024 * 1024:
        size_str = f"{total_size // 1024}KB"
    else:
        size_str = f"{total_size // (1024 * 1024)}MB"
    elapsed = (time.monotonic() - t0) * 1000
    return DoctorCheckResult(
        number=7,
        label=f"Cache: {count} scans · {size_str}",
        status="info",
        message=f"Cache: {count} scans · {size_str}",
        timing_ms=elapsed if verbose else None,
    )


def _check_infracost(verbose: bool) -> DoctorCheckResult:
    """Check 8: Infracost (optional)."""
    t0 = time.monotonic()
    path = shutil.which("infracost")
    if path is None:
        elapsed = (time.monotonic() - t0) * 1000
        return DoctorCheckResult(
            number=8,
            label="infracost not found (optional — needed for --estimate-cost)",
            status="info",
            message="infracost not found (optional — needed for --estimate-cost)",
            timing_ms=elapsed if verbose else None,
        )
    stdout, _, _ = _run_subprocess([path, "--version"])
    ver = stdout.strip() or "unknown"
    elapsed = (time.monotonic() - t0) * 1000
    return DoctorCheckResult(
        number=8,
        label=f"infracost {ver}",
        status="pass",
        message=f"infracost {ver}",
        timing_ms=elapsed if verbose else None,
    )


def _check_cloud_intelligence_apis(
    verbose: bool, tier: str, cloud: str | None
) -> list[DoctorCheckResult]:
    """Check 9, 9b-9g: Cloud Intelligence APIs (Team+ only)."""
    results: list[DoctorCheckResult] = []

    if tier in ("community", "pro"):
        return results

    t0 = time.monotonic()
    if cloud == "gcp":
        # Check 9: GCP IAM Recommender API
        rec_cmd = [
            "gcloud",
            "services",
            "list",
            "--enabled",
            "--filter=name:recommender.googleapis.com",
            "--format=value(name)",
        ]
        stdout, stderr, rc = _run_subprocess(rec_cmd)
        elapsed = (time.monotonic() - t0) * 1000
        if rc == 0 and "recommender" in stdout:
            results.append(
                DoctorCheckResult(
                    number=9,
                    label="GCP IAM Recommender API enabled",
                    status="pass",
                    message="GCP IAM Recommender API enabled",
                    timing_ms=elapsed if verbose else None,
                )
            )
        else:
            results.append(
                DoctorCheckResult(
                    number=9,
                    label="GCP IAM Recommender API",
                    status="warn",
                    message="GCP IAM Recommender API not enabled",
                    fix="gcloud services enable recommender.googleapis.com",
                    timing_ms=elapsed if verbose else None,
                )
            )

        # Check 9b: GCP Security Command Center API
        t0 = time.monotonic()
        scc_cmd = [
            "gcloud",
            "services",
            "list",
            "--enabled",
            "--filter=name:securitycenter.googleapis.com",
            "--format=value(name)",
        ]
        stdout, stderr, rc = _run_subprocess(scc_cmd)
        elapsed = (time.monotonic() - t0) * 1000
        if rc == 0 and "securitycenter" in stdout:
            results.append(
                DoctorCheckResult(
                    number=9,
                    label="GCP Security Command Center API enabled",
                    status="pass",
                    message="GCP Security Command Center API enabled",
                    timing_ms=elapsed if verbose else None,
                )
            )
        else:
            results.append(
                DoctorCheckResult(
                    number=9,
                    label="GCP Security Command Center API",
                    status="warn",
                    message="GCP Security Command Center API not enabled",
                    fix="gcloud services enable securitycenter.googleapis.com",
                    timing_ms=elapsed if verbose else None,
                )
            )

    elif cloud == "aws":
        # Check 9c: AWS IAM Access Analyzer
        t0_aws = time.monotonic()
        stdout, stderr, rc = _run_subprocess(
            ["aws", "accessanalyzer", "list-analyzers", "--region", "us-east-1"]
        )
        elapsed = (time.monotonic() - t0_aws) * 1000
        if rc == 0:
            results.append(
                DoctorCheckResult(
                    number=9,
                    label="AWS IAM Access Analyzer available",
                    status="pass",
                    message="AWS IAM Access Analyzer available",
                    timing_ms=elapsed if verbose else None,
                )
            )
        else:
            results.append(
                DoctorCheckResult(
                    number=9,
                    label="AWS IAM Access Analyzer",
                    status="warn",
                    message="AWS IAM Access Analyzer not accessible",
                    fix="Enable IAM Access Analyzer in your AWS account",
                    timing_ms=elapsed if verbose else None,
                )
            )

    elif cloud == "azure":
        # Check 9d-9g: Azure checks
        t0_az = time.monotonic()
        # Check for azure-mgmt packages
        try:
            import importlib

            importlib.import_module("azure.mgmt.security")
            elapsed = (time.monotonic() - t0_az) * 1000
            results.append(
                DoctorCheckResult(
                    number=9,
                    label="Azure Security SDK installed",
                    status="pass",
                    message="Azure Security SDK installed",
                    timing_ms=elapsed if verbose else None,
                )
            )
        except ImportError:
            elapsed = (time.monotonic() - t0_az) * 1000
            results.append(
                DoctorCheckResult(
                    number=9,
                    label="Azure Security SDK",
                    status="warn",
                    message="Azure Security SDK not installed",
                    fix="pip install azure-mgmt-security",
                    timing_ms=elapsed if verbose else None,
                )
            )

    return results


def _check_disk_space(verbose: bool) -> DoctorCheckResult:
    """Check 10: Disk space."""
    t0 = time.monotonic()
    complyform_dir = Path.home() / ".complyform"
    parent = complyform_dir if complyform_dir.exists() else Path.home()
    usage = shutil.disk_usage(parent)
    free_mb = usage.free // (1024 * 1024)

    # Calculate used space in ~/.complyform
    used = 0
    if complyform_dir.exists():
        for p in complyform_dir.rglob("*"):
            if p.is_file():
                with contextlib.suppress(OSError):
                    used += p.stat().st_size
    used_mb = used // (1024 * 1024)

    elapsed = (time.monotonic() - t0) * 1000
    if free_mb < 100:
        return DoctorCheckResult(
            number=10,
            label=f"Disk: {used_mb}MB used in ~/.complyform ({free_mb}MB free)",
            status="warn",
            message=f"Low disk space: {free_mb}MB free",
            fix="Free disk space on the volume containing ~/.complyform",
            timing_ms=elapsed if verbose else None,
        )
    return DoctorCheckResult(
        number=10,
        label=f"Disk: {used_mb}MB used in ~/.complyform",
        status="pass",
        message=f"Disk: {used_mb}MB used in ~/.complyform",
        timing_ms=elapsed if verbose else None,
    )


def _check_latest_version(verbose: bool, tier: str) -> DoctorCheckResult:
    """Check 11: Latest version check (skip Community)."""
    t0 = time.monotonic()
    if tier == "community":
        elapsed = (time.monotonic() - t0) * 1000
        return DoctorCheckResult(
            number=11,
            label="Version check skipped (Community tier)",
            status="info",
            message="Version check skipped (zero-network guarantee)",
            timing_ms=elapsed if verbose else None,
        )

    try:
        import httpx

        resp = httpx.get("https://pypi.org/pypi/complyform/json", timeout=3.0)
        if resp.status_code == 200:
            from packaging.version import Version

            from complyform import __version__

            remote = resp.json()["info"]["version"]
            if Version(remote) > Version(__version__):
                elapsed = (time.monotonic() - t0) * 1000
                return DoctorCheckResult(
                    number=11,
                    label=f"Update available: {remote}",
                    status="info",
                    message=f"Update available: {remote} (you have {__version__})",
                    fix="uv tool install --upgrade complyform",
                    timing_ms=elapsed if verbose else None,
                )
            elapsed = (time.monotonic() - t0) * 1000
            return DoctorCheckResult(
                number=11,
                label="Up to date",
                status="pass",
                message="Up to date",
                timing_ms=elapsed if verbose else None,
            )
    except Exception:
        pass

    elapsed = (time.monotonic() - t0) * 1000
    return DoctorCheckResult(
        number=11,
        label="Version check failed",
        status="info",
        message="Could not check for updates (network unavailable)",
        timing_ms=elapsed if verbose else None,
    )


def _check_network(verbose: bool, tier: str) -> DoctorCheckResult:
    """Check 12: Network connectivity (skip Community)."""
    t0 = time.monotonic()
    if tier == "community":
        elapsed = (time.monotonic() - t0) * 1000
        return DoctorCheckResult(
            number=12,
            label="Network check skipped (Community tier)",
            status="info",
            message="Network check skipped (zero-network guarantee)",
            timing_ms=elapsed if verbose else None,
        )

    try:
        import httpx

        resp = httpx.head("https://api.complyform.dev/health", timeout=3.0)
        elapsed = (time.monotonic() - t0) * 1000
        if resp.status_code < 500:
            return DoctorCheckResult(
                number=12,
                label="api.complyform.dev reachable",
                status="pass",
                message="api.complyform.dev reachable",
                timing_ms=elapsed if verbose else None,
            )
    except Exception:
        pass

    elapsed = (time.monotonic() - t0) * 1000
    return DoctorCheckResult(
        number=12,
        label="api.complyform.dev unreachable",
        status="warn",
        message="api.complyform.dev unreachable",
        fix="Check network connectivity and firewall rules",
        timing_ms=elapsed if verbose else None,
    )


def _check_config_permissions(verbose: bool) -> DoctorCheckResult:
    """Check 13: Config file permissions."""
    t0 = time.monotonic()
    config_path = Path.home() / ".complyform" / "config.yaml"
    if not config_path.exists():
        elapsed = (time.monotonic() - t0) * 1000
        return DoctorCheckResult(
            number=13,
            label="Config file not found",
            status="info",
            message="No config file at ~/.complyform/config.yaml",
            timing_ms=elapsed if verbose else None,
        )

    stat = config_path.stat()
    mode = stat.st_mode & 0o777
    elapsed = (time.monotonic() - t0) * 1000
    if mode == 0o600:
        return DoctorCheckResult(
            number=13,
            label=f"Config permissions: {oct(mode)}",
            status="pass",
            message=f"Config permissions: {oct(mode)}",
            timing_ms=elapsed if verbose else None,
        )
    return DoctorCheckResult(
        number=13,
        label=f"Config permissions: {oct(mode)}",
        status="warn",
        message=f"Config permissions {oct(mode)} — should be 0o600",
        fix=f"chmod 600 {config_path}",
        timing_ms=elapsed if verbose else None,
    )


def _check_shell_completion(verbose: bool) -> DoctorCheckResult:
    """Check 14: Shell completion."""
    t0 = time.monotonic()
    shell = os.environ.get("SHELL", "")
    shell_name = Path(shell).name if shell else "unknown"

    # Check common completion file locations
    completions_installed = False
    if "zsh" in shell_name:
        for p in [
            Path.home() / ".zfunc" / "_complyform",
            Path("/usr/local/share/zsh/site-functions/_complyform"),
        ]:
            if p.exists():
                completions_installed = True
                break
    elif "bash" in shell_name:
        for p in [
            Path("/etc/bash_completion.d/complyform"),
            Path.home() / ".bash_completion.d" / "complyform",
        ]:
            if p.exists():
                completions_installed = True
                break

    elapsed = (time.monotonic() - t0) * 1000
    if completions_installed:
        return DoctorCheckResult(
            number=14,
            label=f"Shell completion installed ({shell_name})",
            status="pass",
            message=f"Shell completion installed ({shell_name})",
            timing_ms=elapsed if verbose else None,
        )
    return DoctorCheckResult(
        number=14,
        label="Shell completion not installed",
        status="info",
        message="Shell completion not installed",
        fix="complyform --install-completion",
        timing_ms=elapsed if verbose else None,
    )


def _check_github_token(verbose: bool, config_token: str | None) -> DoctorCheckResult:
    """Check 15: GitHub token (SPEC-002)."""
    t0 = time.monotonic()
    if config_token is None:
        elapsed = (time.monotonic() - t0) * 1000
        return DoctorCheckResult(
            number=15,
            label="GitHub token not configured",
            status="info",
            message=(
                "GitHub token not configured"
                " (optional \u2014 needed for PR remediation)"
            ),
            timing_ms=elapsed if verbose else None,
        )

    valid_prefixes = ("ghp_", "github_pat_")
    ok = any(config_token.startswith(p) for p in valid_prefixes)
    elapsed = (time.monotonic() - t0) * 1000
    if ok:
        return DoctorCheckResult(
            number=15,
            label="GitHub token configured",
            status="pass",
            message=f"GitHub token configured ({config_token[:7]}...)",
            timing_ms=elapsed if verbose else None,
        )
    return DoctorCheckResult(
        number=15,
        label="GitHub token format invalid",
        status="warn",
        message="GitHub token does not start with ghp_ or github_pat_",
        fix="Check your GitHub token format",
        timing_ms=elapsed if verbose else None,
    )


def _check_anthropic_key(verbose: bool, config_key: str | None) -> DoctorCheckResult:
    """Check 16: Anthropic API key (SPEC-003)."""
    t0 = time.monotonic()
    if config_key is None:
        elapsed = (time.monotonic() - t0) * 1000
        return DoctorCheckResult(
            number=16,
            label="Anthropic API key not configured",
            status="info",
            message=(
                "Anthropic API key not configured"
                " (optional \u2014 needed for AI explanations)"
            ),
            timing_ms=elapsed if verbose else None,
        )

    ok = config_key.startswith("sk-ant-")
    elapsed = (time.monotonic() - t0) * 1000
    if ok:
        return DoctorCheckResult(
            number=16,
            label="Anthropic API key configured",
            status="pass",
            message=f"Anthropic API key configured ({config_key[:10]}...)",
            timing_ms=elapsed if verbose else None,
        )
    return DoctorCheckResult(
        number=16,
        label="Anthropic API key format invalid",
        status="warn",
        message="Anthropic API key does not start with sk-ant-",
        fix="Check your Anthropic API key format",
        timing_ms=elapsed if verbose else None,
    )


def run_doctor_checks(
    *,
    tier: str = "community",
    cloud: str | None = None,
    verbose: bool = False,
    github_token: str | None = None,
    anthropic_api_key: str | None = None,
) -> DoctorResults:
    """Run all doctor checks and return results."""
    results = DoctorResults()

    results.checks.append(_check_python_version(verbose))
    results.checks.append(_check_complyform_version(verbose))
    results.checks.append(_check_iac_binary(verbose))
    results.checks.append(_check_checkov(verbose))
    results.checks.append(_check_cloud_credentials(verbose, cloud))
    results.checks.append(_check_license_status(verbose))
    results.checks.append(_check_profile_bundle(verbose))
    results.checks.append(_check_cache_status(verbose))
    results.checks.append(_check_infracost(verbose))
    results.checks.extend(_check_cloud_intelligence_apis(verbose, tier, cloud))
    results.checks.append(_check_disk_space(verbose))
    results.checks.append(_check_latest_version(verbose, tier))
    results.checks.append(_check_network(verbose, tier))
    results.checks.append(_check_config_permissions(verbose))
    results.checks.append(_check_shell_completion(verbose))
    results.checks.append(_check_github_token(verbose, github_token))
    results.checks.append(_check_anthropic_key(verbose, anthropic_api_key))

    return results


def build_diagnostic_string(
    *,
    tier: str = "community",
    cloud: str | None = None,
) -> str:
    """Build one-line diagnostic string for bug reports.

    Format: complyform={ver} python={ver} checkov={ver} iac={binary}/{ver}
            os={platform}/{arch} tier={tier} cloud={cloud}
            profiles={ver} cache={count}/{size}
    """
    from complyform import __version__

    parts = [f"complyform={__version__}"]
    parts.append(f"python={platform.python_version()}")

    # Checkov version
    checkov_path = shutil.which("checkov")
    if checkov_path:
        stdout, _, rc = _run_subprocess([checkov_path, "--version"])
        ver = stdout.strip() if rc == 0 else "installed"
        parts.append(f"checkov={ver}")
    else:
        parts.append("checkov=not-found")

    # IaC binary
    try:
        from complyform.core.iac_binary import resolve_iac_binary

        binary = resolve_iac_binary()
        parts.append(f"iac={binary.name}/{binary.version}")
    except Exception:
        parts.append("iac=none")

    parts.append(f"os={platform.system().lower()}/{platform.machine()}")
    parts.append(f"tier={tier}")
    if cloud:
        parts.append(f"cloud={cloud}")

    # Profile version
    version_file = Path.home() / ".complyform" / "profiles" / "VERSION"
    if version_file.exists():
        parts.append(f"profiles=v{version_file.read_text(encoding='utf-8').strip()}")

    # Cache info
    from complyform.core.diagnostics import _cache_info

    parts.append(f"cache={_cache_info()}")

    return " ".join(parts)


_STATUS_ICONS: dict[str, tuple[str, str]] = {
    "pass": ("\u2713", "green"),
    "warn": ("\u26a0", "yellow"),
    "error": ("\u2717", "red"),
    "info": ("\u2139", "blue"),
}


def _render_terminal(
    results: DoctorResults,
    diagnostic_string: str,
    console: Console,
    *,
    verbose: bool = False,
) -> None:
    """Render doctor results to terminal (stderr)."""
    console.print("\n[bold]ComplyForm Doctor[/bold]")
    console.print("\u2500" * 40)

    for check in results.checks:
        icon, color = _STATUS_ICONS[check.status]
        timing = ""
        if verbose and check.timing_ms is not None:
            timing = f" [dim]({check.timing_ms:.0f}ms)[/dim]"
        if check.status == "pass":
            console.print(f"  [{color}]{icon}[/{color}]  {check.message}{timing}")
        elif check.status == "info":
            console.print(f"  [{color}]{icon}[/{color}]  {check.message}{timing}")
            if check.fix:
                console.print(f"     [dim]Run: {check.fix}[/dim]")
        elif check.status == "warn":
            console.print(f"  [{color}]{icon}[/{color}]  {check.message}{timing}")
            if check.fix:
                console.print(f"     [dim]Fix: {check.fix}[/dim]")
        else:  # error
            console.print(f"  [{color}]{icon}[/{color}]  {check.message}{timing}")
            if check.fix:
                console.print(f"     [dim]Fix: {check.fix}[/dim]")

    console.print()
    console.print(
        f"  {results.passed} passed \u00b7 {results.errors} errors"
        f" \u00b7 {results.warnings} warnings \u00b7 {results.info} info"
    )
    console.print()
    sep = "\u2500" * 30
    console.print(f"  [dim]\u2500\u2500 Copy for bug reports \u2500{sep}[/dim]")
    console.print(f"  [dim]{diagnostic_string}[/dim]")
    console.print()


def _render_json(
    results: DoctorResults,
    diagnostic_string: str,
    *,
    iac_binary_info: dict[str, str | None] | None = None,
) -> str:
    """Render doctor results as JSON envelope."""
    from complyform import __version__

    config_path = str(Path.home() / ".complyform" / "config.yaml")

    envelope = {
        "schema_version": 1,
        "tool": "complyform",
        "tool_version": __version__,
        "command": "doctor",
        "timestamp": datetime.now(tz=UTC).isoformat(),
        "result": {
            "checks": [
                {
                    "number": c.number,
                    "label": c.label,
                    "status": c.status,
                    "message": c.message,
                    "fix": c.fix,
                }
                for c in results.checks
            ],
            "summary": {
                "passed": results.passed,
                "errors": results.errors,
                "warnings": results.warnings,
                "info": results.info,
            },
            "iac_binary": iac_binary_info,
            "config_path": config_path,
            "diagnostic_string": diagnostic_string,
        },
    }
    return json.dumps(envelope, indent=2)


@app.command(rich_help_panel=_MG)
def doctor(
    ctx: typer.Context,
    format: Annotated[
        str,
        typer.Option("--format", help="Output format: terminal|json"),
    ] = "terminal",
    verbose: Annotated[
        bool,
        typer.Option("--verbose", "-v", help="Show timing and raw output"),
    ] = False,
) -> None:
    """Check environment health and output diagnostic info."""
    app_ctx: AppContext = ctx.obj
    config = app_ctx.config
    console = app_ctx.console

    results = run_doctor_checks(
        tier=config.tier,
        cloud=config.default_cloud,
        verbose=verbose,
        github_token=config.github_token,
        anthropic_api_key=config.anthropic_api_key,
    )

    diag = build_diagnostic_string(
        tier=config.tier,
        cloud=config.default_cloud,
    )

    # Get IaC binary info for JSON output
    iac_info: dict[str, str | None] | None = None
    try:
        from complyform.core.iac_binary import resolve_iac_binary

        binary = resolve_iac_binary()
        iac_info = {
            "name": binary.name,
            "version": binary.version,
            "path": binary.path,
        }
    except Exception:
        iac_info = None

    if format == "json":
        sys.stdout.write(_render_json(results, diag, iac_binary_info=iac_info) + "\n")
    else:
        _render_terminal(results, diag, console, verbose=verbose)

    exit_code = EXIT_RUNTIME_ERROR if results.has_errors else EXIT_SUCCESS
    raise typer.Exit(code=exit_code)
