"""Tests for the extraction failure queue (v16 migration).

Covers:
  - Enqueue a failed extraction
  - Get pending entries
  - Mark processing/done/failed transitions
  - Max retries enforcement
  - Queue stats
"""

import asyncio
import json
import pytest

from cortex.storage.sqlite_adapter import SQLiteAdapter
from cortex.capture.extraction_queue import (
    enqueue_failed_extraction,
    get_pending_entries,
    mark_processing,
    mark_done,
    mark_failed,
    get_queue_stats,
    QueueEntry,
)


@pytest.fixture
async def adapter():
    """In-memory SQLite adapter with all migrations applied."""
    a = SQLiteAdapter(db_path=":memory:")
    await a.initialize()
    yield a
    await a.close()


@pytest.mark.asyncio
async def test_enqueue_and_get_pending(adapter: SQLiteAdapter):
    """Enqueue a failed extraction and retrieve it."""
    conversation = [{"role": "user", "content": "Hello"}]
    entry_id = await enqueue_failed_extraction(
        adapter,
        session_id="test-session-1",
        owner_id="test-owner",
        conversation=conversation,
        error_message="LLM timeout after 30s",
    )
    assert entry_id is not None

    entries = await get_pending_entries(adapter)
    assert len(entries) == 1
    assert entries[0].id == entry_id
    assert entries[0].owner_id == "test-owner"
    assert entries[0].status == "pending"
    assert entries[0].retry_count == 0

    # Verify payload contains the conversation
    payload = json.loads(entries[0].payload)
    assert payload["conversation"] == conversation
    assert payload["session_id"] == "test-session-1"


@pytest.mark.asyncio
async def test_mark_processing_and_done(adapter: SQLiteAdapter):
    """Mark entry as processing then done."""
    entry_id = await enqueue_failed_extraction(
        adapter,
        session_id="s1",
        owner_id="owner1",
        conversation=[{"role": "user", "content": "Test"}],
        error_message="timeout",
    )

    await mark_processing(adapter, entry_id)

    # Should no longer appear in pending
    pending = await get_pending_entries(adapter)
    assert len(pending) == 0

    await mark_done(adapter, entry_id)
    stats = await get_queue_stats(adapter)
    assert stats.get("done", 0) == 1
    assert stats.get("pending", 0) == 0


@pytest.mark.asyncio
async def test_mark_failed_increments_retry(adapter: SQLiteAdapter):
    """Failed retry should increment retry_count."""
    entry_id = await enqueue_failed_extraction(
        adapter,
        session_id="s2",
        owner_id="owner2",
        conversation=[{"role": "user", "content": "Test"}],
        error_message="initial error",
    )

    # First failure
    await mark_failed(adapter, entry_id, "retry 1 failed")
    entries = await get_pending_entries(adapter)
    assert len(entries) == 1
    assert entries[0].retry_count == 1

    # Second failure
    await mark_failed(adapter, entry_id, "retry 2 failed")
    entries = await get_pending_entries(adapter)
    assert len(entries) == 1
    assert entries[0].retry_count == 2

    # Third failure (max_retries=3, so this should set status='failed')
    await mark_failed(adapter, entry_id, "retry 3 failed - giving up")
    entries = await get_pending_entries(adapter)
    assert len(entries) == 0  # No longer pending

    stats = await get_queue_stats(adapter)
    assert stats.get("failed", 0) == 1


@pytest.mark.asyncio
async def test_queue_stats(adapter: SQLiteAdapter):
    """get_queue_stats returns counts by status."""
    # Enqueue multiple
    for i in range(3):
        await enqueue_failed_extraction(
            adapter,
            session_id=f"s{i}",
            owner_id="owner",
            conversation=[{"role": "user", "content": f"msg {i}"}],
            error_message=f"error {i}",
        )

    stats = await get_queue_stats(adapter)
    assert stats.get("pending", 0) == 3

    # Mark one done
    entries = await get_pending_entries(adapter, limit=1)
    await mark_done(adapter, entries[0].id)

    stats = await get_queue_stats(adapter)
    assert stats.get("pending", 0) == 2
    assert stats.get("done", 0) == 1


@pytest.mark.asyncio
async def test_enqueue_with_provenance(adapter: SQLiteAdapter):
    """Provenance fields are stored in the payload."""
    entry_id = await enqueue_failed_extraction(
        adapter,
        session_id="s1",
        owner_id="owner",
        conversation=[{"role": "user", "content": "Hello"}],
        error_message="err",
        source_session_id="source-sess-123",
        source_message_id="msg-456",
        source_channel="telegram",
        source_type="chat",
    )

    entries = await get_pending_entries(adapter)
    payload = json.loads(entries[0].payload)
    assert payload["source_session_id"] == "source-sess-123"
    assert payload["source_message_id"] == "msg-456"
    assert payload["source_channel"] == "telegram"
    assert payload["source_type"] == "chat"
