"""Redaction over sync + retire (SPEC §6.3) — the AG-18 machine-check extended.

A canary value flows through the sync secret-CREATE path (``store_get`` → the API
body) and the retire store-removal path. Every stdout / stderr / log line of the
whole ``sync``/``retire`` CLI surface is asserted canary-free. The value only ever
lives in the store ciphertext and in-process bytes — never in output.
"""

from __future__ import annotations

import io
import logging
from contextlib import redirect_stderr, redirect_stdout
from datetime import UTC, datetime
from pathlib import Path

import pytest
from pyrage import x25519

from mgf.secrets import cli
from mgf.secrets import store as store_mod
from mgf.secrets.context import Context
from tests._fakes import FakeWoodpecker

CANARY = "CANARY_SYNC_VALUE_9c1f_do_not_leak"

REGISTRY = """\
[shared_pub]
family = "mgf"
kind = "api-token"
purpose = "a shared org secret with a stored value"
consumers = ["clone"]
recovery = "re-mint"
state = "active"
policy = { allowed_repos = "*", blast_radius = "org-wide" }
rotation = { class = "token", staged = true }
verify = { probe = "forge-auth" }
surfaces = [
  { type = "woodpecker-org-secret", events = ["push", "tag"] },
  { type = "store" },
]
"""


def _env(tmp_path: Path):
    fed = tmp_path / "fed"
    (fed / "mgf-standard").mkdir(parents=True)
    store = tmp_path / "store"
    (store / "secrets").mkdir(parents=True)
    founder, operator = x25519.Identity.generate(), x25519.Identity.generate()
    okey = tmp_path / "operator.key"
    okey.write_text(str(operator) + "\n", encoding="utf-8")
    okey.chmod(0o600)
    (store / "recipients.txt").write_text(
        f"founder  {founder.to_public()}\noperator {operator.to_public()}\n", encoding="utf-8"
    )
    (fed / "mgf-standard" / "SECRETS.toml").write_text(REGISTRY, encoding="utf-8")
    store_mod.store_put(
        "shared_pub",
        CANARY.encode(),
        root=store,
        encrypted_at=datetime.now(UTC).strftime("%Y-%m-%dT%H:%M:%SZ"),
        recipients=[str(founder.to_public()), str(operator.to_public())],
        family="mgf",
        kind="api-token",
    )
    return fed, store, okey


def _run(argv, caplog, *, context=None):
    out, err = io.StringIO(), io.StringIO()
    with caplog.at_level(logging.DEBUG), redirect_stdout(out), redirect_stderr(err):
        rc = cli.main(argv, context=context)
    logs = "\n".join(r.getMessage() for r in caplog.records)
    return rc, out.getvalue() + err.getvalue() + logs


def test_canary_absent_from_sync_create_and_retire(tmp_path, caplog) -> None:
    fed, store, okey = _env(tmp_path)
    common = ["--root", str(fed), "--store", str(store)]
    # secret is MISSING live → sync --apply hits the create path (store_get → API body).
    ctx = Context(fed_root=fed, woodpecker=FakeWoodpecker(org_secrets={}, repos=[]))

    blob = ""
    # 1) sync --plan (key-free) and 2) sync --apply --surface woodpecker (create path)
    for argv in (
        [*common, "sync", "--plan", "--surface", "woodpecker"],
        [*common, "sync", "--json", "--surface", "woodpecker"],
        [
            *common,
            "sync",
            "--apply",
            "--surface",
            "woodpecker",
            "--identity",
            str(okey),
            "--skip-canary",
        ],
    ):
        _rc, text = _run(argv, caplog, context=ctx)
        blob += text

    # 3) retire plan + 4) retire --apply (store ciphertext removed, never printed)
    for argv in (
        [*common, "retire", "shared_pub"],
        [*common, "retire", "shared_pub", "--json"],
        [*common, "retire", "shared_pub", "--apply", "--allow-retire"],
    ):
        _rc, text = _run(argv, caplog)
        blob += text

    assert CANARY not in blob, "secret value leaked through sync/retire output"
    # sanity: the create actually ran (metadata visible), value did not
    assert "shared_pub" in blob


def test_sync_apply_injects_value_but_never_prints_it(tmp_path) -> None:
    fed, store, okey = _env(tmp_path)
    wp = FakeWoodpecker(org_secrets={}, repos=[])
    ctx = Context(fed_root=fed, woodpecker=wp)
    out = io.StringIO()
    with redirect_stdout(out):
        rc = cli.main(
            [
                "--root",
                str(fed),
                "--store",
                str(store),
                "sync",
                "--apply",
                "--surface",
                "woodpecker",
                "--identity",
                str(okey),
                "--skip-canary",
                "--json",
            ],
            context=ctx,
        )
    assert rc == 0
    # the secret now exists live at the declared scope (value injected, never shown)
    assert wp.org_secret_states()["shared_pub"].events == frozenset({"push", "tag"})
    assert CANARY not in out.getvalue()


@pytest.mark.parametrize("verb_argv", [["sync", "--plan"], ["sync", "--json"]])
def test_sync_plan_is_value_free_without_identity(tmp_path, verb_argv) -> None:
    """The plan never decrypts — it needs no identity and surfaces no value."""
    fed, store, _okey = _env(tmp_path)
    ctx = Context(fed_root=fed, woodpecker=FakeWoodpecker(org_secrets={}, repos=[]))
    out = io.StringIO()
    with redirect_stdout(out):
        cli.main(
            ["--root", str(fed), "--store", str(store), *verb_argv, "--surface", "woodpecker"],
            context=ctx,
        )
    assert CANARY not in out.getvalue()
