"""Integration regression test for the archive-while-running guard.

`ChatService.archive_session` must refuse to archive a chat session
that has a `ResearchHistory` row in `status='in_progress'` for it.
Without this guard, archiving leaves the running research alive but
tied to a session the user thinks is read-only — an orphaned research
that keeps burning LLM cycles and would race with the archived-session
read-only invariant when it tries to write its final result back.

The chosen behaviour mirrors the existing send-to-archived 409 rule
(see ``insert_message_in_db`` and the send-to-archived guard in
``chat/routes.py``): rather than
auto-terminating the research (which delete_session does — but delete
is destructive by design), archive returns 409 and asks the caller to
stop the research first. This preserves "archive = read-only" without
silent side effects on running computation.
"""

import uuid
from unittest.mock import patch

import pytest

from src.local_deep_research.chat.service import (
    ArchiveBlockedError,
    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, status: str):
    """Insert an active chat session + a research row in *status*."""
    session_id = str(uuid.uuid4())
    research_id = str(uuid.uuid4())
    db.add(
        ChatSession(
            id=session_id,
            title="archive-guard 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 _run_archive(SessionLocal, service, session_id):
    """Drive ``archive_session`` against the test SessionLocal."""
    with 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
        return service.archive_session(session_id)


def test_archive_blocked_when_research_in_progress(
    setup_database_for_all_tests,
):
    """An active session with an in_progress research must NOT archive."""
    SessionLocal = setup_database_for_all_tests
    username = f"alice_{uuid.uuid4().hex[:8]}"
    service = ChatService(username=username)

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

    with pytest.raises(ArchiveBlockedError, match="in_progress"):
        _run_archive(SessionLocal, service, session_id)

    # And the session must still be ACTIVE — the failed archive must
    # not have partially-committed a status change.
    with SessionLocal() as db:
        row = db.query(ChatSession).filter_by(id=session_id).first()
        assert row is not None
        assert row.status == ChatSessionStatus.ACTIVE.value


def test_archive_succeeds_when_research_completed(
    setup_database_for_all_tests,
):
    """Completed research must not block archive — the guard is
    in_progress-only."""
    SessionLocal = setup_database_for_all_tests
    username = f"alice_{uuid.uuid4().hex[:8]}"
    service = ChatService(username=username)

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

    assert _run_archive(SessionLocal, service, session_id) is True

    with SessionLocal() as db:
        row = db.query(ChatSession).filter_by(id=session_id).first()
        assert row.status == ChatSessionStatus.ARCHIVED.value


def test_archive_succeeds_when_no_research_attached(
    setup_database_for_all_tests,
):
    """A session with zero research rows archives cleanly (sanity)."""
    SessionLocal = setup_database_for_all_tests
    username = f"alice_{uuid.uuid4().hex[:8]}"
    service = ChatService(username=username)

    sid = str(uuid.uuid4())
    with SessionLocal() as db:
        db.add(
            ChatSession(
                id=sid,
                title="clean archive",
                status=ChatSessionStatus.ACTIVE.value,
                message_count=0,
            )
        )
        db.commit()

    assert _run_archive(SessionLocal, service, sid) is True

    with SessionLocal() as db:
        assert (
            db.query(ChatSession).filter_by(id=sid).first().status
            == ChatSessionStatus.ARCHIVED.value
        )


def test_archive_missing_session_returns_false(
    setup_database_for_all_tests,
):
    """Unknown id → False return, no exception."""
    SessionLocal = setup_database_for_all_tests
    username = f"alice_{uuid.uuid4().hex[:8]}"
    service = ChatService(username=username)

    assert _run_archive(SessionLocal, service, str(uuid.uuid4())) is False
