"""Bulk-safety harness: its OWN scratch database on the sidecar
Postgres (`with_m5_test` — independent of the reporting/migrate
scratch DBs so mutations here can never contaminate their fixtures),
migrated with the real alembic chain, loaded with the fixture legacy
dataset, and transformed once — the bulk engine then runs over the
same migrated shape production has.

Isolation: most tests run inside a per-test SAVEPOINT session (service
commit()s stay inside it; everything rolls back). The gate test uses
REAL commits — its undo restores the data byte-identically by design,
which is exactly what it proves.
"""

import os
import uuid
from pathlib import Path

import pytest
from sqlalchemy import create_engine, select, text
from sqlalchemy.orm import Session

from app.models import TimeEntry
from app.services.authz.context import resolve_actor
from tests.authz.conftest import fake_request

# read-only property tests against the REAL migrated sidecar data
from tests.reporting.conftest import (  # noqa: F401
    real_engine,
    real_rian,
    real_session,
)

ROOT = Path(__file__).resolve().parents[2]
FIXTURE_SQL = ROOT / "tests" / "migrate" / "fixtures" / "legacy_fixture.sql"
TEST_DB = "with_m5_test"

# fixture ids (tests/migrate/fixtures/legacy_fixture.sql; ids carry
# through the migration — 05 step 2.6 id-carry)
TE = {n: uuid.UUID(f"0a000000-0000-0000-0000-00000000000{n}") for n in range(1, 8)}


def _sidecar_url() -> str:
    from app.config import settings

    url = settings.with_db_url_host or settings.with_db_url
    if not url:
        pytest.fail(
            "bulk tests need the sidecar Postgres — set WITH_DB_URL_HOST"
            " in .env (127.0.0.1:54317)"
        )
    return url


@pytest.fixture(scope="session")
def m5_url() -> str:
    base = _sidecar_url()
    admin = create_engine(
        base, isolation_level="AUTOCOMMIT", connect_args={"connect_timeout": 3}
    )
    try:
        with admin.connect() as conn:
            conn.execute(text(f"DROP DATABASE IF EXISTS {TEST_DB} WITH (FORCE)"))
            conn.execute(text(f"CREATE DATABASE {TEST_DB}"))
    except Exception as exc:
        pytest.fail(f"cannot reach the sidecar Postgres for bulk tests: {exc}")
    finally:
        admin.dispose()

    url = base.rsplit("/", 1)[0] + f"/{TEST_DB}"

    from alembic.config import Config

    from alembic import command

    cfg = Config(str(ROOT / "alembic.ini"))
    cfg.set_main_option("script_location", str(ROOT / "alembic"))
    previous = os.environ.get("WITH_DB_URL")
    os.environ["WITH_DB_URL"] = url
    try:
        command.upgrade(cfg, "head")
    finally:
        os.environ["WITH_DB_URL"] = previous if previous is not None else ""
    return url


@pytest.fixture(scope="session")
def m5_engine(m5_url):
    engine = create_engine(m5_url)
    with engine.begin() as conn:
        conn.exec_driver_sql(FIXTURE_SQL.read_text(encoding="utf-8"))
    from app.services.migrate import run_all

    report = run_all(engine=engine)
    assert report.ok, report.render()
    yield engine
    engine.dispose()


@pytest.fixture()
def s(m5_engine):
    """SAVEPOINT-isolated session: service-level commit()s stay inside
    the outer transaction, which rolls back after the test."""
    conn = m5_engine.connect()
    trans = conn.begin()
    session = Session(bind=conn, join_transaction_mode="create_savepoint")
    yield session
    session.close()
    trans.rollback()
    conn.close()


def actor_named(session: Session, username: str):
    return resolve_actor(fake_request(username), session)


@pytest.fixture()
def rian(s):
    return actor_named(s, "rian")


@pytest.fixture()
def adi(s):
    return actor_named(s, "adi")


def entry(session: Session, n: int) -> TimeEntry:
    return session.scalars(select(TimeEntry).where(TimeEntry.id == TE[n])).one()
