"""
tests/test_conflict_resolution_audit.py
Conflict Resolution Audit — bilateral audit trail for CONTRADICT events.

Verifies that:
- Both winner and loser memories receive `contradict` audit events
- All winner values (new/existing/unknown) produce audit records
- `reason`, `role`, `opponent_memory_id` are recorded in details
- Source trust override records `original_winner`
"""
import json

import pytest


# ── Fixtures ────────────────────────────────────────────────────


@pytest.fixture
def handler(mock_ai):
    from trustmem.handler.persistent import PersistentHandler

    h = PersistentHandler(database_url=":memory:", ai_client=mock_ai)
    h.reset_stores()
    return h


# ── Helpers ─────────────────────────────────────────────────────


def _contradict(handler, existing_id, *, winner="new", reason="test reason",
                loser_confidence="low", source_trust="derived"):
    """Run _handle_contradict and return result."""
    fact = {
        "content": "New contradicting fact",
        "confidence": "high",
        "winner": winner,
        "loser_confidence": loser_confidence,
        "reason": reason,
    }
    return handler._handle_contradict(
        fact, "New contradicting fact", existing_id,
        add_tags=[], add_metadata={},
        source_trust=source_trust,
        user_id="test",
    )


def _get_contradict_audits(handler, memory_id):
    """Get contradict audit events for a memory."""
    trail = handler.get_audit_trail(memory_id)
    return [e for e in trail if e.get("event_type") == "contradict"]


def _parse_detail(audit_event):
    """Parse details JSON from audit event."""
    raw = audit_event.get("details", "{}")
    if isinstance(raw, str):
        return json.loads(raw)
    return raw


# ── winner=new ──────────────────────────────────────────────────


class TestWinnerNew:
    """When winner='new', new memory is winner, existing is loser."""

    def test_both_memories_get_audit_events(self, handler):
        existing = handler.add_memory("test", "Old fact")
        result = _contradict(handler, existing["memory_id"], winner="new")

        new_audits = _get_contradict_audits(handler, result["memory_id"])
        existing_audits = _get_contradict_audits(handler, existing["memory_id"])

        assert len(new_audits) == 1
        assert len(existing_audits) == 1

    def test_new_memory_is_winner(self, handler):
        existing = handler.add_memory("test", "Old fact")
        result = _contradict(handler, existing["memory_id"], winner="new")

        audit = _get_contradict_audits(handler, result["memory_id"])[0]
        detail = _parse_detail(audit)

        assert detail["role"] == "winner"
        assert detail["winner"] == "new"
        assert detail["opponent_memory_id"] == existing["memory_id"]

    def test_existing_memory_is_loser(self, handler):
        existing = handler.add_memory("test", "Old fact")
        result = _contradict(handler, existing["memory_id"], winner="new")

        audit = _get_contradict_audits(handler, existing["memory_id"])[0]
        detail = _parse_detail(audit)

        assert detail["role"] == "loser"
        assert detail["winner"] == "new"
        assert detail["opponent_memory_id"] == result["memory_id"]

    def test_loser_confidence_downgraded(self, handler):
        existing = handler.add_memory("test", "Old fact")
        _contradict(handler, existing["memory_id"], winner="new",
                    loser_confidence="low")

        audit = _get_contradict_audits(handler, existing["memory_id"])[0]
        assert audit["new_confidence"] == "low"

    def test_reason_recorded(self, handler):
        existing = handler.add_memory("test", "Old fact")
        result = _contradict(handler, existing["memory_id"], winner="new",
                             reason="Budget updated in March 2026")

        for mid in [result["memory_id"], existing["memory_id"]]:
            audit = _get_contradict_audits(handler, mid)[0]
            detail = _parse_detail(audit)
            assert detail["reason"] == "Budget updated in March 2026"


# ── winner=existing ─────────────────────────────────────────────


class TestWinnerExisting:
    """When winner='existing', existing is winner, new memory is loser."""

    def test_both_memories_get_audit_events(self, handler):
        existing = handler.add_memory("test", "Trusted fact")
        result = _contradict(handler, existing["memory_id"], winner="existing")

        new_audits = _get_contradict_audits(handler, result["memory_id"])
        existing_audits = _get_contradict_audits(handler, existing["memory_id"])

        assert len(new_audits) == 1
        assert len(existing_audits) == 1

    def test_existing_memory_is_winner(self, handler):
        existing = handler.add_memory("test", "Trusted fact")
        result = _contradict(handler, existing["memory_id"], winner="existing")

        audit = _get_contradict_audits(handler, existing["memory_id"])[0]
        detail = _parse_detail(audit)

        assert detail["role"] == "winner"
        assert detail["winner"] == "existing"
        assert detail["opponent_memory_id"] == result["memory_id"]

    def test_new_memory_is_loser(self, handler):
        existing = handler.add_memory("test", "Trusted fact")
        result = _contradict(handler, existing["memory_id"], winner="existing")

        audit = _get_contradict_audits(handler, result["memory_id"])[0]
        detail = _parse_detail(audit)

        assert detail["role"] == "loser"
        assert detail["opponent_memory_id"] == existing["memory_id"]

    def test_existing_confidence_unchanged(self, handler):
        existing = handler.add_memory("test", "Trusted fact")
        _contradict(handler, existing["memory_id"], winner="existing")

        audit = _get_contradict_audits(handler, existing["memory_id"])[0]
        assert audit["old_confidence"] == audit["new_confidence"]


# ── winner=unknown ──────────────────────────────────────────────


class TestWinnerUnknown:
    """When winner='unknown', both memories get 'unknown' role."""

    def test_both_memories_get_audit_events(self, handler):
        existing = handler.add_memory("test", "Opinion A")
        result = _contradict(handler, existing["memory_id"], winner="unknown")

        new_audits = _get_contradict_audits(handler, result["memory_id"])
        existing_audits = _get_contradict_audits(handler, existing["memory_id"])

        assert len(new_audits) == 1
        assert len(existing_audits) == 1

    def test_both_have_unknown_role(self, handler):
        existing = handler.add_memory("test", "Opinion A")
        result = _contradict(handler, existing["memory_id"], winner="unknown")

        for mid in [result["memory_id"], existing["memory_id"]]:
            audit = _get_contradict_audits(handler, mid)[0]
            detail = _parse_detail(audit)
            assert detail["role"] == "unknown"
            assert detail["winner"] == "unknown"

    def test_existing_confidence_unchanged(self, handler):
        existing = handler.add_memory("test", "Opinion A")
        _contradict(handler, existing["memory_id"], winner="unknown")

        audit = _get_contradict_audits(handler, existing["memory_id"])[0]
        assert audit["old_confidence"] == audit["new_confidence"]


# ── Source Trust Override ───────────────────────────────────────


class TestSourceTrustOverride:
    """Source trust hierarchy override records original_winner."""

    def test_override_records_original_winner(self, handler):
        """When LLM says winner=new but source_trust forces existing."""
        existing = handler.add_memory(
            "test", "Verified fact", _source_trust="verified",
        )
        result = _contradict(
            handler, existing["memory_id"],
            winner="new",  # LLM says new, but verified > derived
            source_trust="derived",
        )

        # Source trust override should force winner=existing
        assert result["winner"] == "existing"
        assert result["source_trust_override"] is True

        audit = _get_contradict_audits(handler, existing["memory_id"])[0]
        detail = _parse_detail(audit)

        assert detail["source_trust_override"] is True
        assert detail["original_winner"] == "new"
        assert detail["winner"] == "existing"
        assert detail["role"] == "winner"

    def test_override_records_trust_levels(self, handler):
        """Override detail includes both source trust levels."""
        existing = handler.add_memory(
            "test", "Verified fact", _source_trust="verified",
        )
        _contradict(
            handler, existing["memory_id"],
            winner="new", source_trust="derived",
        )

        audit = _get_contradict_audits(handler, existing["memory_id"])[0]
        detail = _parse_detail(audit)

        assert detail["new_source_trust"] == "derived"
        assert detail["existing_source_trust"] == "verified"

    def test_no_override_has_null_original_winner(self, handler):
        """When no override, original_winner should be null."""
        existing = handler.add_memory("test", "Fact")
        result = _contradict(
            handler, existing["memory_id"],
            winner="new", source_trust="derived",
        )

        audit = _get_contradict_audits(handler, result["memory_id"])[0]
        detail = _parse_detail(audit)

        assert detail["source_trust_override"] is False
        assert detail["original_winner"] is None


# ── Opponent Memory Linkage ─────────────────────────────────────


class TestOpponentLinkage:
    """opponent_memory_id correctly links the pair."""

    def test_mutual_reference(self, handler):
        """Both audit events reference each other via opponent_memory_id."""
        existing = handler.add_memory("test", "Fact A")
        result = _contradict(handler, existing["memory_id"], winner="new")

        new_audit = _get_contradict_audits(handler, result["memory_id"])[0]
        existing_audit = _get_contradict_audits(handler, existing["memory_id"])[0]

        new_detail = _parse_detail(new_audit)
        existing_detail = _parse_detail(existing_audit)

        # Mutual reference
        assert new_detail["opponent_memory_id"] == existing["memory_id"]
        assert existing_detail["opponent_memory_id"] == result["memory_id"]


# ── Integration with _commit_facts ──────────────────────────────


class TestCommitFactsIntegration:
    """CONTRADICT through _commit_facts produces bilateral audit."""

    def test_commit_facts_contradict_creates_bilateral_audit(self, handler):
        existing = handler.add_memory("test", "Original fact")
        facts = [{
            "content": "Updated fact",
            "type": "semantic",
            "confidence": "high",
            "action": "CONTRADICT",
            "conflict_with": existing["memory_id"],
            "winner": "new",
            "loser_confidence": "low",
            "reason": "temporal update",
        }]
        extracted, stats = handler._commit_facts(facts, user_id="test")
        assert stats["contradicted"] == 1

        new_mid = extracted[0]["memory_id"]

        # Both sides have audit
        new_audits = _get_contradict_audits(handler, new_mid)
        existing_audits = _get_contradict_audits(handler, existing["memory_id"])

        assert len(new_audits) == 1
        assert len(existing_audits) == 1

        # Reason preserved through pipeline
        new_detail = _parse_detail(new_audits[0])
        assert new_detail["reason"] == "temporal update"
