"""Mock tests for DataTable DB helpers."""

import pytest

import soigia.datatable as dtpkg
from soigia.datatable import DataTable
from tests.datatable_mock_helpers import FakeEngine


def test_db_connector_and_default_connector_roundtrip(monkeypatch):
    connector = dtpkg.db_connector(
        connection_string="sqlite:///base.db",
        mysql="mysql:///db",
        maria="maria:///db",
        oracle="oracle:///db",
        postgres="postgres:///db",
        sqlite="sqlite:///db",
    )

    assert isinstance(connector, dtpkg.DatabaseConnector)
    assert dtpkg._connector() is connector
    assert connector._engine_url("sqlite") == "sqlite:///db"
    assert connector._engine_url("mysql") == "mysql:///db"
    assert connector._engine_url("postgres") == "postgres:///db"

    monkeypatch.setenv("SOIGIA_ORACLE_URL", "oracle://from-env")
    fresh = dtpkg.DatabaseConnector()
    assert fresh._engine_url("oracle") == "oracle://from-env"

    default_sqlite = dtpkg.DatabaseConnector()._engine_url("sqlite")
    assert default_sqlite.startswith("sqlite:///")


def test_db_engine_and_query_branches(monkeypatch):
    fake_engine = FakeEngine()

    monkeypatch.setattr(dtpkg, "create_engine", lambda url: fake_engine)
    monkeypatch.setattr(dtpkg.pd, "read_sql_query", lambda sql, engine, **kwargs: dtpkg.pd.DataFrame({"sql": [sql]}))

    connector = dtpkg.DatabaseConnector(connection_string="sqlite:///memory.db")

    engine = connector._engine("sqlite")
    assert engine is fake_engine

    table = connector.query("select 1", "sqlite")
    assert isinstance(table, DataTable)
    assert table.to_rows() == [{"sql": "select 1"}]
    assert fake_engine.disposed is True


def test_db_query_disposes_engine_on_exception(monkeypatch):
    fake_engine = FakeEngine()

    monkeypatch.setattr(dtpkg, "create_engine", lambda url: fake_engine)

    def boom(sql, engine, **kwargs):
        raise RuntimeError("query failed")

    monkeypatch.setattr(dtpkg.pd, "read_sql_query", boom)
    connector = dtpkg.DatabaseConnector(connection_string="sqlite:///memory.db")

    with pytest.raises(RuntimeError):
        connector.query("select 1", "sqlite")

    assert fake_engine.disposed is True


def test_db_engine_import_error_branch(monkeypatch):
    monkeypatch.setattr(dtpkg, "create_engine", None)
    connector = dtpkg.DatabaseConnector(connection_string="sqlite:///memory.db")

    with pytest.raises(ImportError):
        connector._engine("sqlite")


def test_db_missing_connection_string_error():
    connector = dtpkg.DatabaseConnector()

    assert connector.from_sql("select 1").iloc[0]["1"] == 1

    with pytest.raises(ValueError):
        connector.from_mysql_db("select 1")


def test_db_from_sql_and_backend_wrappers(monkeypatch):
    calls = []

    def fake_query(self, sql, backend, connection_string=None, **kwargs):
        calls.append((backend, sql, connection_string, kwargs))
        return DataTable({"backend": [backend], "sql": [sql]})

    monkeypatch.setattr(dtpkg.DatabaseConnector, "query", fake_query)
    monkeypatch.setattr(dtpkg, "_DEFAULT_CONNECTOR", None)

    connector = dtpkg.db_connector(connection_string="sqlite:///default.db")
    assert connector.from_sql("select 1").iloc[0]["backend"] == "sqlite"
    assert connector.from_mysql_db("select 1").iloc[0]["backend"] == "mysql"
    assert connector.from_maria_db("select 1").iloc[0]["backend"] == "maria"
    assert connector.from_oracle_db("select 1").iloc[0]["backend"] == "oracle"
    assert connector.from_postgres_db("select 1").iloc[0]["backend"] == "postgres"
    assert connector.from_sqlite_db("select 1").iloc[0]["backend"] == "sqlite"

    monkeypatch.setattr(dtpkg, "_DEFAULT_CONNECTOR", connector)
    assert dtpkg.from_sql("select 1").iloc[0]["backend"] == "sqlite"
    assert dtpkg.from_mysql_db("select 1").iloc[0]["backend"] == "mysql"
    assert dtpkg.from_maria_db("select 1").iloc[0]["backend"] == "maria"
    assert dtpkg.from_oracle_db("select 1").iloc[0]["backend"] == "oracle"
    assert dtpkg.from_postgres_db("select 1").iloc[0]["backend"] == "postgres"
    assert dtpkg.from_sqlite_db("select 1").iloc[0]["backend"] == "sqlite"

    assert len(calls) >= 12


def test_db_classmethod_delegation(monkeypatch):
    monkeypatch.setattr(dtpkg, "from_sql", lambda sql, backend="sqlite", connection_string=None, **kwargs: DataTable({"kind": [backend]}))
    monkeypatch.setattr(dtpkg, "from_mysql_db", lambda sql, connection_string=None, **kwargs: DataTable({"kind": ["mysql"]}))
    monkeypatch.setattr(dtpkg, "from_maria_db", lambda sql, connection_string=None, **kwargs: DataTable({"kind": ["maria"]}))
    monkeypatch.setattr(dtpkg, "from_oracle_db", lambda sql, connection_string=None, **kwargs: DataTable({"kind": ["oracle"]}))
    monkeypatch.setattr(dtpkg, "from_postgres_db", lambda sql, connection_string=None, **kwargs: DataTable({"kind": ["postgres"]}))
    monkeypatch.setattr(dtpkg, "from_sqlite_db", lambda sql, connection_string=None, **kwargs: DataTable({"kind": ["sqlite"]}))

    assert DataTable.from_sql("select 1")["kind"].iloc[0] == "sqlite"
    assert DataTable.from_mysql_db("select 1")["kind"].iloc[0] == "mysql"
    assert DataTable.from_maria_db("select 1")["kind"].iloc[0] == "maria"
    assert DataTable.from_oracle_db("select 1")["kind"].iloc[0] == "oracle"
    assert DataTable.from_postgres_db("select 1")["kind"].iloc[0] == "postgres"
    assert DataTable.from_sqlite_db("select 1")["kind"].iloc[0] == "sqlite"
