from __future__ import annotations

import importlib.util
import io
import json
import sys
from pathlib import Path
from types import ModuleType

from health_client import AccountSnapshot, HealthStatus, NamedLimit


def _load_module() -> ModuleType:
    script_path = Path(__file__).resolve().parents[1] / "scripts" / "verify_claude_health.py"
    assert script_path.exists()
    spec = importlib.util.spec_from_file_location("verify_claude_health", script_path)
    assert spec is not None
    assert spec.loader is not None
    module = importlib.util.module_from_spec(spec)
    spec.loader.exec_module(module)
    return module


def test_module_import_does_not_require_unused_health_status_symbol(monkeypatch) -> None:
    script_path = Path(__file__).resolve().parents[1] / "scripts" / "verify_claude_health.py"
    assert script_path.exists()

    account_registry = ModuleType("account_registry")

    class Account:
        def __init__(self, *args) -> None:
            self.args = args

    class AccountRef:
        def __init__(self, *args) -> None:
            self.args = args

    account_registry.Account = Account
    account_registry.AccountRef = AccountRef

    claude_health_client = ModuleType("claude_health_client")

    class ClaudeHealthClient:
        def fetch(self, *args, **kwargs):
            raise AssertionError("fetch should not run during import")

    claude_health_client.ClaudeHealthClient = ClaudeHealthClient

    health_client = ModuleType("health_client")
    health_client.AccountSnapshot = AccountSnapshot
    health_client.NamedLimit = NamedLimit

    monkeypatch.setitem(sys.modules, "account_registry", account_registry)
    monkeypatch.setitem(sys.modules, "claude_health_client", claude_health_client)
    monkeypatch.setitem(sys.modules, "health_client", health_client)

    spec = importlib.util.spec_from_file_location("verify_claude_health_stubbed", script_path)
    assert spec is not None
    assert spec.loader is not None
    module = importlib.util.module_from_spec(spec)
    spec.loader.exec_module(module)

    assert module.AccountSnapshot is AccountSnapshot
    assert module.NamedLimit is NamedLimit


def test_main_requires_explicit_account_home(tmp_path: Path) -> None:
    module = _load_module()
    stdout = io.StringIO()
    stderr = io.StringIO()

    exit_code = module.main(["verify_claude_health"], stdout=stdout, stderr=stderr)

    assert exit_code == 2
    assert stdout.getvalue() == ""
    assert "--account-home" in stderr.getvalue()
    assert str(tmp_path) not in stderr.getvalue()
    assert "~/.claude" not in stderr.getvalue()


def test_main_emits_expected_schema_and_returns_zero_for_healthy_snapshot(
    monkeypatch,
    tmp_path: Path,
) -> None:
    module = _load_module()
    account_home = tmp_path / "CLAUDE_HOME"
    account_home.mkdir()
    seen: dict[str, object] = {}

    class FakeClient:
        def fetch(self, account, timeout_secs: float = 10.0) -> AccountSnapshot:
            seen["account_home"] = account.account_home
            seen["timeout_secs"] = timeout_secs
            return AccountSnapshot(
                status=HealthStatus.OK,
                primary_used_pct=27,
                secondary_used_pct=61,
                primary_reset_at=1_783_382_400.0,
                secondary_reset_at=1_783_728_000.0,
                named_limits=(
                    NamedLimit("five_hour", "primary", 27, 1_783_382_400.0, True),
                    NamedLimit("seven_day", "secondary", 61, 1_783_728_000.0, True),
                    NamedLimit("burst", "burst", 10, 1_783_400_000.0, True),
                ),
                detail="token=secret should never print",
            )

    monkeypatch.setattr(module, "ClaudeHealthClient", FakeClient)
    stdout = io.StringIO()
    stderr = io.StringIO()

    exit_code = module.main(
        ["verify_claude_health", "--account-home", str(account_home)],
        stdout=stdout,
        stderr=stderr,
    )

    assert exit_code == 0
    assert stderr.getvalue() == ""
    payload = json.loads(stdout.getvalue())
    assert list(payload) == [
        "status",
        "five_hour_available",
        "seven_day_available",
        "five_hour_reset_valid",
        "seven_day_reset_valid",
        "named_limit_count",
    ]
    assert payload == {
        "status": "ok",
        "five_hour_available": True,
        "seven_day_available": True,
        "five_hour_reset_valid": True,
        "seven_day_reset_valid": True,
        "named_limit_count": 3,
    }
    assert seen == {"account_home": account_home, "timeout_secs": 10.0}
    assert "secret" not in stdout.getvalue()


def test_main_returns_one_when_required_reset_is_missing_and_redacts_detail(
    monkeypatch,
    tmp_path: Path,
) -> None:
    module = _load_module()
    account_home = tmp_path / "CLAUDE_HOME"
    account_home.mkdir()

    class FakeClient:
        def fetch(self, account, timeout_secs: float = 10.0) -> AccountSnapshot:
            return AccountSnapshot(
                status=HealthStatus.OK,
                primary_used_pct=27,
                secondary_used_pct=61,
                named_limits=(
                    NamedLimit("five_hour", "primary", 27, None, True),
                    NamedLimit("seven_day", "secondary", 61, 1_783_728_000.0, True),
                ),
                detail=(
                    "email=probe@example.com path=/tmp/private "
                    "account_id=acct_123 authorization=Bearer abc payload={\"a\":1}"
                ),
            )

    monkeypatch.setattr(module, "ClaudeHealthClient", FakeClient)
    stdout = io.StringIO()
    stderr = io.StringIO()

    exit_code = module.main(
        ["verify_claude_health", "--account-home", str(account_home)],
        stdout=stdout,
        stderr=stderr,
    )

    assert exit_code == 1
    assert stderr.getvalue() == ""
    assert json.loads(stdout.getvalue()) == {
        "status": "ok",
        "five_hour_available": True,
        "seven_day_available": True,
        "five_hour_reset_valid": False,
        "seven_day_reset_valid": True,
        "named_limit_count": 2,
    }
    for forbidden in (
        "probe@example.com",
        "/tmp/private",
        "acct_123",
        "Bearer abc",
        "payload",
    ):
        assert forbidden not in stdout.getvalue()
        assert forbidden not in stderr.getvalue()


def test_main_rejects_non_directory_account_home() -> None:
    module = _load_module()
    stdout = io.StringIO()
    stderr = io.StringIO()

    exit_code = module.main(
        ["verify_claude_health", "--account-home", "missing-home"],
        stdout=stdout,
        stderr=stderr,
    )

    assert exit_code == 2
    assert stdout.getvalue() == ""
    assert "--account-home must point to a directory" in stderr.getvalue()
    assert "missing-home" not in stderr.getvalue()
