"""Test harness: a throwaway SQLite DB per test, get_db overridden, a TestClient
that does NOT run lifespan startup (so schedulers/SLMM polling stay off)."""
import uuid
import pytest
from datetime import datetime
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from starlette.testclient import TestClient

from backend.database import Base, get_db
import backend.models as models  # noqa: F401  (ensure all tables are registered on Base)


@pytest.fixture()
def db_session(tmp_path):
    db_file = tmp_path / "test.db"
    engine = create_engine(f"sqlite:///{db_file}", connect_args={"check_same_thread": False})
    Base.metadata.create_all(bind=engine)
    TestingSession = sessionmaker(autocommit=False, autoflush=False, bind=engine)
    sess = TestingSession()
    try:
        yield sess
    finally:
        sess.close()
        engine.dispose()


@pytest.fixture()
def client(db_session):
    from backend.main import app  # imported lazily so module side effects are contained
    def _override():
        yield db_session
    app.dependency_overrides[get_db] = _override
    # No `with` → lifespan/startup events do not run (no scheduler/SLMM threads).
    c = TestClient(app)
    yield c
    app.dependency_overrides.pop(get_db, None)


@pytest.fixture(autouse=True)
def _reset_portal_lockout():
    """Portal lockout state is a module-global dict; clear it between tests so
    one test's failed attempts can't lock out another."""
    try:
        import backend.portal_auth as _pa
        if hasattr(_pa, "_failures"):
            _pa._failures.clear()
    except Exception:
        pass
    yield


@pytest.fixture(autouse=True)
def _operator_auth_off_by_default(monkeypatch):
    """Pin the operator-auth gate OFF for every test, so the suite is deterministic
    regardless of the container's OPERATOR_AUTH_ENABLED env (the dev container may
    have it ON for manual testing). Tests that exercise the gate opt in via
    wire_operator_auth(enabled=True), which overrides this within the test body."""
    try:
        import backend.operator_auth as _oa
        monkeypatch.setattr(_oa, "OPERATOR_AUTH_ENABLED", False, raising=False)
    except Exception:
        pass
    yield


def make_project(db_session, name=None, **kwargs):
    """Insert and return a Project with a unique name."""
    p = models.Project(
        id=str(uuid.uuid4()),
        name=name or f"Proj {uuid.uuid4().hex[:8]}",
        status="active",
        created_at=datetime.utcnow(),
        **kwargs,
    )
    db_session.add(p)
    db_session.commit()
    return p


def wire_operator_auth(monkeypatch, db_session, enabled=True):
    """Point the gate middleware's SessionLocal at the test engine and flip the
    flag. The middleware opens its OWN session (it can't use the get_db override),
    so it must read the same engine the test writes to."""
    import backend.operator_auth as oa
    from sqlalchemy.orm import sessionmaker
    maker = sessionmaker(bind=db_session.get_bind(), autocommit=False, autoflush=False)
    monkeypatch.setattr(oa, "SessionLocal", maker, raising=False)
    monkeypatch.setattr(oa, "OPERATOR_AUTH_ENABLED", enabled, raising=False)
    return oa
