from __future__ import annotations

from pathlib import Path

from openpyxl import Workbook
import pytest

from tigrbl import TigrblApp
from tigrbl import rpc_call
from tigrbl.engine import EngineSpec
from tigrbl.orm.mixins import GUIDPk
from tigrbl.specs import F, IO, S
from tigrbl.shortcuts import acol
from tigrbl.table import Table
from tigrbl.types import Mapped, String

from tigrbl_engine_xlsx import register, xlsx_engine


class XlsxWidget(Table, GUIDPk):
    __tablename__ = "widgets"

    name: Mapped[str] = acol(
        storage=S(type_=String(50), nullable=False),
        field=F(py_type=str),
        io=IO(
            in_verbs=("create", "update", "replace"),
            out_verbs=("read", "list"),
            mutable_verbs=("create", "update", "replace"),
        ),
    )


@pytest.fixture()
def app_and_db(tmp_path: Path) -> tuple[TigrblApp, object]:
    register()
    path = tmp_path / "demo.xlsx"
    workbook = Workbook()
    sheet = workbook.active
    sheet.title = XlsxWidget.__tablename__
    sheet.append(["id", "name"])
    workbook.save(path)

    spec = EngineSpec(kind="xlsx", mapping={"path": str(path), "pk": "id"})
    _, session_factory = xlsx_engine(mapping=spec.mapping)
    db = session_factory()

    api = TigrblApp(engine=spec)
    api.include_table(XlsxWidget, mount_router=False)
    api.initialize()
    return api, db


def _snapshot_xlsx(db: object) -> list[dict[str, object]]:
    rows = db.table(XlsxWidget.__tablename__)  # type: ignore[attr-defined]
    return sorted(rows, key=lambda row: str(row.get("id")))


@pytest.mark.asyncio
async def test_xlsx_engine_builtin_rpc_crud_ops(
    app_and_db: tuple[TigrblApp, object],
) -> None:
    app, db = app_and_db

    before = _snapshot_xlsx(db)
    assert before == []

    created = await rpc_call(app, XlsxWidget, "create", {"name": "a"}, db=db)
    after_create = _snapshot_xlsx(db)

    fetched = await rpc_call(app, XlsxWidget, "read", {"id": created["id"]}, db=db)
    after_read = _snapshot_xlsx(db)

    updated = await rpc_call(
        app, XlsxWidget, "update", {"id": created["id"], "name": "b"}, db=db
    )
    after_update = _snapshot_xlsx(db)

    replaced = await rpc_call(
        app, XlsxWidget, "replace", {"id": created["id"], "name": "c"}, db=db
    )
    after_replace = _snapshot_xlsx(db)

    listed = await rpc_call(app, XlsxWidget, "list", {}, db=db)
    after_list = _snapshot_xlsx(db)

    cleared = await rpc_call(
        app, XlsxWidget, "clear", {"filters": {"name": "c"}}, db=db
    )
    after_clear = _snapshot_xlsx(db)

    created2 = await rpc_call(app, XlsxWidget, "create", {"name": "d"}, db=db)
    after_create2 = _snapshot_xlsx(db)

    deleted = await rpc_call(app, XlsxWidget, "delete", {"id": created2["id"]}, db=db)
    after_delete = _snapshot_xlsx(db)

    assert fetched["id"] == created["id"]
    assert updated["name"] == "b"
    assert replaced["name"] == "c"
    assert len(listed) == 1
    assert cleared == {"deleted": 1}
    assert deleted == {"deleted": 1}

    assert after_create == [{"id": created["id"], "name": "a"}]
    assert after_read == after_create
    assert after_update == [{"id": created["id"], "name": "b"}]
    assert after_replace == [{"id": created["id"], "name": "c"}]
    assert after_list == after_replace
    assert after_clear == []
    assert after_create2 == [{"id": created2["id"], "name": "d"}]
    assert after_delete == []
