"""Live model smoke test — proves the registry models actually run.

For every model whose provider has a configured API key, makes one real API
round-trip ("Reply with the single word: OK") and reports a pass/fail/skip
matrix. Models whose provider has no key are skipped (not failed). Exit code is
non-zero if any *attempted* model fails, so this doubles as a CI/acceptance gate.

    pentestgpt-legacy --smoke-test
    pentestgpt-legacy --smoke-test --provider anthropic
    pentestgpt-legacy --smoke-test --model gpt-5.5
"""

from __future__ import annotations

import asyncio

from pentestgpt_legacy.llm.factory import (
    MissingCredentialsError,
    UnknownModelError,
    get_client,
)
from pentestgpt_legacy.llm.registry import ALL_SPECS, PROVIDERS, ModelSpec

_PROMPT = "Reply with the single word: OK"
_SYSTEM = "You are a helpful assistant. Answer in one word."
_TIMEOUT_S = 90.0

PASS, FAIL, SKIP = "PASS", "FAIL", "SKIP"


async def _probe(spec: ModelSpec) -> tuple[str, str]:
    """Return (status, detail) for one model."""
    try:
        client = get_client(spec.id)
    except MissingCredentialsError:
        return SKIP, "no API key configured"
    except UnknownModelError as e:  # pragma: no cover - registry is the source
        return FAIL, str(e)

    # Anthropic requires an explicit (small) output cap; others use their default.
    max_tokens = 32 if spec.provider == "anthropic" else None
    try:
        text = await asyncio.wait_for(
            client.provider.acomplete(
                [{"role": "user", "content": _PROMPT}],
                _SYSTEM,
                spec,
                max_output_tokens=max_tokens,
            ),
            timeout=_TIMEOUT_S,
        )
    except TimeoutError:
        return FAIL, f"timed out after {_TIMEOUT_S:.0f}s"
    except Exception as e:
        return FAIL, f"{type(e).__name__}: {e}"[:140]

    if text and text.strip():
        return PASS, text.strip().replace("\n", " ")[:48]
    return FAIL, "empty response"


def _select(provider: str | None, model: str | None) -> list[ModelSpec]:
    specs = list(ALL_SPECS)
    if model:
        specs = [s for s in specs if s.id == model]
    if provider:
        specs = [s for s in specs if s.provider == provider]
    return specs


async def _run(provider: str | None, model: str | None) -> int:
    specs = _select(provider, model)
    if not specs:
        print(f"No models match (provider={provider!r}, model={model!r}).")
        return 1

    results = await asyncio.gather(*(_probe(s) for s in specs))

    print("\n=== PentestGPT model smoke test ===\n")
    last_provider = None
    failures = 0
    attempted = 0
    icon = {PASS: "[ok]", FAIL: "[x]", SKIP: "[-]"}
    for spec, (status, detail) in zip(specs, results, strict=True):
        if spec.provider != last_provider:
            print(f"\n[{PROVIDERS[spec.provider].label}]")
            last_provider = spec.provider
        print(f"  {icon[status]} {status:<4} {spec.id:<32} {detail}")
        if status != SKIP:
            attempted += 1
        if status == FAIL:
            failures += 1

    passed = attempted - failures
    skipped = len(specs) - attempted
    print(
        f"\nSummary: {passed} passed, {failures} failed, {skipped} skipped ({len(specs)} total).\n"
    )
    return 1 if failures else 0


def run_smoke_test(provider: str | None = None, model: str | None = None) -> int:
    """Synchronous entry point. Returns a process exit code."""
    return asyncio.run(_run(provider, model))


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