"""Task10 evidence authenticity contracts at the candidate boundary."""

from __future__ import annotations

import os
from pathlib import Path

import pytest

from scripts.nutricoach_v150_candidate_cli import CliNamespace
from scripts.nutricoach_v150_r71b_task10_evidence import (
    R71B_TASK10_EVIDENCE,
    Task10EvidenceError,
    load_candidate_task10_evidence,
    task10_evidence_spec,
    validate_fixed_task10_evidence_sources,
    verify_candidate_task10_evidence,
)
from scripts.seal_nutricoach_v150_candidate import copy_r71b_evidence

_INPUTS = {
    "r70_error": "inputs/r70-canonical-authority-drift.json",
    "observer": "inputs/r71b-observer-final.json",
    "health_recovery": "inputs/r71b-fixed-collector.json",
}


def _arguments() -> CliNamespace:
    arguments = CliNamespace()
    arguments.r70_error_evidence = task10_evidence_spec("r70_error").source_path
    arguments.observer_evidence = task10_evidence_spec("observer").source_path
    arguments.fixed_collector_evidence = task10_evidence_spec(
        "health_recovery"
    ).source_path
    return arguments


def _seal_task10_copies(tmp_path: Path) -> Path:
    candidate = tmp_path / "candidate"
    copied = copy_r71b_evidence(_arguments(), candidate)
    for path in copied:
        path.chmod(0o444)
    return candidate


def test_candidate_sealer_accepts_only_the_exact_resolved_task10_sources(
    tmp_path: Path,
) -> None:
    # Given / When
    candidate = _seal_task10_copies(tmp_path)
    copies = verify_candidate_task10_evidence(candidate, _INPUTS)

    # Then
    assert tuple(copy.input_name for copy in copies) == (
        "r70_error",
        "observer",
        "health_recovery",
    )
    assert tuple(copy.source_path for copy in copies) == tuple(
        str(spec.source_path) for spec in R71B_TASK10_EVIDENCE
    )
    assert tuple(copy.source_sha256 for copy in copies) == tuple(
        spec.source_sha256 for spec in R71B_TASK10_EVIDENCE
    )
    assert tuple(copy.source_schema for copy in copies) == tuple(
        spec.source_schema for spec in R71B_TASK10_EVIDENCE
    )
    assert tuple(copy.source_status for copy in copies) == tuple(
        spec.status for spec in R71B_TASK10_EVIDENCE
    )


def test_candidate_sealer_rejects_a_same_basename_source_outside_the_plan_root(
    tmp_path: Path,
) -> None:
    # Given
    arguments = _arguments()
    arguments.r70_error_evidence = tmp_path / "task-10-cron-no-send-recovery-analysis.json"
    _ = arguments.r70_error_evidence.write_text("{}\n", encoding="utf-8")

    # When / Then
    with pytest.raises(Task10EvidenceError, match="task10_evidence_path"):
        _ = copy_r71b_evidence(arguments, tmp_path / "candidate")
    assert not (tmp_path / "candidate").exists()


@pytest.mark.parametrize("mutation", ("noncanonical", "symlink", "hardlink", "mode"))
def test_candidate_verifier_rejects_malformed_or_substituted_task10_copies(
    tmp_path: Path,
    mutation: str,
) -> None:
    # Given
    candidate = _seal_task10_copies(tmp_path)
    path = candidate / _INPUTS["observer"]
    if mutation == "noncanonical":
        payload = path.read_bytes()
        path.chmod(0o644)
        _ = path.write_bytes(payload + b"\n")
        path.chmod(0o444)
    elif mutation == "symlink":
        target = tmp_path / "source.json"
        _ = target.write_bytes(path.read_bytes())
        path.unlink()
        path.symlink_to(target)
    elif mutation == "hardlink":
        linked = tmp_path / "linked.json"
        os.link(path, linked)
    else:
        path.chmod(0o644)

    # When / Then
    with pytest.raises(Task10EvidenceError):
        _ = load_candidate_task10_evidence(
            candidate,
            _INPUTS["observer"],
            task10_evidence_spec("observer"),
        )


def test_exact_task10_sources_enforce_the_expected_source_metadata() -> None:
    # Given / When
    evidence = validate_fixed_task10_evidence_sources(
        task10_evidence_spec("r70_error").source_path,
        task10_evidence_spec("observer").source_path,
        task10_evidence_spec("health_recovery").source_path,
    )

    # Then
    assert tuple(item.spec.source_sha256 for item in evidence) == tuple(
        spec.source_sha256 for spec in R71B_TASK10_EVIDENCE
    )
