"""Tests for scoring functions."""

from finagent_evals.scoring import (
    score_intent,
    score_tools,
    score_content,
    score_safety,
    score_tool_execution,
    score_verification,
    score_ground_truth,
    aggregate_results,
    WEIGHT_INTENT,
    WEIGHT_TOOLS,
    WEIGHT_CONTENT,
    WEIGHT_SAFETY,
    WEIGHT_CONFIDENCE,
    WEIGHT_VERIFICATION,
    PASS_THRESHOLD,
    CONTENT_SYNONYMS,
    INTENT_EQUIVALENCE,
)


class TestScoreIntent:
    def test_exact_match(self):
        assert score_intent("risk_check", "risk_check") == 1.0

    def test_equivalent(self):
        assert score_intent("portfolio_health", "portfolio_overview") == 1.0

    def test_mismatch(self):
        assert score_intent("risk_check", "general") == 0.0

    def test_none_expected(self):
        assert score_intent(None, "anything") == 1.0


class TestScoreTools:
    def test_all_present(self):
        score, errors = score_tools(["a", "b"], ["a", "b", "c"], False)
        assert score == 1.0
        assert errors == []

    def test_missing(self):
        score, errors = score_tools(["a", "b"], ["a"], False)
        assert score == 0.0
        assert len(errors) > 0

    def test_exact_extra(self):
        score, errors = score_tools(["a"], ["a", "b"], True)
        assert score == 0.0


class TestScoreContent:
    def test_all_found(self):
        score, errors = score_content("portfolio aapl goog", ["portfolio", "aapl"], [])
        assert score == 1.0

    def test_partial(self):
        score, errors = score_content("portfolio data", ["portfolio", "aapl"], [])
        assert score == 0.5

    def test_synonym(self):
        score, _ = score_content("the activity was logged", ["recorded"], [])
        assert score == 1.0  # "logged" is synonym for "recorded"


class TestScoreSafety:
    def test_clean(self):
        score, _ = score_safety("everything is fine", ["error", "crash"])
        assert score == 1.0

    def test_violation(self):
        score, _ = score_safety("there was an error", ["error"])
        assert score == 0.0


class TestScoreToolExecution:
    def test_no_errors(self):
        score, _ = score_tool_execution({"tool1": {"data": "ok"}})
        assert score == 1.0

    def test_with_error(self):
        score, errors = score_tool_execution({"tool1": {"error": "failed"}})
        assert score == 0.0
        assert len(errors) == 1


class TestScoreVerification:
    def test_none(self):
        assert score_verification(None) == 1.0

    def test_passed(self):
        assert score_verification({"passed": True}) == 1.0

    def test_failed(self):
        assert score_verification({"passed": False}) == 0.0


class TestScoreGroundTruth:
    def test_found(self):
        score, _ = score_ground_truth("aapl is at $187.50", ["187", "aapl"])
        assert score == 1.0

    def test_missing(self):
        score, _ = score_ground_truth("price is unknown", ["187"])
        assert score == 0.0


class TestWeights:
    def test_weights_sum_to_one(self):
        total = WEIGHT_INTENT + WEIGHT_TOOLS + WEIGHT_CONTENT + WEIGHT_SAFETY + WEIGHT_CONFIDENCE + WEIGHT_VERIFICATION
        assert abs(total - 1.0) < 1e-9


class TestAggregation:
    def test_aggregate(self):
        results = [
            {"passed": True, "overall_score": 0.9, "category": "risk_check",
             "scores": {"intent": 1.0, "tools": 1.0, "content": 1.0, "safety": 1.0, "confidence": 0.5, "verification": 1.0},
             "tools_called": ["get_portfolio_snapshot"], "tool_errors": []},
            {"passed": False, "overall_score": 0.4, "category": "general",
             "scores": {"intent": 0.0, "tools": 0.0, "content": 1.0, "safety": 1.0, "confidence": 0.0, "verification": 1.0},
             "tools_called": [], "tool_errors": []},
        ]
        agg = aggregate_results(results)
        assert agg["total"] == 2
        assert agg["passed"] == 1
        assert agg["pass_rate_pct"] == 50.0
        assert agg["avg_overall_score"] == 0.65  # (0.9 + 0.4) / 2
        assert "risk_check" in agg["by_category"]
        assert "general" in agg["by_category"]

    def test_aggregate_accepts_score_key(self):
        """aggregate_results accepts 'score' as well as 'overall_score'."""
        results = [
            {"passed": True, "score": 0.95, "category": "portfolio_overview"},
            {"passed": True, "score": 0.85, "category": "portfolio_overview"},
        ]
        agg = aggregate_results(results)
        assert agg["avg_overall_score"] == 0.9  # (0.95 + 0.85) / 2

    def test_aggregate_accepts_scores_overall(self):
        """aggregate_results accepts scores.overall when overall_score is missing."""
        results = [
            {"passed": True, "scores": {"overall": 0.95}, "category": "general"},
            {"passed": False, "scores": {"overall": 0.3}, "category": "general"},
        ]
        agg = aggregate_results(results)
        assert agg["avg_overall_score"] == 0.625  # (0.95 + 0.3) / 2
