"""
Behavioral tests for base_candidate module.

Tests Candidate dataclass with evidence management and scoring logic.
"""

import pytest


class TestCandidateInit:
    """Tests for Candidate dataclass initialization."""

    def test_basic_construction(self):
        from local_deep_research.advanced_search_system.candidates.base_candidate import (
            Candidate,
        )

        c = Candidate(name="Lake X")
        assert c.name == "Lake X"

    def test_evidence_defaults_empty_dict(self):
        from local_deep_research.advanced_search_system.candidates.base_candidate import (
            Candidate,
        )

        c = Candidate(name="Lake X")
        assert c.evidence == {}

    def test_score_defaults_zero(self):
        from local_deep_research.advanced_search_system.candidates.base_candidate import (
            Candidate,
        )

        c = Candidate(name="Lake X")
        assert c.score == 0.0

    def test_metadata_defaults_empty_dict(self):
        from local_deep_research.advanced_search_system.candidates.base_candidate import (
            Candidate,
        )

        c = Candidate(name="Lake X")
        assert c.metadata == {}


class TestCandidateAddEvidence:
    """Tests for Candidate.add_evidence() method."""

    def test_add_single_evidence(self):
        from local_deep_research.advanced_search_system.candidates.base_candidate import (
            Candidate,
        )
        from local_deep_research.advanced_search_system.evidence.base_evidence import (
            Evidence,
            EvidenceType,
        )

        c = Candidate(name="Lake X")
        e = Evidence(
            claim="formed in ice age",
            type=EvidenceType.RESEARCH_FINDING,
            source="wiki",
        )
        c.add_evidence("c1", e)
        assert "c1" in c.evidence
        assert c.evidence["c1"] is e

    def test_add_multiple_evidence(self):
        from local_deep_research.advanced_search_system.candidates.base_candidate import (
            Candidate,
        )
        from local_deep_research.advanced_search_system.evidence.base_evidence import (
            Evidence,
            EvidenceType,
        )

        c = Candidate(name="Lake X")
        e1 = Evidence(
            claim="claim1", type=EvidenceType.DIRECT_STATEMENT, source="s1"
        )
        e2 = Evidence(
            claim="claim2", type=EvidenceType.NEWS_REPORT, source="s2"
        )
        c.add_evidence("c1", e1)
        c.add_evidence("c2", e2)
        assert len(c.evidence) == 2

    def test_overwrite_existing_evidence(self):
        from local_deep_research.advanced_search_system.candidates.base_candidate import (
            Candidate,
        )
        from local_deep_research.advanced_search_system.evidence.base_evidence import (
            Evidence,
            EvidenceType,
        )

        c = Candidate(name="Lake X")
        e1 = Evidence(claim="old", type=EvidenceType.INFERENCE, source="s1")
        e2 = Evidence(
            claim="new", type=EvidenceType.DIRECT_STATEMENT, source="s2"
        )
        c.add_evidence("c1", e1)
        c.add_evidence("c1", e2)
        assert c.evidence["c1"].claim == "new"


class TestCandidateCalculateScore:
    """Tests for Candidate.calculate_score() method."""

    def test_empty_constraints_returns_zero(self):
        from local_deep_research.advanced_search_system.candidates.base_candidate import (
            Candidate,
        )

        c = Candidate(name="Lake X")
        assert c.calculate_score([]) == 0.0

    def test_single_constraint_with_evidence(self):
        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,
        )
        from local_deep_research.advanced_search_system.evidence.base_evidence import (
            Evidence,
            EvidenceType,
        )

        c = Candidate(name="Lake X")
        constraint = Constraint(
            id="c1",
            type=ConstraintType.PROPERTY,
            description="d",
            value="v",
            weight=1.0,
        )
        evidence = Evidence(
            claim="supports",
            type=EvidenceType.DIRECT_STATEMENT,
            source="s",
            confidence=0.8,
        )
        c.add_evidence("c1", evidence)
        score = c.calculate_score([constraint])
        assert score == pytest.approx(0.8)

    def test_score_considers_weight(self):
        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,
        )
        from local_deep_research.advanced_search_system.evidence.base_evidence import (
            Evidence,
            EvidenceType,
        )

        c = Candidate(name="Lake X")
        c1 = Constraint(
            id="c1",
            type=ConstraintType.PROPERTY,
            description="d",
            value="v",
            weight=2.0,
        )
        c2 = Constraint(
            id="c2",
            type=ConstraintType.EVENT,
            description="d",
            value="v",
            weight=1.0,
        )
        e1 = Evidence(
            claim="e1",
            type=EvidenceType.DIRECT_STATEMENT,
            source="s",
            confidence=0.9,
        )
        e2 = Evidence(
            claim="e2",
            type=EvidenceType.NEWS_REPORT,
            source="s",
            confidence=0.3,
        )
        c.add_evidence("c1", e1)
        c.add_evidence("c2", e2)
        score = c.calculate_score([c1, c2])
        # (0.9*2.0 + 0.3*1.0) / (2.0 + 1.0) = 2.1 / 3.0 = 0.7
        assert score == pytest.approx(0.7)

    def test_missing_evidence_contributes_zero(self):
        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,
        )

        c = Candidate(name="Lake X")
        constraint = Constraint(
            id="c1",
            type=ConstraintType.PROPERTY,
            description="d",
            value="v",
            weight=1.0,
        )
        score = c.calculate_score([constraint])
        assert score == 0.0

    def test_score_stored_on_candidate(self):
        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,
        )
        from local_deep_research.advanced_search_system.evidence.base_evidence import (
            Evidence,
            EvidenceType,
        )

        c = Candidate(name="Lake X")
        constraint = Constraint(
            id="c1", type=ConstraintType.PROPERTY, description="d", value="v"
        )
        evidence = Evidence(
            claim="c",
            type=EvidenceType.DIRECT_STATEMENT,
            source="s",
            confidence=0.7,
        )
        c.add_evidence("c1", evidence)
        c.calculate_score([constraint])
        assert c.score == pytest.approx(0.7)


class TestCandidateGetUnverifiedConstraints:
    """Tests for Candidate.get_unverified_constraints() method."""

    def test_all_unverified_when_no_evidence(self):
        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,
        )

        c = Candidate(name="Lake X")
        constraints = [
            Constraint(
                id="c1",
                type=ConstraintType.PROPERTY,
                description="d",
                value="v",
            ),
            Constraint(
                id="c2", type=ConstraintType.EVENT, description="d", value="v"
            ),
        ]
        unverified = c.get_unverified_constraints(constraints)
        assert len(unverified) == 2

    def test_none_unverified_when_all_have_evidence(self):
        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,
        )
        from local_deep_research.advanced_search_system.evidence.base_evidence import (
            Evidence,
            EvidenceType,
        )

        c = Candidate(name="Lake X")
        constraint = Constraint(
            id="c1", type=ConstraintType.PROPERTY, description="d", value="v"
        )
        c.add_evidence(
            "c1", Evidence(claim="c", type=EvidenceType.INFERENCE, source="s")
        )
        assert len(c.get_unverified_constraints([constraint])) == 0

    def test_partial_evidence_returns_missing(self):
        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,
        )
        from local_deep_research.advanced_search_system.evidence.base_evidence import (
            Evidence,
            EvidenceType,
        )

        c = Candidate(name="Lake X")
        c1 = Constraint(
            id="c1", type=ConstraintType.PROPERTY, description="d", value="v"
        )
        c2 = Constraint(
            id="c2", type=ConstraintType.EVENT, description="d", value="v"
        )
        c.add_evidence(
            "c1", Evidence(claim="c", type=EvidenceType.INFERENCE, source="s")
        )
        unverified = c.get_unverified_constraints([c1, c2])
        assert len(unverified) == 1
        assert unverified[0].id == "c2"


class TestCandidateGetWeakEvidence:
    """Tests for Candidate.get_weak_evidence() method."""

    def test_empty_evidence_returns_empty(self):
        from local_deep_research.advanced_search_system.candidates.base_candidate import (
            Candidate,
        )

        c = Candidate(name="Lake X")
        assert c.get_weak_evidence() == []

    def test_returns_low_confidence_ids(self):
        from local_deep_research.advanced_search_system.candidates.base_candidate import (
            Candidate,
        )
        from local_deep_research.advanced_search_system.evidence.base_evidence import (
            Evidence,
            EvidenceType,
        )

        c = Candidate(name="Lake X")
        c.add_evidence(
            "c1",
            Evidence(
                claim="weak",
                type=EvidenceType.SPECULATION,
                source="s",
                confidence=0.1,
            ),
        )
        c.add_evidence(
            "c2",
            Evidence(
                claim="strong",
                type=EvidenceType.DIRECT_STATEMENT,
                source="s",
                confidence=0.9,
            ),
        )
        weak = c.get_weak_evidence(threshold=0.5)
        assert "c1" in weak
        assert "c2" not in weak

    def test_custom_threshold(self):
        from local_deep_research.advanced_search_system.candidates.base_candidate import (
            Candidate,
        )
        from local_deep_research.advanced_search_system.evidence.base_evidence import (
            Evidence,
            EvidenceType,
        )

        c = Candidate(name="Lake X")
        c.add_evidence(
            "c1",
            Evidence(
                claim="c",
                type=EvidenceType.NEWS_REPORT,
                source="s",
                confidence=0.75,
            ),
        )
        # Default threshold is 0.5 - should not be weak
        assert "c1" not in c.get_weak_evidence(threshold=0.5)
        # Higher threshold - should be weak
        assert "c1" in c.get_weak_evidence(threshold=0.8)
