"""Tests for type-mapping helpers in the SQL Server and Snowflake executors."""

from __future__ import annotations

import pytest

from test_runner.common.type_mapping import base_type_name as _base_type_name
from test_runner.capture.sources.sqlserver import _parse_sp_describe_rows
from test_runner.validate.targets.snowflake import _describe_to_column_types


# ---------------------------------------------------------------------------
# _base_type_name (SQL Server)
# ---------------------------------------------------------------------------

class TestBaseTypeName:
    def test_simple(self):
        assert _base_type_name("int") == "INT"

    def test_with_precision(self):
        assert _base_type_name("nvarchar(100)") == "NVARCHAR"

    def test_with_precision_and_scale(self):
        assert _base_type_name("decimal(18,2)") == "DECIMAL"

    def test_with_identity(self):
        assert _base_type_name("int identity") == "INT"

    def test_uppercase(self):
        assert _base_type_name("BIGINT") == "BIGINT"

    def test_mixed_case(self):
        assert _base_type_name("nVarChar(50)") == "NVARCHAR"

    def test_max_length(self):
        assert _base_type_name("nvarchar(max)") == "NVARCHAR"

    def test_datetime2(self):
        assert _base_type_name("datetime2(7)") == "DATETIME2"


# ---------------------------------------------------------------------------
# _parse_sp_describe_rows (SQL Server)
# ---------------------------------------------------------------------------

def _sp_row(name: str, system_type_name: str):
    """Build a mock sp_describe_first_result_set row."""
    return (0, 1, name, 1, 56, system_type_name)


_TEST_YAML_MAPPING = {
    "INT": "NUMBER",
    "BIGINT": "NUMBER",
    "SMALLINT": "NUMBER",
    "TINYINT": "NUMBER",
    "DECIMAL": "NUMBER",
    "NUMERIC": "NUMBER",
    "MONEY": "NUMBER",
    "FLOAT": "FLOAT",
    "REAL": "FLOAT",
    "BIT": "BOOLEAN",
    "VARCHAR": "VARCHAR",
    "NVARCHAR": "VARCHAR",
    "CHAR": "VARCHAR",
    "NCHAR": "VARCHAR",
    "TEXT": "VARCHAR",
    "DATETIME": "TIMESTAMP_NTZ",
    "DATETIME2": "TIMESTAMP_NTZ",
    "DATE": "DATE",
    "TIME": "TIME",
    "VARBINARY": "BINARY",
    "BINARY": "BINARY",
    "UNIQUEIDENTIFIER": "VARCHAR",
}


class TestParseSpDescribeRows:
    def test_basic_types(self):
        rows = [
            _sp_row("Id", "int"),
            _sp_row("Name", "nvarchar(100)"),
            _sp_row("Price", "decimal(18,2)"),
        ]
        result = _parse_sp_describe_rows(rows, _TEST_YAML_MAPPING)
        assert result == {"ID": "NUMBER", "NAME": "VARCHAR", "PRICE": "NUMBER"}

    def test_all_integer_types(self):
        rows = [
            _sp_row("a", "tinyint"),
            _sp_row("b", "smallint"),
            _sp_row("c", "int"),
            _sp_row("d", "bigint"),
        ]
        result = _parse_sp_describe_rows(rows, _TEST_YAML_MAPPING)
        assert all(v == "NUMBER" for v in result.values())

    def test_string_types(self):
        rows = [
            _sp_row("a", "varchar(50)"),
            _sp_row("b", "nvarchar(max)"),
            _sp_row("c", "char(10)"),
            _sp_row("d", "nchar(5)"),
        ]
        result = _parse_sp_describe_rows(rows, _TEST_YAML_MAPPING)
        assert all(v == "VARCHAR" for v in result.values())

    def test_datetime_types(self):
        rows = [
            _sp_row("a", "datetime"),
            _sp_row("b", "datetime2(7)"),
            _sp_row("c", "date"),
            _sp_row("d", "time(7)"),
        ]
        result = _parse_sp_describe_rows(rows, _TEST_YAML_MAPPING)
        assert result == {
            "A": "TIMESTAMP_NTZ",
            "B": "TIMESTAMP_NTZ",
            "C": "DATE",
            "D": "TIME",
        }

    def test_without_yaml_mapping(self):
        rows = [
            _sp_row("Id", "int"),
            _sp_row("Name", "nvarchar(100)"),
        ]
        result = _parse_sp_describe_rows(rows, None)
        assert result == {"ID": "INT", "NAME": "NVARCHAR"}

    def test_empty_rows(self):
        result = _parse_sp_describe_rows([], _TEST_YAML_MAPPING)
        assert result == {}

    def test_unknown_type_passes_through(self):
        rows = [_sp_row("x", "geography")]
        result = _parse_sp_describe_rows(rows, _TEST_YAML_MAPPING)
        assert result == {"X": "GEOGRAPHY"}

    def test_column_names_uppercased(self):
        rows = [_sp_row("myColumn", "int")]
        result = _parse_sp_describe_rows(rows, _TEST_YAML_MAPPING)
        assert "MYCOLUMN" in result


# ---------------------------------------------------------------------------
# _describe_to_column_types (Snowflake)
# ---------------------------------------------------------------------------

def _sf_desc(name: str, type_code: int):
    """Build a single Snowflake cursor.description tuple."""
    return (name, type_code, None, None, None, None, True)


class TestSfDescribeToColumnTypes:
    def test_basic_types(self):
        description = [
            _sf_desc("Id", 0),
            _sf_desc("Name", 2),
            _sf_desc("Active", 13),
        ]
        result = _describe_to_column_types(description)
        assert result == {"ID": "FIXED", "NAME": "TEXT", "ACTIVE": "BOOLEAN"}

    def test_all_common_types(self):
        description = [
            _sf_desc("a", 0),   # FIXED
            _sf_desc("b", 1),   # REAL
            _sf_desc("c", 2),   # TEXT
            _sf_desc("d", 3),   # DATE
            _sf_desc("e", 8),   # TIMESTAMP_NTZ
            _sf_desc("f", 7),   # TIMESTAMP_TZ
            _sf_desc("g", 11),  # BINARY
            _sf_desc("h", 12),  # TIME
            _sf_desc("i", 5),   # VARIANT
        ]
        result = _describe_to_column_types(description)
        assert result == {
            "A": "FIXED",
            "B": "REAL",
            "C": "TEXT",
            "D": "DATE",
            "E": "TIMESTAMP_NTZ",
            "F": "TIMESTAMP_TZ",
            "G": "BINARY",
            "H": "TIME",
            "I": "VARIANT",
        }

    def test_empty_description(self):
        result = _describe_to_column_types([])
        assert result == {}

    def test_unknown_type_code_defaults_to_varchar(self):
        description = [_sf_desc("mystery", 999)]
        result = _describe_to_column_types(description)
        assert result == {"MYSTERY": "VARCHAR"}

    def test_column_names_uppercased(self):
        description = [_sf_desc("myColumn", 2)]
        result = _describe_to_column_types(description)
        assert "MYCOLUMN" in result
