# [DEF:backend.tests.services.clean_release.test_report_audit_immutability:Module]
# @TIER: CRITICAL
# @SEMANTICS: tests, clean-release, report, audit, immutability, append-only
# @PURPOSE: Validate report snapshot immutability expectations and append-only audit hook behavior for US2.
# @LAYER: Tests
# @RELATION: TESTS -> src.services.clean_release.report_builder.ComplianceReportBuilder
# @RELATION: TESTS -> src.services.clean_release.audit_service
# @RELATION: TESTS -> src.services.clean_release.repository.CleanReleaseRepository
# @INVARIANT: Built reports are immutable snapshots; audit hooks produce append-only event traces.

from __future__ import annotations

from datetime import datetime, timezone
from unittest.mock import patch

import pytest

from src.models.clean_release import ComplianceReport, ComplianceRun, ComplianceViolation
from src.services.clean_release.audit_service import audit_check_run, audit_preparation, audit_report, audit_violation
from src.services.clean_release.enums import ComplianceDecision, RunStatus
from src.services.clean_release.report_builder import ComplianceReportBuilder
from src.services.clean_release.repository import CleanReleaseRepository


# [DEF:_terminal_run:Function]
# @PURPOSE: Build deterministic terminal run fixture for report snapshot tests.
# @PRE: final_status is a valid ComplianceDecision value.
# @POST: Returns a terminal ComplianceRun suitable for report generation.
def _terminal_run(final_status: ComplianceDecision = ComplianceDecision.PASSED) -> ComplianceRun:
    return ComplianceRun(
        id="run-immut-1",
        candidate_id="cand-immut-1",
        manifest_id="manifest-immut-1",
        manifest_digest="digest-immut-1",
        policy_snapshot_id="policy-immut-1",
        registry_snapshot_id="registry-immut-1",
        requested_by="tester",
        requested_at=datetime.now(timezone.utc),
        started_at=datetime.now(timezone.utc),
        finished_at=datetime.now(timezone.utc),
        status=RunStatus.SUCCEEDED,
        final_status=final_status,
    )
# [/DEF:_terminal_run:Function]


# [DEF:test_report_builder_sets_immutable_snapshot_flag:Function]
# @PURPOSE: Ensure generated report payload is marked immutable and persisted as snapshot.
# @PRE: Terminal run exists.
# @POST: Built report has immutable=True and repository stores same immutable object.
def test_report_builder_sets_immutable_snapshot_flag():
    repository = CleanReleaseRepository()
    builder = ComplianceReportBuilder(repository)
    run = _terminal_run()

    report = builder.build_report_payload(run, [])
    persisted = builder.persist_report(report)

    assert report.immutable is True
    assert persisted.immutable is True
    assert repository.get_report(report.id) is persisted
# [/DEF:test_report_builder_sets_immutable_snapshot_flag:Function]


# [DEF:test_repository_rejects_report_overwrite_for_same_report_id:Function]
# @PURPOSE: Define immutability contract that report snapshots cannot be overwritten by same identifier.
# @PRE: Existing report with id is already persisted.
# @POST: Second save for same report id is rejected with explicit immutability error.
def test_repository_rejects_report_overwrite_for_same_report_id():
    repository = CleanReleaseRepository()
    original = ComplianceReport(
        id="CCR-immut-fixed-id",
        run_id="run-immut-1",
        candidate_id="cand-immut-1",
        final_status=ComplianceDecision.PASSED,
        summary_json={"operator_summary": "original", "violations_count": 0, "blocking_violations_count": 0},
        generated_at=datetime.now(timezone.utc),
        immutable=True,
    )
    mutated = ComplianceReport(
        id="CCR-immut-fixed-id",
        run_id="run-immut-2",
        candidate_id="cand-immut-2",
        final_status=ComplianceDecision.ERROR,
        summary_json={"operator_summary": "mutated", "violations_count": 1, "blocking_violations_count": 1},
        generated_at=datetime.now(timezone.utc),
        immutable=True,
    )

    repository.save_report(original)

    with pytest.raises(ValueError, match="immutable"):
        repository.save_report(mutated)
# [/DEF:test_repository_rejects_report_overwrite_for_same_report_id:Function]


# [DEF:test_audit_hooks_emit_append_only_event_stream:Function]
# @PURPOSE: Verify audit hooks emit one event per action call and preserve call order.
# @PRE: Logger backend is patched.
# @POST: Three calls produce three ordered info entries with molecular prefixes.
@patch("src.services.clean_release.audit_service.logger")
def test_audit_hooks_emit_append_only_event_stream(mock_logger):
    audit_preparation("cand-immut-1", "PREPARED")
    audit_check_run("run-immut-1", "PASSED")
    audit_report("CCR-immut-1", "cand-immut-1")

    assert mock_logger.info.call_count == 3
    logged_messages = [call.args[0] for call in mock_logger.info.call_args_list]
    assert logged_messages[0].startswith("[REASON]")
    assert logged_messages[1].startswith("[REFLECT]")
    assert logged_messages[2].startswith("[EXPLORE]")
# [/DEF:test_audit_hooks_emit_append_only_event_stream:Function]

# [/DEF:backend.tests.services.clean_release.test_report_audit_immutability:Module]