"""
Pure-logic tests for RejectionEngine.check_all_constraints.

These tests actually call check_all_constraints with non-empty constraint
dicts, using a hashable Constraint subclass to work around the default
dataclass unhashability.  This covers lines 130-143 which are not reached
by the sibling test file that only calls should_reject_candidate in a loop.
"""

from unittest.mock import patch

from local_deep_research.advanced_search_system.constraint_checking.evidence_analyzer import (
    ConstraintEvidence,
)
from local_deep_research.advanced_search_system.constraint_checking.rejection_engine import (
    RejectionEngine,
)
from local_deep_research.advanced_search_system.candidates.base_candidate import (
    Candidate,
)
from local_deep_research.advanced_search_system.constraints.base_constraint import (
    Constraint,
    ConstraintType,
)


class HashableConstraint(Constraint):
    """Constraint subclass that can be used as a dict key."""

    def __hash__(self):
        return hash(self.id)


def _constraint(cid, value="test", ctype=ConstraintType.PROPERTY):
    return HashableConstraint(
        id=cid, type=ctype, description=value, value=value, weight=1.0
    )


def _evidence(pos=0.8, neg=0.1):
    return ConstraintEvidence(
        positive_confidence=pos,
        negative_confidence=neg,
        uncertainty=1.0 - pos - neg,
        evidence_text="text",
        source="src",
    )


class TestCheckAllConstraintsActual:
    """Call check_all_constraints directly (not via should_reject_candidate)."""

    def test_empty_dict_returns_none(self):
        """Empty constraint_results returns None (accepted)."""
        engine = RejectionEngine()
        candidate = Candidate(name="Test")
        with patch(
            "local_deep_research.advanced_search_system.constraint_checking.rejection_engine.logger"
        ):
            result = engine.check_all_constraints(candidate, {})
        assert result is None

    def test_single_constraint_passes(self):
        """Single constraint with good evidence returns None."""
        engine = RejectionEngine()
        candidate = Candidate(name="Good Candidate")
        c = _constraint("c1")
        constraint_results = {c: [_evidence(pos=0.8, neg=0.1)]}
        with patch(
            "local_deep_research.advanced_search_system.constraint_checking.rejection_engine.logger"
        ):
            result = engine.check_all_constraints(candidate, constraint_results)
        assert result is None

    def test_single_constraint_rejects_high_negative(self):
        """Single constraint with high negative evidence triggers rejection."""
        engine = RejectionEngine(negative_threshold=0.25)
        candidate = Candidate(name="Bad Candidate")
        c = _constraint("c1")
        constraint_results = {c: [_evidence(pos=0.2, neg=0.5)]}
        with patch(
            "local_deep_research.advanced_search_system.constraint_checking.rejection_engine.logger"
        ):
            result = engine.check_all_constraints(candidate, constraint_results)
        assert result is not None
        assert result.should_reject is True
        assert "High negative evidence" in result.reason

    def test_single_constraint_rejects_low_positive(self):
        """Single constraint with low positive evidence triggers rejection."""
        engine = RejectionEngine(positive_threshold=0.4)
        candidate = Candidate(name="Weak Candidate")
        c = _constraint("c1")
        constraint_results = {c: [_evidence(pos=0.2, neg=0.1)]}
        with patch(
            "local_deep_research.advanced_search_system.constraint_checking.rejection_engine.logger"
        ):
            result = engine.check_all_constraints(candidate, constraint_results)
        assert result is not None
        assert result.should_reject is True

    def test_multiple_constraints_all_pass(self):
        """Multiple constraints all passing returns None."""
        engine = RejectionEngine()
        candidate = Candidate(name="Great Candidate")
        constraint_results = {
            _constraint("c1", "height"): [_evidence(pos=0.8, neg=0.1)],
            _constraint("c2", "weight"): [_evidence(pos=0.7, neg=0.1)],
            _constraint("c3", "color"): [_evidence(pos=0.9, neg=0.05)],
        }
        with patch(
            "local_deep_research.advanced_search_system.constraint_checking.rejection_engine.logger"
        ):
            result = engine.check_all_constraints(candidate, constraint_results)
        assert result is None

    def test_stops_at_first_rejection(self):
        """Returns the first rejected constraint, not all of them."""
        engine = RejectionEngine(negative_threshold=0.25)
        candidate = Candidate(name="Test")
        c1 = _constraint("c1", "passes")
        c2 = _constraint("c2", "fails")
        c3 = _constraint("c3", "also_fails")
        constraint_results = {
            c1: [_evidence(pos=0.8, neg=0.1)],
            c2: [_evidence(pos=0.2, neg=0.5)],
            c3: [_evidence(pos=0.1, neg=0.7)],
        }
        with patch(
            "local_deep_research.advanced_search_system.constraint_checking.rejection_engine.logger"
        ):
            result = engine.check_all_constraints(candidate, constraint_results)
        assert result is not None
        assert result.should_reject is True

    def test_no_evidence_does_not_reject(self):
        """Constraint with empty evidence list does not trigger rejection."""
        engine = RejectionEngine()
        candidate = Candidate(name="Unknown")
        c = _constraint("c1")
        constraint_results = {c: []}
        with patch(
            "local_deep_research.advanced_search_system.constraint_checking.rejection_engine.logger"
        ):
            result = engine.check_all_constraints(candidate, constraint_results)
        assert result is None

    def test_multiple_evidence_per_constraint(self):
        """Multiple evidence items are averaged correctly."""
        engine = RejectionEngine(negative_threshold=0.25)
        candidate = Candidate(name="Test")
        c = _constraint("c1")
        constraint_results = {
            c: [
                _evidence(pos=0.6, neg=0.1),
                _evidence(pos=0.8, neg=0.05),
                _evidence(pos=0.7, neg=0.15),
            ]
        }
        with patch(
            "local_deep_research.advanced_search_system.constraint_checking.rejection_engine.logger"
        ):
            result = engine.check_all_constraints(candidate, constraint_results)
        # avg_pos = 0.7, avg_neg = 0.1 -> should pass
        assert result is None
