"""``retire`` — completion-gated state flip + store removal (SPEC §3, §6.1).

Refusals: referenced by a live pipeline (the mgf-hrb lesson) or founder-held.
Success: ``active → retiring`` surgical TOML edit + store entry/manifest removal,
with the ``my_stuff/`` file left untouched (a separate deliberate prune).
"""

from __future__ import annotations

from datetime import UTC, datetime

import pytest

from mgf.secrets import store as store_mod
from mgf.secrets.errors import UsageError
from mgf.secrets.registry import load
from mgf.secrets.retire import apply_retire, flip_state_in_file, plan_retire
from tests._fakes import write_pipeline
from tests._store import make_store_env


def _store_value(env, entry_id, value=b"secret-fixture-value"):
    store_mod.store_put(
        entry_id,
        value,
        root=env.store_root,
        encrypted_at=datetime.now(UTC).strftime("%Y-%m-%dT%H:%M:%SZ"),
        recipients=[env.founder_pub, env.operator_pub],
        family="mgf",
        kind="api-token",
    )


def _toml(env):
    return env.fed_root / "mgf-standard" / "SECRETS.toml"


# --------------------------------------------------------------------------- #
# Gates (refusals).
# --------------------------------------------------------------------------- #


def test_retire_plan_ready_when_unreferenced(tmp_path) -> None:
    env = make_store_env(tmp_path)
    _store_value(env, "test_token")
    plan = plan_retire(env.registry, "test_token", fed_root=env.fed_root, store_root=env.store_root)
    assert not plan.refused
    assert plan.from_state == "active" and plan.to_state == "retiring"
    assert plan.store_present is True


def test_retire_refused_when_referenced_by_live_pipeline(tmp_path) -> None:
    env = make_store_env(tmp_path)
    write_pipeline(
        env.fed_root,
        "some-app",
        """\
when:
  - event: push
steps:
  - name: build
    environment:
      T:
        from_secret: test_token
    commands: [echo x]
""",
    )
    plan = plan_retire(env.registry, "test_token", fed_root=env.fed_root, store_root=env.store_root)
    assert plan.refused
    assert "referenced" in plan.refusal and "some-app" in plan.refusal
    # and --apply refuses too (never a solo destructive act)
    with pytest.raises(UsageError, match="referenced"):
        apply_retire(
            env.registry,
            "test_token",
            fed_root=env.fed_root,
            toml_paths=[_toml(env)],
            store_root=env.store_root,
            allow_retire=True,
        )


def test_retire_refused_when_founder_held(tmp_path) -> None:
    env = make_store_env(tmp_path)
    plan = plan_retire(
        env.registry, "held_secret", fed_root=env.fed_root, store_root=env.store_root
    )
    assert plan.refused and "held" in plan.refusal and "⚖" in plan.refusal
    with pytest.raises(UsageError, match="held"):
        apply_retire(
            env.registry,
            "held_secret",
            fed_root=env.fed_root,
            toml_paths=[_toml(env)],
            store_root=env.store_root,
            allow_retire=True,
        )


def test_apply_requires_allow_retire(tmp_path) -> None:
    env = make_store_env(tmp_path)
    with pytest.raises(UsageError, match="allow-retire"):
        apply_retire(
            env.registry,
            "test_token",
            fed_root=env.fed_root,
            toml_paths=[_toml(env)],
            store_root=env.store_root,
            allow_retire=False,
        )


# --------------------------------------------------------------------------- #
# Success — surgical flip + store removal, my_stuff untouched.
# --------------------------------------------------------------------------- #


def test_apply_flips_state_and_removes_store(tmp_path) -> None:
    env = make_store_env(tmp_path)
    _store_value(env, "test_token")
    assert store_mod.cipher_path(env.store_root, "test_token").is_file()
    result = apply_retire(
        env.registry,
        "test_token",
        fed_root=env.fed_root,
        toml_paths=[_toml(env)],
        store_root=env.store_root,
        allow_retire=True,
    )
    assert result.state_flipped and result.store_removed
    # the registry now reads retiring (surgical edit persisted)
    reloaded = load([_toml(env)])
    assert reloaded.get("test_token").state == "retiring"
    # the other entries are untouched
    assert reloaded.get("test_docform").state == "active"
    # store ciphertext + manifest row are gone
    assert not store_mod.cipher_path(env.store_root, "test_token").is_file()
    assert "test_token" not in store_mod.manifest_load(env.store_root)["entries"]


def test_flip_state_only_edits_the_state_line(tmp_path) -> None:
    p = tmp_path / "SECRETS.toml"
    p.write_text(
        '[a]\nkind = "api-token"\nstate = "active"\nrecovery = "x"\n\n[b]\nstate = "active"\n',
        encoding="utf-8",
    )
    assert flip_state_in_file(p, "a", "retiring") is True
    text = p.read_text(encoding="utf-8")
    assert 'state = "retiring"' in text  # a flipped
    assert text.count('state = "active"') == 1  # b untouched
    assert 'kind = "api-token"' in text and 'recovery = "x"' in text  # neighbours intact


def test_retire_unknown_id_raises(tmp_path) -> None:
    env = make_store_env(tmp_path)
    with pytest.raises(UsageError, match="unknown id"):
        plan_retire(env.registry, "nope", fed_root=env.fed_root, store_root=env.store_root)
