"""Tests for SQL Server ODBC output converters."""

import struct
import unittest
from datetime import datetime, time, timedelta, timezone
from unittest.mock import MagicMock

from data_exchange_agent.data_sources import odbc_output_converters as conv


class TestDecodeToString(unittest.TestCase):
    """Tests for ``_decode_to_string``."""

    def test_none(self):
        self.assertIsNone(conv._decode_to_string(None))

    def test_str_passthrough(self):
        self.assertEqual(conv._decode_to_string("already"), "already")

    def test_utf16_le(self):
        raw = "snowflake".encode("utf-16-le")
        self.assertEqual(conv._decode_to_string(raw), "snowflake")

    def test_odd_length_falls_back_to_utf8(self):
        raw = b"abc"
        self.assertEqual(conv._decode_to_string(raw), "abc")

    def test_invalid_utf8_uses_replace(self):
        raw = b"\xff"
        self.assertEqual(conv._decode_to_string(raw), "\ufffd")


class TestDecodeTime2(unittest.TestCase):
    """Tests for ``_decode_time2``."""

    def test_none(self):
        self.assertIsNone(conv._decode_time2(None))

    def test_str_passthrough(self):
        self.assertEqual(conv._decode_time2("x"), "x")

    def test_packed_ten_bytes(self):
        frac_ns = 500_123_000
        buf = struct.pack("<HHHI", 14, 30, 45, frac_ns)
        self.assertEqual(len(buf), 10)
        t = conv._decode_time2(buf)
        self.assertEqual(t, time(14, 30, 45, 500_123))

    def test_padded_twelve_bytes(self):
        buf = struct.pack("<HHH2xI", 8, 9, 10, 0)
        self.assertEqual(len(buf), 12)
        t = conv._decode_time2(buf)
        self.assertEqual(t, time(8, 9, 10, 0))


class TestDecodeDatetimeoffset(unittest.TestCase):
    """Tests for ``_decode_datetimeoffset``."""

    def test_none(self):
        self.assertIsNone(conv._decode_datetimeoffset(None))

    def test_str_passthrough(self):
        self.assertEqual(conv._decode_datetimeoffset("y"), "y")

    def test_unpacks_tz_aware_datetime(self):
        frac_ns = 123_456_789
        buf = struct.pack("<hHHHHHIhh", 2025, 6, 1, 12, 34, 56, frac_ns, -7, 30)
        self.assertEqual(len(buf), 20)
        dt = conv._decode_datetimeoffset(buf)
        expected_tz = timezone(timedelta(hours=-7, minutes=30))
        self.assertEqual(
            dt,
            datetime(2025, 6, 1, 12, 34, 56, 123_456, tzinfo=expected_tz),
        )


class TestRegisterOutputConverters(unittest.TestCase):
    """Tests for ``register_output_converters``."""

    def test_registers_all_unsupported_type_codes(self):
        conn = MagicMock()
        conv.register_output_converters(conn)
        self.assertEqual(
            conn.add_output_converter.call_count,
            len(conv._UNSUPPORTED_ODBC_TYPE_CODES),
        )
        registered = {c.args[0]: c.args[1] for c in conn.add_output_converter.call_args_list}
        self.assertIs(registered[-155], conv._decode_datetimeoffset)
        self.assertIs(registered[-154], conv._decode_time2)
        self.assertIs(registered[-16], conv._decode_to_string)


if __name__ == "__main__":
    unittest.main()
