"""Store durability + doc-form guards — the 2026-07-24 feedback wave.

Covers FEEDBACK PAPER-02 (a doc-form value must not enter the store) and PAPER-03
(a written value must land in git, and the doctor must notice when it hasn't).
"""

from __future__ import annotations

import shutil
import subprocess

import pytest

from mgf.secrets import store as S
from mgf.secrets.cli import _guard_single_line
from mgf.secrets.errors import UsageError
from tests._store import make_store_env

#: The fleet CI image ships no `git` binary (the same constraint `_drift.ungoverned_repos`
#: works around with a path test). The code under test degrades to "no findings" there;
#: these tests need a real repo, so they run where git exists — locally and on the host.
needs_git = pytest.mark.skipif(shutil.which("git") is None, reason="CI image has no git binary")


def _git(root, *args: str) -> None:
    subprocess.run(["git", "-C", str(root), *args], check=True, capture_output=True)


def _init_repo(root) -> None:
    _git(root, "init", "-q")
    _git(root, "config", "user.email", "test@example.invalid")
    _git(root, "config", "user.name", "Test")


def _put(env, entry_id: str, value: bytes) -> None:
    S.store_put(
        entry_id,
        value,
        root=env.store_root,
        encrypted_at="2026-07-24T00:00:00Z",
        recipients=[env.founder_pub, env.operator_pub],
    )


# --------------------------------------------------------------------------- #
# PAPER-02 — single-line kinds must refuse a doc-form (multi-line) value.
# --------------------------------------------------------------------------- #


def test_guard_refuses_multiline_api_token() -> None:
    with pytest.raises(UsageError) as ei:
        _guard_single_line(
            "api-token", "logship_ingest_token", b"TOKEN\nnote about it\n", allow_multiline=False
        )
    msg = str(ei.value)
    assert "2 non-blank lines" in msg
    # AG-18: the shape is reported, never the value.
    assert "TOKEN" not in msg
    assert "note about it" not in msg


def test_guard_allows_multiline_when_explicitly_permitted() -> None:
    _guard_single_line("api-token", "x", b"A\nB\n", allow_multiline=True)


def test_guard_ignores_kinds_that_are_legitimately_multiline() -> None:
    """`password` holds env content and keypairs hold PEM blocks — never guarded."""
    _guard_single_line("password", "logship_collector_env", b"A=1\nB=2\n", allow_multiline=False)
    _guard_single_line(
        "ssh-keypair", "k", b"-----BEGIN-----\nx\n-----END-----\n", allow_multiline=False
    )
    _guard_single_line("openstack-appcred", "c", b"clouds:\n  a: b\n", allow_multiline=False)


def test_guard_accepts_single_line_with_trailing_newline() -> None:
    _guard_single_line("api-token", "t", b"ONE_LINE_TOKEN\n", allow_multiline=False)


# --------------------------------------------------------------------------- #
# PAPER-03 — a stored value must reach git, and be visible when it hasn't.
# --------------------------------------------------------------------------- #


@needs_git
def test_uncommitted_lists_written_but_uncommitted_values(tmp_path) -> None:
    env = make_store_env(tmp_path)
    _init_repo(env.store_root)
    assert S.uncommitted(env.store_root) == []
    _put(env, "test_token", b"V")
    dirty = S.uncommitted(env.store_root)
    assert any("test_token.age" in p for p in dirty)


@needs_git
def test_store_commit_makes_the_store_clean_and_is_idempotent(tmp_path) -> None:
    env = make_store_env(tmp_path)
    _init_repo(env.store_root)
    _put(env, "test_token", b"V")
    assert S.store_commit(env.store_root, "add: test_token") is True
    assert S.uncommitted(env.store_root) == []
    # Nothing to commit the second time — reported as False, not an error.
    assert S.store_commit(env.store_root, "add: test_token") is False


def test_store_commit_is_non_fatal_outside_a_git_repo(tmp_path) -> None:
    env = make_store_env(tmp_path)  # no git init
    _put(env, "test_token", b"V")
    assert S.store_commit(env.store_root, "add: test_token") is False
    assert S.uncommitted(env.store_root) == []


@needs_git
def test_doctor_c10_flags_an_uncommitted_store(tmp_path) -> None:
    from mgf.secrets.doctor import Doctor

    env = make_store_env(tmp_path)
    _init_repo(env.store_root)
    _put(env, "test_token", b"V")
    doc = Doctor(env.registry, fed_root=env.fed_root, store_root=env.store_root)
    findings = doc.c10_store_committed()
    assert len(findings) == 1
    f = findings[0]
    assert f.check == "C10"
    assert f.severity == "high"
    assert "test_token" in f.evidence["ids"]

    S.store_commit(env.store_root, "add: test_token")
    assert doc.c10_store_committed() == []
