"""Tests for eval system in browser-hybrid."""

from __future__ import annotations

from unittest.mock import MagicMock, patch

import yaml

from browser_hybrid.evals import EvalCase, EvalReport, EvalResult, EvalRunner


class TestEvalModels:
    """Tests for eval data models."""

    def test_eval_case_init(self):
        """Test EvalCase creation."""
        case = EvalCase(
            name="test-case",
            suite="test-suite",
            input={"url": "example.com"},
            expected={"title": "Example"},
        )
        assert case.name == "test-case"
        assert case.suite == "test-suite"
        assert case.input["url"] == "example.com"
        assert case.expected["title"] == "Example"

    def test_eval_result_init(self):
        """Test EvalResult creation."""
        case = EvalCase("b", "a", {}, {})
        result = EvalResult(case=case, passed=True, duration_ms=123.4)
        assert result.case is case
        assert result.passed is True
        assert result.duration_ms == 123.4

    def test_eval_report_stats(self):
        """Test EvalReport calculations."""
        case = EvalCase("b", "a", {}, {})
        r1 = EvalResult(case=case, passed=True)
        r2 = EvalResult(case=case, passed=False)
        report = EvalReport(results=[r1, r2])

        assert report.total == 2
        assert report.passed_count == 1
        assert report.failed_count == 1
        assert report.pass_rate == 0.5
        assert "a" in report.by_suite()

    def test_eval_report_summary(self):
        """Test summary string generation."""
        case = EvalCase("test", "suite", {}, {})
        r = EvalResult(case=case, passed=False, error="Failed match")
        report = EvalReport(results=[r])
        summary = report.summary()
        assert "suite: 0/1 PASSED" in summary
        assert "Failed cases:" in summary
        assert "suite/test: Failed match" in summary

    def test_eval_report_to_json(self):
        """Test JSON serialization."""
        case = EvalCase("test", "suite", {"in": 1}, {"out": 2})
        r = EvalResult(case=case, passed=True, actual=2, duration_ms=10.0)
        report = EvalReport(results=[r])
        data = report.to_json()
        assert data["total"] == 1
        assert data["results"][0]["name"] == "test"
        assert data["results"][0]["actual"] == 2


class TestEvalRunner:
    """Tests for EvalRunner."""

    def test_runner_init_defaults(self):
        """Test runner initialization with defaults."""
        runner = EvalRunner()
        # Should resolve to project_root/evals
        assert "evals" in str(runner.evals_dir)

    def test_load_cases_empty(self, tmp_path):
        """Test loading cases from empty directory."""
        runner = EvalRunner(evals_dir=tmp_path)
        cases = runner.load_cases()
        assert len(cases) == 0

    def test_load_cases_from_yaml(self, tmp_path):
        """Test loading cases from YAML file."""
        suite_file = tmp_path / "test_suite.yaml"
        data = {
            "cases": [
                {"name": "case1", "input": {"x": 1}, "expected": {"y": 2}},
                {"name": "case2", "input": {"x": 3}, "expected": {"y": 4}},
            ]
        }
        with open(suite_file, "w") as f:
            yaml.dump(data, f)

        runner = EvalRunner(evals_dir=tmp_path)
        cases = runner.load_cases()
        assert len(cases) == 2
        assert cases[0].suite == "test_suite"
        assert cases[0].name == "case1"
        assert cases[1].name == "case2"

    @patch("browser_hybrid.evals.EvalRunner._execute_case")
    def test_run_cases(self, mock_execute, tmp_path):
        """Test running cases and generating report."""
        suite_file = tmp_path / "suite.yaml"
        with open(suite_file, "w") as f:
            yaml.dump({"cases": [{"name": "test", "expected": {"found": True}}]}, f)

        mock_execute.return_value = {"found": True}

        runner = EvalRunner(evals_dir=tmp_path, browser=MagicMock())
        report = runner.run()

        assert report.total == 1
        assert report.results[0].passed is True
        assert report.results[0].actual == {"found": True}

    def test_validate_rules(self):
        """Test validation logic for different rules."""
        runner = EvalRunner()

        # url_contains
        pass1, _ = runner._validate({"url": "http://google.com"}, {"url_contains": "google"})
        assert pass1 is True
        fail1, _ = runner._validate({"url": "http://bing.com"}, {"url_contains": "google"})
        assert fail1 is False

        # title
        pass2, _ = runner._validate({"title": "Home"}, {"title": "Home"})
        assert pass2 is True

        # min_count
        pass3, _ = runner._validate({"count": 5}, {"min_count": 3})
        assert pass3 is True
        fail3, _ = runner._validate({"count": 2}, {"min_count": 3})
        assert fail3 is False

        # browser_contains (info)
        pass4, _ = runner._validate({"browser": "Chrome/123"}, {"browser_contains": "Chrome"})
        assert pass4 is True
