# #region TestReportAuditImmutability [C:2] [TYPE Module]
# @RELATION  BINDS_TO -> SrcRoot
# @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
# @INVARIANT: Built reports are immutable snapshots; audit hooks produce append-only event traces.

from __future__ import annotations

from datetime import UTC, datetime
import pytest
from unittest.mock import patch

from src.models.clean_release import (
    ComplianceReport,
    ComplianceRun,
)
from src.services.clean_release.audit_service import (
    audit_check_run,
    audit_preparation,
    audit_report,
)
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


# #region _terminal_run [C:2] [TYPE Function]
# @RELATION  BINDS_TO -> TestReportAuditImmutability
# @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(UTC),
        started_at=datetime.now(UTC),
        finished_at=datetime.now(UTC),
        status=RunStatus.SUCCEEDED,
        final_status=final_status,
    )


# #endregion _terminal_run


# #region test_report_builder_sets_immutable_snapshot_flag [C:2] [TYPE Function]
# @RELATION  BINDS_TO -> TestReportAuditImmutability
# @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


# #endregion test_report_builder_sets_immutable_snapshot_flag


# #region test_repository_rejects_report_overwrite_for_same_report_id [C:2] [TYPE Function]
# @RELATION  BINDS_TO -> TestReportAuditImmutability
# @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(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(UTC),
        immutable=True,
    )

    repository.save_report(original)

    with pytest.raises(ValueError, match="immutable"):
        repository.save_report(mutated)


# #endregion test_repository_rejects_report_overwrite_for_same_report_id


# #region test_audit_hooks_emit_append_only_event_stream [C:2] [TYPE Function]
# @RELATION  BINDS_TO -> TestReportAuditImmutability
# @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.reason.call_count == 1
    assert mock_logger.reflect.call_count == 1
    assert mock_logger.explore.call_count == 1

    reason_msg = mock_logger.reason.call_args[0][0]
    reflect_msg = mock_logger.reflect.call_args[0][0]
    explore_msg = mock_logger.explore.call_args[0][0]

    assert reason_msg.startswith("clean-release preparation")
    assert reflect_msg.startswith("clean-release check_run")
    assert explore_msg.startswith("clean-release report_id")


# #endregion test_audit_hooks_emit_append_only_event_stream

# #endregion TestReportAuditImmutability
