"""Integration regression test: deleting a session terminates its research.

Previously, `ChatService.delete_session` issued a hard ``db.delete``
on the chat_sessions row. Any in-progress research linked to that session
had its ``chat_session_id`` set to NULL by the FK (ON DELETE SET NULL),
but the worker thread itself was left running — burning LLM cycles on a
conversation the user had already discarded.

The fix collapses the termination-flag set and the DELETE into one
transaction (chat/service.py:delete_session) so the worker is signalled
to terminate before the session row is removed.

This test exercises the full integration path:

1. Insert a chat session via ChatService.
2. Manually insert a ResearchHistory row tied to that session with
   status="in_progress" (simulating a research that was just spawned).
3. Call ChatService.delete_session(...).
4. Assert that ``set_termination_flag`` was called with the in-flight
   research id BEFORE the row was removed.

The test deliberately patches ``set_termination_flag`` so we can assert
on call args without needing a real worker thread.
"""

import uuid
from unittest.mock import patch

from src.local_deep_research.chat.service import ChatService
from src.local_deep_research.constants import ResearchStatus
from src.local_deep_research.database.models import (
    ChatSession,
    ChatSessionStatus,
    ResearchHistory,
)


def _seed_session_and_research(db, username: str, status: str):
    """Create a chat session + a research row linked to it in *status*."""
    session_id = str(uuid.uuid4())
    research_id = str(uuid.uuid4())
    db.add(
        ChatSession(
            id=session_id,
            title="terminate-on-delete test",
            status=ChatSessionStatus.ACTIVE.value,
            message_count=0,
        )
    )
    db.add(
        ResearchHistory(
            id=research_id,
            query="probe",
            mode="quick",
            status=status,
            created_at="2026-05-14T00:00:00",
            chat_session_id=session_id,
        )
    )
    db.commit()
    return session_id, research_id


def test_delete_session_flags_in_progress_research_for_termination(
    setup_database_for_all_tests,
):
    """The single happy path: deleting a session with an in-flight
    research must call ``set_termination_flag`` for that research id."""
    SessionLocal = setup_database_for_all_tests
    username = f"alice_{uuid.uuid4().hex[:8]}"

    with SessionLocal() as db:
        session_id, research_id = _seed_session_and_research(
            db, username, status=ResearchStatus.IN_PROGRESS.value
        )

    service = ChatService(username=username)
    flagged = []

    def _capture(rid):
        flagged.append(rid)

    # The service binds `set_termination_flag` at module top-level
    # (``from ..web.routes.globals import set_termination_flag``), so
    # patch at the binding location, not the source module — patching
    # ``web.routes.globals`` would not intercept the call inside
    # delete_session once the import has captured the reference.
    with (
        patch(
            "src.local_deep_research.chat.service.set_termination_flag",
            side_effect=_capture,
        ),
        patch(
            "src.local_deep_research.chat.service.get_user_db_session"
        ) as ctx,
    ):
        ctx.return_value.__enter__.return_value = SessionLocal()
        ctx.return_value.__exit__.return_value = False
        assert service.delete_session(session_id) is True

    assert flagged == [research_id], (
        f"Expected exactly one termination-flag for {research_id}, got {flagged}"
    )

    # And the session row itself must be gone.
    with SessionLocal() as db:
        assert db.query(ChatSession).filter_by(id=session_id).first() is None


def test_delete_session_skips_completed_research(setup_database_for_all_tests):
    """Completed research should NOT receive a termination flag — it has
    nothing to abort and flagging it would be misleading log noise."""
    SessionLocal = setup_database_for_all_tests
    username = f"alice_{uuid.uuid4().hex[:8]}"

    with SessionLocal() as db:
        session_id, _research_id = _seed_session_and_research(
            db, username, status=ResearchStatus.COMPLETED.value
        )

    service = ChatService(username=username)
    flagged = []

    with (
        patch(
            "src.local_deep_research.chat.service.set_termination_flag",
            side_effect=lambda rid: flagged.append(rid),
        ),
        patch(
            "src.local_deep_research.chat.service.get_user_db_session"
        ) as ctx,
    ):
        ctx.return_value.__enter__.return_value = SessionLocal()
        ctx.return_value.__exit__.return_value = False
        assert service.delete_session(session_id) is True

    assert flagged == [], (
        f"Completed research must not be flagged for termination, got {flagged}"
    )


def test_delete_session_returns_false_for_missing_session(
    setup_database_for_all_tests,
):
    """No-op + False return when the session doesn't exist — no
    termination flag set, no exception."""
    SessionLocal = setup_database_for_all_tests
    username = f"alice_{uuid.uuid4().hex[:8]}"
    service = ChatService(username=username)

    flagged = []
    with (
        patch(
            "src.local_deep_research.chat.service.set_termination_flag",
            side_effect=lambda rid: flagged.append(rid),
        ),
        patch(
            "src.local_deep_research.chat.service.get_user_db_session"
        ) as ctx,
    ):
        ctx.return_value.__enter__.return_value = SessionLocal()
        ctx.return_value.__exit__.return_value = False
        result = service.delete_session(str(uuid.uuid4()))

    assert result is False
    assert flagged == []
