"""Tests for MysqlAdapter."""

from __future__ import annotations

from datetime import date, datetime

import pytest

from db_test_helpers.core._errors import UnsupportedError
from db_test_helpers.mysql._adapter import MysqlAdapter, _quote_identifier


class TestQuoteIdentifier:
    def test_normal_identifier(self):
        assert _quote_identifier("users") == "`users`"

    def test_identifier_with_backtick(self):
        assert _quote_identifier("my`table") == "`my``table`"

    def test_sql_keyword(self):
        assert _quote_identifier("select") == "`select`"

    def test_empty_string_raises(self):
        with pytest.raises(ValueError):
            _quote_identifier("")


class TestDocumentIdGuard:
    def test_rejects_document_id(self):
        from unittest.mock import MagicMock

        adapter = MysqlAdapter(MagicMock())
        with pytest.raises(UnsupportedError, match="MysqlAdapter does not support __document_id__"):
            adapter.write_record("t", "", {"__document_id__": "abc", "name": "Alice"})


class TestWriteRecord:
    def test_basic_write(self, conn):
        adapter = MysqlAdapter(conn)
        result = adapter.write_record("users", "", {"name": "Alice", "age": 30})

        assert result["name"] == "Alice"
        assert result["age"] == 30

        cursor = conn.cursor(dictionary=True)
        cursor.execute("SELECT * FROM users")
        rows = cursor.fetchall()
        cursor.close()
        assert len(rows) == 1
        assert rows[0]["name"] == "Alice"

    def test_write_multiple_records(self, conn):
        adapter = MysqlAdapter(conn)
        adapter.write_record("users", "", {"name": "Alice", "age": 30})
        adapter.write_record("users", "", {"name": "Bob", "age": 25})

        cursor = conn.cursor(dictionary=True)
        cursor.execute("SELECT * FROM users")
        rows = cursor.fetchall()
        cursor.close()
        assert len(rows) == 2

    def test_write_rejects_non_empty_parent_path(self):
        from unittest.mock import MagicMock

        adapter = MysqlAdapter(MagicMock())
        with pytest.raises(UnsupportedError, match="does not support parent_path"):
            adapter.write_record("users", "some/parent/path", {"name": "Alice", "age": 30})


class TestReadRecords:
    def test_read_empty(self, conn):
        adapter = MysqlAdapter(conn)
        result = adapter.read_records("users", "")
        assert result == []

    def test_read_multiple_records(self, conn):
        adapter = MysqlAdapter(conn)
        adapter.write_record("users", "", {"name": "Alice", "age": 30})
        adapter.write_record("users", "", {"name": "Bob", "age": 25})

        result = adapter.read_records("users", "")
        assert len(result) == 2
        names = {r["name"] for r in result}
        assert names == {"Alice", "Bob"}

    def test_read_converts_decimal_to_int(self, conn):
        adapter = MysqlAdapter(conn)
        adapter.write_record("users", "", {"name": "Alice", "score": 100})

        result = adapter.read_records("users", "")
        assert result[0]["score"] == 100
        assert isinstance(result[0]["score"], int)

    def test_read_converts_decimal_to_float(self, conn):
        adapter = MysqlAdapter(conn)
        adapter.write_record("users", "", {"name": "Alice", "score": 99.5})

        result = adapter.read_records("users", "")
        assert result[0]["score"] == 99.5
        assert isinstance(result[0]["score"], float)

    def test_read_converts_datetime_to_iso_string(self, conn):
        adapter = MysqlAdapter(conn)
        dt = datetime(2024, 1, 15, 10, 30, 0)
        adapter.write_record("users", "", {"name": "Alice", "created_at": dt})

        result = adapter.read_records("users", "")
        assert result[0]["created_at"] == "2024-01-15T10:30:00"
        assert isinstance(result[0]["created_at"], str)

    def test_read_converts_date_to_iso_string(self, conn):
        adapter = MysqlAdapter(conn)
        d = date(2024, 1, 15)
        adapter.write_record("users", "", {"name": "Alice", "birth_date": d})

        result = adapter.read_records("users", "")
        assert result[0]["birth_date"] == "2024-01-15"
        assert isinstance(result[0]["birth_date"], str)

    def test_read_converts_timedelta_to_string(self, conn):
        adapter = MysqlAdapter(conn)
        cursor = conn.cursor()
        cursor.execute("INSERT INTO users (name, login_time) VALUES (%s, %s)", ("Alice", "09:30:00"))
        conn.commit()
        cursor.close()

        result = adapter.read_records("users", "")
        assert result[0]["login_time"] == "09:30:00"
        assert isinstance(result[0]["login_time"], str)

    def test_read_converts_bytes_to_str(self, conn):
        adapter = MysqlAdapter(conn)
        cursor = conn.cursor()
        cursor.execute("INSERT INTO users (name, bio) VALUES (%s, %s)", ("Alice", b"Hello world"))
        conn.commit()
        cursor.close()

        result = adapter.read_records("users", "")
        assert result[0]["bio"] == "Hello world"
        assert isinstance(result[0]["bio"], str)

    def test_read_none_stays_none(self, conn):
        adapter = MysqlAdapter(conn)
        adapter.write_record("users", "", {"name": "Alice"})

        result = adapter.read_records("users", "")
        assert result[0]["email"] is None

    def test_read_rejects_non_empty_parent_path(self):
        from unittest.mock import MagicMock

        adapter = MysqlAdapter(MagicMock())
        with pytest.raises(UnsupportedError, match="does not support parent_path"):
            adapter.read_records("users", "some/parent/path")


class TestClearGroup:
    def test_clear_deletes_all_rows(self, conn):
        adapter = MysqlAdapter(conn)
        adapter.write_record("users", "", {"name": "Alice", "age": 30})
        adapter.write_record("users", "", {"name": "Bob", "age": 25})

        adapter.clear_group("users", "")

        result = adapter.read_records("users", "")
        assert result == []

    def test_clear_rejects_non_empty_parent_path(self):
        from unittest.mock import MagicMock

        adapter = MysqlAdapter(MagicMock())
        with pytest.raises(UnsupportedError, match="does not support parent_path"):
            adapter.clear_group("users", "some/parent/path")
