"""Redaction property EXTENDED to the value verbs (SPEC §6.2) — the AG-18 machine-check.

Plant a canary value via ``add`` (stdin) so it lives ENCRYPTED in the store, then drive
every non-surfacing path — ``show`` (metadata), ``list``, ``doctor``, ``history``,
``materialize --plan``, ``materialize --apply``, and a FAILING ``store_get`` (wrong key)
— asserting the canary appears in no stdout / stderr / log. The two paths that MAY
surface it (``show --value`` + ``exec``) are asserted positively in
``test_value_verbs_cli`` — here they are deliberately excluded.
"""

from __future__ import annotations

import contextlib
import io
import logging
import shutil

from pyrage import x25519

from mgf.secrets import cli
from mgf.secrets.context import Context
from tests._store import make_store_env

CANARY = "CANARY_VALUE_5e17_never_surface"


def _capture(env, argv, *, stdin_bytes=None, context=None, caplog=None):
    out, err = io.StringIO(), io.StringIO()
    import sys

    saved_stdin = sys.stdin
    if stdin_bytes is not None:
        sys.stdin = type("_S", (), {"buffer": io.BytesIO(stdin_bytes)})()  # type: ignore[assignment]
    try:
        ctx = contextlib.ExitStack()
        if caplog is not None:
            ctx.enter_context(caplog.at_level(logging.DEBUG))
        with ctx, contextlib.redirect_stdout(out), contextlib.redirect_stderr(err):
            code = cli.main([*env.common(), *argv], registry=env.registry, context=context)
    finally:
        sys.stdin = saved_stdin
    logs = "\n".join(r.getMessage() for r in caplog.records) if caplog is not None else ""
    return code, out.getvalue() + err.getvalue() + logs


def _git_commit(store_root):
    import subprocess

    e = {
        "GIT_AUTHOR_NAME": "t",
        "GIT_AUTHOR_EMAIL": "t@e.org",
        "GIT_COMMITTER_NAME": "t",
        "GIT_COMMITTER_EMAIL": "t@e.org",
        "GIT_CONFIG_GLOBAL": "/dev/null",
        "GIT_CONFIG_SYSTEM": "/dev/null",
        "HOME": str(store_root),
    }
    for args in (["init", "-q"], ["add", "-A"], ["commit", "-q", "-m", "store test_token"]):
        subprocess.run(
            ["git", "-C", str(store_root), *args], check=True, capture_output=True, env=e
        )


def test_no_canary_across_value_verbs(tmp_path, caplog) -> None:
    env = make_store_env(tmp_path)
    # Plant the canary — it now lives encrypted at rest in the store.
    code, _ = _capture(env, ["add", "test_token", "--value-file", "-"], stdin_bytes=CANARY.encode())
    assert code == 0
    # Commit only if git exists; on the git-less CI image `history` below runs the
    # git-absent path instead — either way it must surface no value (redaction holds).
    if shutil.which("git") is not None:
        _git_commit(env.store_root)

    # A wrong-key identity for the failing store_get path.
    stranger = x25519.Identity.generate()
    wrong = tmp_path / "wrong.key"
    wrong.write_text(str(stranger) + "\n", encoding="utf-8")

    # Local-only doctor context (no network) so C6 hygiene reads the materialized file.
    local_ctx = Context(fed_root=env.fed_root, woodpecker=None, forge=None, hosts=None)

    # Materialize first so doctor's C6 actually reads a plaintext file holding the canary.
    _capture(env, ["materialize", "--apply", "--identity", str(env.operator_key)])

    non_surfacing = [
        (["show", "test_token"], None),
        (["show", "test_token", "--json"], None),
        (["list"], None),
        (["list", "--json"], None),
        (["history", "test_token"], None),
        (["history", "test_token", "--json"], None),
        (["materialize"], None),
        (["materialize", "--json"], None),
        (["doctor", "--surface", "local"], local_ctx),
        (["doctor", "--surface", "local", "--json"], local_ctx),
        # The failing decrypt path (wrong key) must not echo anything value-derived.
        (["show", "test_token", "--value", "--identity", str(wrong)], None),
    ]
    for argv, ctx in non_surfacing:
        caplog.clear()
        _code, blob = _capture(env, argv, context=ctx, caplog=caplog)
        assert CANARY not in blob, f"canary leaked into {argv}"


def test_show_value_and_exec_are_the_only_surfacers(tmp_path) -> None:
    """Positive control: the value IS reachable — only via show --value + exec."""
    env = make_store_env(tmp_path)
    _capture(env, ["add", "test_token", "--value-file", "-"], stdin_bytes=CANARY.encode())
    out = io.StringIO()
    with contextlib.redirect_stdout(out):
        cli.main(
            [*env.common(), "show", "test_token", "--value", "--identity", str(env.operator_key)],
            registry=env.registry,
        )
    assert CANARY in out.getvalue()
