"""Shared fixtures for ``tests/web/db/`` — dual-backend repo testing.

The ``db_backends`` fixture parametrizes a repository instance over
SQLite (always) and MySQL (skipped when ``DECNET_TEST_MYSQL_URL`` is
unset). This is the single source of truth referenced by the design
doc's "every repo test runs against both SQLite and MySQL"
convention; new repo tests under ``tests/web/db/`` should consume
the fixture rather than instantiating their own backend.

MySQL is gated on env var rather than auto-detected because spinning
a real MySQL is heavy enough to belong in CI / live runs but not the
dev loop. Per project memory: "skip heavy test categories" in the
dev cycle.
"""
from __future__ import annotations

import os
from pathlib import Path
from typing import AsyncIterator

import pytest
import pytest_asyncio

from decnet.web.db.factory import get_repository
from decnet.web.db.repository import BaseRepository


_BACKENDS: list[str] = ["sqlite"]
if os.environ.get("DECNET_TEST_MYSQL_URL"):
    _BACKENDS.append("mysql")


@pytest_asyncio.fixture(params=_BACKENDS, ids=_BACKENDS)
async def db_backends(
    request: pytest.FixtureRequest,
    tmp_path: Path,
    monkeypatch: pytest.MonkeyPatch,
) -> AsyncIterator[BaseRepository]:
    """Yield an initialized :class:`BaseRepository` for each available
    backend.

    SQLite always runs (via ``aiosqlite`` + a tmp file). MySQL runs
    iff ``DECNET_TEST_MYSQL_URL`` is set in the environment to a real
    MySQL DSN — in that case the fixture writes a per-test schema
    name into ``DECNET_DB_URL`` so concurrent tests don't collide.
    """
    backend = request.param
    if backend == "sqlite":
        monkeypatch.setenv("DECNET_DB_TYPE", "sqlite")
        repo = get_repository(db_path=str(tmp_path / "ttp.db"))
    else:
        # MySQL — uses the operator-supplied DSN. Per dual-DB-backend
        # convention, dialect-specific behavior overrides land in the
        # MySQL repo class; this fixture does not paper over them.
        url = os.environ["DECNET_TEST_MYSQL_URL"]
        monkeypatch.setenv("DECNET_DB_TYPE", "mysql")
        monkeypatch.setenv("DECNET_DB_URL", url)
        repo = get_repository()
    await repo.initialize()
    try:
        yield repo
    finally:
        # SQLite is fully isolated per tmp_path; MySQL needs explicit
        # teardown that's the operator's responsibility (truncate or
        # drop schema in a CI hook). The repo close is best-effort.
        engine = getattr(repo, "engine", None)
        if engine is not None:
            try:
                await engine.dispose()
            except Exception:  # noqa: BLE001 — teardown best-effort
                pass
