import os

import pytest
import pytest_asyncio
from httpx import ASGITransport, AsyncClient
from sqlalchemy.ext.asyncio import async_sessionmaker, create_async_engine

# Provide required settings for the test environment before any app imports resolve them
os.environ.setdefault("JWT_SECRET_KEY", "test-secret-key-for-testing-only")
os.environ.setdefault("OWNER_USERNAME", "testowner")
os.environ.setdefault("OWNER_PASSWORD", "testpassword")

from app.auth.jwt_provider import JWTAuthProvider  # noqa: E402
from app.config import get_settings  # noqa: E402
from app.database import Base  # noqa: E402
from app.dependencies import get_auth, get_db, get_storage  # noqa: E402
from app.main import app  # noqa: E402

# Bust the LRU cache so get_settings() picks up the env vars set above
get_settings.cache_clear()

_TEST_JWT_SECRET = os.environ["JWT_SECRET_KEY"]
_TEST_OWNER_USERNAME = os.environ["OWNER_USERNAME"]
_TEST_OWNER_PASSWORD = os.environ["OWNER_PASSWORD"]


@pytest_asyncio.fixture(scope="session", loop_scope="session")
async def engine():
    settings = get_settings()
    db_url = os.getenv("TEST_DATABASE_URL", settings.database_url)
    eng = create_async_engine(db_url, echo=False)
    async with eng.begin() as conn:
        await conn.run_sync(Base.metadata.create_all)
    yield eng
    async with eng.begin() as conn:
        await conn.run_sync(Base.metadata.drop_all)
    await eng.dispose()


@pytest_asyncio.fixture
async def db_session(engine):
    session_factory = async_sessionmaker(engine, expire_on_commit=False)
    async with session_factory() as session:
        yield session
        await session.rollback()


@pytest_asyncio.fixture
async def client(db_session):
    from app.auth.noop import NoOpAuthProvider
    from app.storage.s3_backend import S3StorageBackend

    storage = S3StorageBackend()
    auth = NoOpAuthProvider()

    async def override_db():
        yield db_session

    def override_storage():
        return storage

    def override_auth():
        return auth

    app.dependency_overrides[get_db] = override_db
    app.dependency_overrides[get_storage] = override_storage
    app.dependency_overrides[get_auth] = override_auth

    async with AsyncClient(transport=ASGITransport(app=app), base_url="http://test") as c:
        yield c

    app.dependency_overrides.clear()


@pytest_asyncio.fixture
async def jwt_auth_provider() -> JWTAuthProvider:
    return JWTAuthProvider(
        secret_key=_TEST_JWT_SECRET,
        expiry_seconds=3600,
        owner_username=_TEST_OWNER_USERNAME,
        owner_password=_TEST_OWNER_PASSWORD,
    )


@pytest_asyncio.fixture
async def authed_client(db_session, jwt_auth_provider):
    from app.storage.s3_backend import S3StorageBackend

    storage = S3StorageBackend()
    auth = jwt_auth_provider

    async def override_db():
        yield db_session

    def override_storage():
        return storage

    def override_auth():
        return auth

    app.dependency_overrides[get_db] = override_db
    app.dependency_overrides[get_storage] = override_storage
    app.dependency_overrides[get_auth] = override_auth

    valid_token = auth.create_token()
    async with AsyncClient(transport=ASGITransport(app=app), base_url="http://test") as c:
        yield c, valid_token

    app.dependency_overrides.clear()


def pytest_configure(config):
    db_url = os.getenv("TEST_DATABASE_URL") or os.getenv("DATABASE_URL", "")
    if not db_url.startswith("postgresql+asyncpg://"):
        pytest.exit(
            "Integration tests require a PostgreSQL database "
            "(postgresql+asyncpg://...). "
            "Set TEST_DATABASE_URL or DATABASE_URL accordingly. "
            f"Got: {db_url!r}",
            returncode=1,
        )
