"""Tests for type-coerced result comparison."""

import pytest
from opencypher_compliance.adapters import QueryResult
from opencypher_compliance.comparator import compare_result, _coerce


class TestCoerce:
    def test_none(self):
        assert _coerce(None) is None

    def test_bool(self):
        assert _coerce(True) is True
        assert _coerce(False) is False

    def test_int(self):
        assert _coerce(42) == 42

    def test_float_whole_number_becomes_int(self):
        assert _coerce(5.0) == 5
        assert isinstance(_coerce(5.0), int)

    def test_float_fractional_stays_float(self):
        assert _coerce(3.14) == 3.14
        assert isinstance(_coerce(3.14), float)

    def test_string(self):
        assert _coerce("hello") == "hello"

    def test_list(self):
        assert _coerce([1, 2.0, "three"]) == [1, 2, "three"]

    def test_nested_list(self):
        assert _coerce([[1.0, 2.0]]) == [[1, 2]]

    def test_dict(self):
        assert _coerce({"a": 1.0}) == {"a": 1}

    def test_unknown_type_to_string(self):
        class Custom:
            def __str__(self):
                return "custom_val"
        assert _coerce(Custom()) == "custom_val"


class TestCompareResultExactMatch:
    def test_exact_match_pass(self):
        result = QueryResult(columns=["name"], rows=[["Alice"]])
        test = {"expected_columns": ["name"], "expected_rows": [["Alice"]]}
        assert compare_result(result, test)["passed"] is True

    def test_exact_match_fail_wrong_value(self):
        result = QueryResult(columns=["name"], rows=[["Bob"]])
        test = {"expected_columns": ["name"], "expected_rows": [["Alice"]]}
        out = compare_result(result, test)
        assert out["passed"] is False
        assert "Row mismatch" in out["reason"]

    def test_exact_match_fail_wrong_columns(self):
        result = QueryResult(columns=["wrong"], rows=[["Alice"]])
        test = {"expected_columns": ["name"], "expected_rows": [["Alice"]]}
        out = compare_result(result, test)
        assert out["passed"] is False
        assert "Column mismatch" in out["reason"]

    def test_multiple_rows(self):
        result = QueryResult(columns=["x"], rows=[[1], [2], [3]])
        test = {"expected_columns": ["x"], "expected_rows": [[1], [2], [3]]}
        assert compare_result(result, test)["passed"] is True

    def test_row_count_mismatch(self):
        result = QueryResult(columns=["x"], rows=[[1], [2]])
        test = {"expected_columns": ["x"], "expected_rows": [[1], [2], [3]]}
        assert compare_result(result, test)["passed"] is False


class TestCompareResultTypeCoercion:
    def test_int_equals_float(self):
        result = QueryResult(columns=["result"], rows=[[5.0]])
        test = {"expected_columns": ["result"], "expected_rows": [[5]]}
        assert compare_result(result, test)["passed"] is True

    def test_float_equals_int(self):
        result = QueryResult(columns=["result"], rows=[[5]])
        test = {"expected_columns": ["result"], "expected_rows": [[5.0]]}
        assert compare_result(result, test)["passed"] is True


class TestCompareResultNull:
    def test_null_match(self):
        result = QueryResult(columns=["x"], rows=[[None]])
        test = {"expected_columns": ["x"], "expected_rows": [[None]]}
        assert compare_result(result, test)["passed"] is True

    def test_null_vs_value_fail(self):
        result = QueryResult(columns=["x"], rows=[[None]])
        test = {"expected_columns": ["x"], "expected_rows": [["something"]]}
        assert compare_result(result, test)["passed"] is False


class TestCompareResultExpectSuccess:
    def test_expect_success_pass(self):
        result = QueryResult(columns=["x"], rows=[[1]])
        test = {"expect_success": True}
        assert compare_result(result, test)["passed"] is True

    def test_expect_success_with_error(self):
        test = {"expect_success": True}
        out = compare_result(QueryResult(), test, error=RuntimeError("boom"))
        assert out["passed"] is False
        assert "Query error" in out["reason"]


class TestCompareResultExpectError:
    def test_expect_error_with_error(self):
        test = {"expect_error": True}
        out = compare_result(QueryResult(), test, error=RuntimeError("boom"))
        assert out["passed"] is True

    def test_expect_error_without_error(self):
        result = QueryResult(columns=["x"], rows=[[1]])
        test = {"expect_error": True}
        out = compare_result(result, test)
        assert out["passed"] is False
        assert "Expected an error" in out["reason"]


class TestCompareResultExpectedContains:
    def test_contains_pass(self):
        result = QueryResult(columns=["x"], rows=[[1], [2], [3]])
        test = {"expected_contains": [[2]]}
        assert compare_result(result, test)["passed"] is True

    def test_contains_fail(self):
        result = QueryResult(columns=["x"], rows=[[1], [2], [3]])
        test = {"expected_contains": [[5]]}
        out = compare_result(result, test)
        assert out["passed"] is False
        assert "not found" in out["reason"]

    def test_contains_with_type_coercion(self):
        result = QueryResult(columns=["x"], rows=[[1.0], [2.0]])
        test = {"expected_contains": [[1]]}
        assert compare_result(result, test)["passed"] is True
