"""Whose Claude account gets used, and — the point of all of it — whose never does."""
import pytest

from scout import llm
from scout.llm import Access, NoLLMAccess, claude_env, credentials, require_llm_access

# What the operator's own machine has lying about in its environment. None of it may reach a
# model session started by anyone.
OPERATORS_SECRETS = {
    "ANTILIB_GMAIL_CLIENT_ID": "operator-mail-id",
    "ANTILIB_GMAIL_CLIENT_SECRET": "operator-mail-secret",
    "ANTILIB_GMAIL_REFRESH_TOKEN": "operator-mail-token",
    "LIBRARYMODE_TOKEN": "librarymode_operator_token",
    "FLY_API_TOKEN": "fly-org-token",
    "LITESTREAM_SECRET_ACCESS_KEY": "backup-key",
    "SCOUT_SENDER": "someone@example.com",
}


@pytest.fixture(autouse=True)
def forget_yesterdays_answer(tmp_path, monkeypatch):
    """Each test decides for itself; nothing is carried in from a remembered check."""
    monkeypatch.setattr(llm, "STATE_PATH", tmp_path / "state.json")


def test_none_of_the_operators_settings_reach_a_model_session():
    env = {"PATH": "/usr/bin", "HOME": "/home/someone", **OPERATORS_SECRETS,
           "ANTHROPIC_API_KEY": "sk-ant-theirs"}
    out = claude_env(env)
    for name, value in OPERATORS_SECRETS.items():
        assert name not in out, f"{name} was handed to the model"
        assert value not in out.values()


def test_the_environment_is_built_up_not_copied_down():
    """Anything invented later is excluded by default, not by remembering to exclude it."""
    out = claude_env({"PATH": "/usr/bin", "SOME_FUTURE_SECRET": "oops"})
    assert "SOME_FUTURE_SECRET" not in out


def test_a_persons_own_claude_settings_travel():
    out = claude_env({"PATH": "/usr/bin", "CLAUDE_CONFIG_DIR": "/home/someone/.claude"})
    assert out["CLAUDE_CONFIG_DIR"] == "/home/someone/.claude"


def test_a_nested_session_does_not_inherit_the_markers():
    out = claude_env({"PATH": "/usr/bin", "CLAUDECODE": "1", "CLAUDE_CODE_ENTRYPOINT": "cli"})
    assert "CLAUDECODE" not in out and "CLAUDE_CODE_ENTRYPOINT" not in out


def test_someone_whose_only_access_is_a_key_can_use_it():
    """The old rule threw the key away on any machine that wasn't the cloud, which would have
    left a new user with no way to run at all."""
    out = claude_env({"PATH": "/usr/bin", "ANTHROPIC_API_KEY": "sk-ant-theirs"})
    assert out["ANTHROPIC_API_KEY"] == "sk-ant-theirs"


def test_a_signed_in_subscription_is_preferred_over_a_key(monkeypatch):
    monkeypatch.setattr(llm, "_remembered_check", lambda: {"kind": "subscription", "detail": "d"})
    out = claude_env({"PATH": "/usr/bin", "ANTHROPIC_API_KEY": "sk-ant-theirs"})
    assert "ANTHROPIC_API_KEY" not in out


def test_the_cloud_machine_keeps_working_exactly_as_before(monkeypatch):
    """Its sign-in token travels; with no token it falls back to the key, as it does today."""
    monkeypatch.setattr(llm, "CLOUD", True)
    out = claude_env({"PATH": "/usr/bin", "CLAUDE_CODE_OAUTH_TOKEN": "oauth", **OPERATORS_SECRETS,
                      "ANTHROPIC_API_KEY": "sk-ant-operator"})
    assert out["CLAUDE_CODE_OAUTH_TOKEN"] == "oauth"
    assert "ANTHROPIC_API_KEY" not in out          # the subscription wins, as before
    assert "FLY_API_TOKEN" not in out              # but the deploy token no longer travels

    out = claude_env({"PATH": "/usr/bin", "ANTHROPIC_API_KEY": "sk-ant-operator"})
    assert out["ANTHROPIC_API_KEY"] == "sk-ant-operator"


def test_what_account_is_in_use_is_said_without_saying_the_secret():
    a = credentials({"ANTHROPIC_API_KEY": "sk-ant-abcd1234"})
    assert a.kind == "api_key" and "1234" in a.detail and "sk-ant-abcd1234" not in a.detail
    assert credentials({"CLAUDE_CODE_OAUTH_TOKEN": "x"}).kind == "subscription"
    assert credentials({}).kind == "unknown"       # only asking Claude can say


def test_nothing_runs_until_a_person_has_their_own_access():
    with pytest.raises(NoLLMAccess) as e:
        require_llm_access(prober=lambda: (False, "Claude is installed but not signed in."), env={})
    said = str(e.value)
    assert "runs on your own Claude account" in said
    assert "console.anthropic.com" in said and "claude" in said


def test_a_working_login_is_remembered_so_it_is_not_paid_for_twice():
    asked = []

    def prober():
        asked.append(1)
        return True, "Claude answered."

    assert llm.check_access(prober, env={}).usable
    assert llm.check_access(prober, env={}).usable
    assert len(asked) == 1


def test_being_out_of_credit_is_said_plainly():
    access = llm.check_access(lambda: (False, "Your Claude account answered, but it's out of credit."),
                              env={})
    assert not access.usable and "out of credit" in access.detail


def test_a_pasted_key_is_kept_on_this_computer_only(tmp_path, monkeypatch):
    monkeypatch.setattr(llm, "DATA_DIR", tmp_path)
    path = llm.save_api_key("sk-ant-mine")
    assert path.read_text().strip().endswith("sk-ant-mine")
    assert oct(path.stat().st_mode)[-3:] == "600"


def test_saving_a_key_twice_does_not_leave_two(tmp_path, monkeypatch):
    monkeypatch.setattr(llm, "DATA_DIR", tmp_path)
    llm.save_api_key("sk-ant-one")
    path = llm.save_api_key("sk-ant-two")
    keys = [ln for ln in path.read_text().splitlines() if ln.startswith("ANTHROPIC_API_KEY=")]
    assert keys == ["ANTHROPIC_API_KEY=sk-ant-two"]


def test_no_personal_address_is_saved_in_the_project():
    """The project is public on librarymo.de. The owner's addresses live in Fly secrets (see
    docs/OPERATIONS.md), never in the files that get saved."""
    import re
    from pathlib import Path
    root = Path(__file__).resolve().parents[1]
    saved = [p for pat in ("fly.toml", "cloud/*", "launchd/*", "scripts/*", "docs/*.md", "README.md", "scout/**/*.py")
             for p in root.glob(pat) if p.is_file()]
    assert saved
    address = re.compile(r"[A-Za-z0-9._%+-]+@(?!example\.com|localhost)[A-Za-z0-9.-]+\.[a-z]{2,}")
    hits = [f"{p.relative_to(root)}: {m.group(0)}" for p in saved
            for m in address.finditer(p.read_text(encoding="utf-8", errors="ignore"))
            if not m.group(0).endswith(("@librarymo.de", "@anthropic.com"))]
    assert hits == [], hits
