# tests/conftest.py - Updated main conftest with core fixtures only
"""
Core pytest configuration and fixtures.

IMPORTANT - Fixture Best Practices:
===================================
1. DO NOT use db.expunge() on fixtures - it detaches objects from the session
   and breaks lazy loading of relationships (e.g., product.marketplace_product).

2. Test isolation is achieved through the db fixture which drops and recreates
   all tables after each test - no need to manually detach objects.

3. If you need to ensure an object has fresh data, use db.refresh(obj) instead
   of expunge/re-query patterns.

4. The session uses expire_on_commit=False to prevent objects from being expired
   after commits, which helps with relationship access across operations.

See docs/testing/testing-guide.md for comprehensive testing documentation.
"""
import pytest
from fastapi.testclient import TestClient
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from sqlalchemy.pool import StaticPool

from app.core.database import Base, get_db
from main import app

# Import all models to ensure they're registered with Base metadata

# Use in-memory SQLite database for tests
SQLALCHEMY_TEST_DATABASE_URL = "sqlite:///:memory:"


@pytest.fixture(scope="session")
def engine():
    """Create test database engine."""
    return create_engine(
        SQLALCHEMY_TEST_DATABASE_URL,
        connect_args={"check_same_thread": False},
        poolclass=StaticPool,
        echo=False,  # Set to True for SQL debugging
    )


@pytest.fixture(scope="session")
def testing_session_local(engine):
    """
    Create session factory for tests.

    Uses expire_on_commit=False to prevent objects from being expired after
    commits. This allows fixtures to remain usable after database operations
    without needing to refresh or re-query them.
    """
    return sessionmaker(
        autocommit=False,
        autoflush=False,
        bind=engine,
        expire_on_commit=False,  # Prevents lazy-load issues after commits
    )


@pytest.fixture(scope="function")
def db(engine, testing_session_local):
    """
    Create a database session for each test function.

    Provides test isolation by:
    - Creating fresh tables before each test
    - Dropping all tables after each test completes

    Note: Fixtures should NOT use db.expunge() as this detaches objects
    from the session and breaks lazy loading. The table drop/create cycle
    provides sufficient isolation between tests.
    """
    # Create all tables
    Base.metadata.create_all(bind=engine)

    # Create session
    db_session = testing_session_local()

    try:
        yield db_session
    finally:
        db_session.close()
        # Clean up all data after each test
        Base.metadata.drop_all(bind=engine)
        Base.metadata.create_all(bind=engine)


@pytest.fixture(scope="function")
def client(db):
    """Create a test client with database dependency override"""

    # Override the dependency to use our test database
    def override_get_db():
        try:
            yield db
        finally:
            pass  # Don't close here, the db fixture handles it

    app.dependency_overrides[get_db] = override_get_db

    try:
        client = TestClient(app)
        yield client
    finally:
        # Clean up the dependency override
        if get_db in app.dependency_overrides:
            del app.dependency_overrides[get_db]


# Cleanup fixture to ensure clean state
@pytest.fixture(autouse=True)
def cleanup():
    """Automatically clean up after each test"""
    yield
    # Clear any remaining dependency overrides
    app.dependency_overrides.clear()


# Import fixtures from fixture modules
pytest_plugins = [
    "tests.fixtures.auth_fixtures",
    "tests.fixtures.marketplace_product_fixtures",
    "tests.fixtures.vendor_fixtures",
    "tests.fixtures.customer_fixtures",
    "tests.fixtures.marketplace_import_job_fixtures",
    "tests.fixtures.testing_fixtures",
]
