from __future__ import annotations

import asyncio
from pathlib import Path

from aicd.tools import db_multi


def test_is_readonly_sql() -> None:
    assert db_multi.is_readonly_sql("SELECT 1")
    assert db_multi.is_readonly_sql("WITH x AS (SELECT 1) SELECT * FROM x")
    assert db_multi.is_readonly_sql("SHOW TABLES")
    assert db_multi.is_readonly_sql("DESC foo")
    assert not db_multi.is_readonly_sql("INSERT INTO t VALUES (1)")
    assert not db_multi.is_readonly_sql("UPDATE t SET x=1")
    assert not db_multi.is_readonly_sql("DELETE FROM t")


def test_write_confirm_required(tmp_path: Path) -> None:
    dbf = tmp_path / "w.db"

    async def _go() -> None:
        spec = db_multi.connection_from_dict(
            {"engine": "sqlite", "path": str(dbf.resolve())}
        )
        r = await db_multi.db_execute(
            spec,
            "CREATE TABLE a(id INT)",
            read_only=False,
            max_rows=10,
            write_confirm=None,
        )
        assert r["ok"] is False
        r2 = await db_multi.db_execute(
            spec,
            "CREATE TABLE a(id INT)",
            read_only=False,
            max_rows=10,
            write_confirm=db_multi.WRITE_CONFIRM,
        )
        assert r2.get("ok") is True

    asyncio.run(_go())


def test_sqlite_inspect_and_query(tmp_path: Path) -> None:
    dbf = tmp_path / "x.db"

    async def _go() -> None:
        import aiosqlite

        async with aiosqlite.connect(dbf) as db:
            await db.execute("CREATE TABLE jobs (id INTEGER PRIMARY KEY, name TEXT)")
            await db.execute("INSERT INTO jobs (name) VALUES ('a')")
            await db.commit()

        spec = db_multi.connection_from_dict(
            {"engine": "sqlite", "path": str(dbf.resolve())}
        )
        ping = await db_multi.db_ping(spec)
        assert ping["ok"] is True

        tables = await db_multi.db_inspect_operation(spec, "list_tables")
        assert tables["ok"] is True
        names = [r.get("name") for r in tables.get("rows", [])]
        assert "jobs" in names

        desc = await db_multi.db_inspect_operation(
            spec, "describe_table", table="jobs"
        )
        assert desc["ok"] is True

        q = await db_multi.db_execute(
            spec,
            "SELECT * FROM jobs",
            read_only=True,
            max_rows=50,
            write_confirm=None,
        )
        assert q["ok"] is True
        assert len(q["rows"]) == 1

    asyncio.run(_go())


def test_multi_statement_rejected() -> None:
    async def _go() -> None:
        spec = db_multi.connection_from_dict(
            {"engine": "sqlite", "path": ":memory:"}
        )
        r = await db_multi.db_execute(
            spec,
            "SELECT 1; SELECT 2",
            read_only=True,
            max_rows=10,
            write_confirm=None,
        )
        assert r["ok"] is False

    asyncio.run(_go())
