"""Test the ORM implementation for ResearchStrategy model and related functions."""

import pytest
from sqlalchemy import create_engine, event
from sqlalchemy.orm import sessionmaker

from src.local_deep_research.web.database.models import (
    Base,
    Research,
    ResearchMode,
    ResearchStatus,
    ResearchStrategy,
)
from src.local_deep_research.web.services.research_service import (
    get_research_strategy,
    save_research_strategy,
)


@pytest.fixture
def test_db():
    """Create a test database for each test."""
    # Create an in-memory SQLite database with foreign key support
    engine = create_engine("sqlite:///:memory:")

    # Enable foreign key constraints for SQLite
    @event.listens_for(engine, "connect")
    def set_sqlite_pragma(dbapi_connection, connection_record):
        cursor = dbapi_connection.cursor()
        cursor.execute("PRAGMA foreign_keys=ON")
        cursor.close()

    Base.metadata.create_all(engine)

    # Create session
    SessionLocal = sessionmaker(bind=engine, autocommit=False, autoflush=False)
    session = SessionLocal()

    yield session

    session.close()


@pytest.fixture
def test_research(test_db):
    """Create a test research record."""
    research = Research(
        query="Test query for strategy",
        status=ResearchStatus.COMPLETED,
        mode=ResearchMode.QUICK,
    )
    test_db.add(research)
    test_db.commit()
    test_db.refresh(research)
    return research


def test_save_research_strategy(test_research, monkeypatch):
    """Test saving a research strategy."""

    # Mock get_db_session to return our test session
    def mock_get_db_session():
        # Create a new session from the same engine
        engine = test_research._sa_instance_state.session.bind
        SessionLocal = sessionmaker(
            bind=engine, autocommit=False, autoflush=False
        )
        return SessionLocal()

    monkeypatch.setattr(
        "src.local_deep_research.web.services.research_service.get_db_session",
        mock_get_db_session,
    )

    # Test saving a strategy
    strategy_name = "source-based"
    save_research_strategy(test_research.id, strategy_name)

    # Verify the strategy was saved
    session = mock_get_db_session()
    strategy = (
        session.query(ResearchStrategy)
        .filter_by(research_id=test_research.id)
        .first()
    )

    assert strategy is not None
    assert strategy.strategy_name == strategy_name
    assert strategy.research_id == test_research.id

    session.close()


def test_update_research_strategy(test_research, monkeypatch):
    """Test updating an existing research strategy."""

    # Mock get_db_session
    def mock_get_db_session():
        engine = test_research._sa_instance_state.session.bind
        SessionLocal = sessionmaker(
            bind=engine, autocommit=False, autoflush=False
        )
        return SessionLocal()

    monkeypatch.setattr(
        "src.local_deep_research.web.services.research_service.get_db_session",
        mock_get_db_session,
    )

    # Save initial strategy
    initial_strategy = "source-based"
    save_research_strategy(test_research.id, initial_strategy)

    # Update to a new strategy
    new_strategy = "iterative"
    save_research_strategy(test_research.id, new_strategy)

    # Verify the strategy was updated
    session = mock_get_db_session()
    strategy = (
        session.query(ResearchStrategy)
        .filter_by(research_id=test_research.id)
        .first()
    )

    assert strategy is not None
    assert strategy.strategy_name == new_strategy

    # Ensure there's only one strategy record for this research
    strategy_count = (
        session.query(ResearchStrategy)
        .filter_by(research_id=test_research.id)
        .count()
    )
    assert strategy_count == 1

    session.close()


def test_get_research_strategy(test_research, monkeypatch):
    """Test retrieving a research strategy."""

    # Mock get_db_session
    def mock_get_db_session():
        engine = test_research._sa_instance_state.session.bind
        SessionLocal = sessionmaker(
            bind=engine, autocommit=False, autoflush=False
        )
        return SessionLocal()

    monkeypatch.setattr(
        "src.local_deep_research.web.services.research_service.get_db_session",
        mock_get_db_session,
    )

    # Test when no strategy exists
    strategy = get_research_strategy(test_research.id)
    assert strategy is None

    # Save a strategy
    strategy_name = "parallel"
    save_research_strategy(test_research.id, strategy_name)

    # Test retrieving the strategy
    strategy = get_research_strategy(test_research.id)
    assert strategy == strategy_name


def test_get_research_strategy_nonexistent(monkeypatch):
    """Test retrieving a strategy for a non-existent research."""

    # Mock get_db_session
    def mock_get_db_session():
        engine = create_engine("sqlite:///:memory:")
        Base.metadata.create_all(engine)
        SessionLocal = sessionmaker(
            bind=engine, autocommit=False, autoflush=False
        )
        return SessionLocal()

    monkeypatch.setattr(
        "src.local_deep_research.web.services.research_service.get_db_session",
        mock_get_db_session,
    )

    # Test with non-existent research ID
    strategy = get_research_strategy(99999)
    assert strategy is None


def test_research_strategy_relationship(test_db, test_research):
    """Test the relationship between Research and ResearchStrategy."""
    # Create a strategy
    strategy = ResearchStrategy(
        research_id=test_research.id, strategy_name="recursive"
    )
    test_db.add(strategy)
    test_db.commit()

    # Verify the relationship works
    # The backref creates a list since it's a one-to-many relationship
    assert len(test_research.strategy) == 1
    assert test_research.strategy[0].strategy_name == "recursive"
    assert strategy.research.query == "Test query for strategy"


def test_research_strategy_unique_constraint(test_db, test_research):
    """Test that only one strategy can exist per research."""
    # Create first strategy
    strategy1 = ResearchStrategy(
        research_id=test_research.id, strategy_name="standard"
    )
    test_db.add(strategy1)
    test_db.commit()

    # Try to create a second strategy for the same research
    strategy2 = ResearchStrategy(
        research_id=test_research.id, strategy_name="rapid"
    )
    test_db.add(strategy2)

    # Should raise an integrity error due to unique constraint
    with pytest.raises(Exception):  # SQLAlchemy will raise IntegrityError
        test_db.commit()
