"""Tests for routing logic in test_runner.validate.comparator.compare_results."""

from __future__ import annotations

from unittest.mock import MagicMock, patch

import pytest

from test_runner.common.config import ValidationConfiguration
from test_runner.common.models import ComparisonResult
from test_runner.validate.comparator import compare_results


# ---------------------------------------------------------------------------
# Helpers
# ---------------------------------------------------------------------------

def _make_config(threshold: int = 10_000) -> MagicMock:
    cfg = MagicMock()
    cfg.validation_database = "TESTDB"
    vc = ValidationConfiguration(in_memory_row_threshold=threshold)
    cfg.validation_configuration = vc
    return cfg


def _make_baseline(row_count: int | None = None) -> MagicMock:
    bl = MagicMock()
    bl.row_counts = [row_count] if row_count is not None else []
    return bl


def _pass_result() -> ComparisonResult:
    return ComparisonResult(match=True, match_type="data_match")


def _fail_result() -> ComparisonResult:
    return ComparisonResult(match=False, match_type="data_mismatch")


# ---------------------------------------------------------------------------
# TestCompareResultsRouting
# ---------------------------------------------------------------------------

class TestCompareResultsRouting:
    @patch("test_runner.validate.comparator.InMemoryResultComparator")
    @patch("test_runner.validate.comparator.ResultComparator")
    def test_uses_in_memory_when_below_threshold(self, MockResult, MockInMemory):
        MockInMemory.return_value.compare.return_value = _pass_result()
        cfg = _make_config(threshold=10_000)
        bl = _make_baseline(row_count=500)
        compare_results(cfg, MagicMock(), "CALL P()", "h", "P", baseline=bl)
        MockInMemory.assert_called_once()
        MockResult.assert_not_called()

    @patch("test_runner.validate.comparator.InMemoryResultComparator")
    @patch("test_runner.validate.comparator.ResultComparator")
    def test_uses_in_memory_when_equal_to_threshold(self, MockResult, MockInMemory):
        MockInMemory.return_value.compare.return_value = _pass_result()
        cfg = _make_config(threshold=500)
        bl = _make_baseline(row_count=500)
        compare_results(cfg, MagicMock(), "CALL P()", "h", "P", baseline=bl)
        MockInMemory.assert_called_once()
        MockResult.assert_not_called()

    @patch("test_runner.validate.comparator.InMemoryResultComparator")
    @patch("test_runner.validate.comparator.ResultComparator")
    def test_uses_transient_table_when_above_threshold(self, MockResult, MockInMemory):
        MockResult.return_value.compare.return_value = _pass_result()
        cfg = _make_config(threshold=10_000)
        bl = _make_baseline(row_count=10_001)
        compare_results(cfg, MagicMock(), "CALL P()", "h", "P", baseline=bl)
        MockResult.assert_called_once()
        MockInMemory.assert_not_called()

    @patch("test_runner.validate.comparator.InMemoryResultComparator")
    @patch("test_runner.validate.comparator.ResultComparator")
    def test_uses_in_memory_when_no_baseline(self, MockResult, MockInMemory):
        MockInMemory.return_value.compare.return_value = _pass_result()
        cfg = _make_config(threshold=10_000)
        compare_results(cfg, MagicMock(), "CALL P()", "h", "P", baseline=None)
        MockInMemory.assert_called_once()
        MockResult.assert_not_called()

    @patch("test_runner.validate.comparator.InMemoryResultComparator")
    @patch("test_runner.validate.comparator.ResultComparator")
    def test_uses_in_memory_when_no_row_counts(self, MockResult, MockInMemory):
        MockInMemory.return_value.compare.return_value = _pass_result()
        cfg = _make_config(threshold=10_000)
        bl = _make_baseline(row_count=None)
        compare_results(cfg, MagicMock(), "CALL P()", "h", "P", baseline=bl)
        MockInMemory.assert_called_once()
        MockResult.assert_not_called()

    @patch("test_runner.validate.comparator.InMemoryResultComparator")
    @patch("test_runner.validate.comparator.ResultComparator")
    def test_zero_threshold_defaults_to_in_memory(self, MockResult, MockInMemory):
        MockInMemory.return_value.compare.return_value = _pass_result()
        cfg = _make_config(threshold=0)
        bl = _make_baseline(row_count=5)
        compare_results(cfg, MagicMock(), "CALL P()", "h", "P", baseline=bl)
        MockInMemory.assert_called_once()
        MockResult.assert_not_called()

    @patch("test_runner.validate.comparator.InMemoryResultComparator")
    @patch("test_runner.validate.comparator.ResultComparator")
    def test_no_validation_config_defaults_to_in_memory(self, MockResult, MockInMemory):
        MockInMemory.return_value.compare.return_value = _pass_result()
        cfg = MagicMock()
        cfg.validation_configuration = None
        cfg.validation_database = "DB"
        bl = _make_baseline(row_count=5)
        compare_results(cfg, MagicMock(), "CALL P()", "h", "P", baseline=bl)
        MockInMemory.assert_called_once()
        MockResult.assert_not_called()

    @patch("test_runner.validate.comparator.InMemoryResultComparator")
    @patch("test_runner.validate.comparator.ResultComparator")
    def test_outputs_only_uses_in_memory(self, MockResult, MockInMemory):
        MockInMemory.return_value.compare.return_value = _pass_result()
        cfg = _make_config(threshold=10_000)
        bl = _make_baseline(row_count=None)
        compare_results(
            cfg, MagicMock(), "CALL P()", "h", "P",
            baseline=bl,
            outputs_only=True,
            outputs_sql="BEGIN SELECT 1; END;",
        )
        MockInMemory.assert_called_once()
        MockResult.assert_not_called()
