import pytest
from app.database import get_database
from app.repositories.session_repository import (
    create_session,
    get_session_by_id,
    update_session_status,
    claim_session_atomically,
    requeue_session
)

@pytest.mark.anyio
async def test_claim_session_atomically_success():
    db = get_database()
    session = await create_session(db, site_id="site_demo_123")
    session_id = session["sessionId"]
    assert session["status"] == "RINGING"
    assert session.get("assignedAgentId") is None

    # First agent claims RINGING session
    claimed = await claim_session_atomically(db, session_id, agent_id="agent_alpha")
    assert claimed is not None
    assert claimed["status"] == "IN_CHAT"
    assert claimed["mode"] == "human"
    assert claimed["assignedAgentId"] == "agent_alpha"
    assert claimed.get("assignedAt") is not None

    # Verify persistence in DB
    fetched = await get_session_by_id(db, session_id)
    assert fetched["assignedAgentId"] == "agent_alpha"
    assert fetched["status"] == "IN_CHAT"

@pytest.mark.anyio
async def test_claim_session_race_condition_prevention():
    db = get_database()
    session = await create_session(db, site_id="site_demo_123")
    session_id = session["sessionId"]

    # Agent 1 claims session
    claim1 = await claim_session_atomically(db, session_id, agent_id="agent_1")
    assert claim1 is not None
    assert claim1["assignedAgentId"] == "agent_1"

    # Agent 2 attempts to claim the same session (now in IN_CHAT state)
    claim2 = await claim_session_atomically(db, session_id, agent_id="agent_2")
    assert claim2 is None

    # Verify assignedAgentId remains agent_1 in DB
    fetched = await get_session_by_id(db, session_id)
    assert fetched["assignedAgentId"] == "agent_1"
    assert fetched["status"] == "IN_CHAT"

@pytest.mark.anyio
async def test_cannot_claim_ended_session():
    db = get_database()
    session = await create_session(db, site_id="site_demo_123")
    session_id = session["sessionId"]

    # Transition to ENDED
    await update_session_status(db, session_id, "ENDED")

    # Claim attempt on ENDED session
    claimed = await claim_session_atomically(db, session_id, agent_id="agent_1")
    assert claimed is None

    # Verify status remains ENDED
    fetched = await get_session_by_id(db, session_id)
    assert fetched["status"] == "ENDED"

@pytest.mark.anyio
async def test_requeue_session_success():
    db = get_database()
    session = await create_session(db, site_id="site_demo_123")
    session_id = session["sessionId"]

    # Claim session first
    await claim_session_atomically(db, session_id, agent_id="agent_alpha")

    # Requeue IN_CHAT session back to RINGING
    requeued = await requeue_session(db, session_id)
    assert requeued is not None
    assert requeued["status"] == "RINGING"
    assert requeued["mode"] == "rule"
    assert requeued.get("assignedAgentId") is None
    assert requeued.get("assignedAt") is None

    # Verify DB persistence
    fetched = await get_session_by_id(db, session_id)
    assert fetched["status"] == "RINGING"
    assert fetched.get("assignedAgentId") is None

@pytest.mark.anyio
async def test_cannot_requeue_invalid_or_ended_session():
    db = get_database()
    # Non-existent session
    bad_requeue = await requeue_session(db, session_id="non_existent_sess")
    assert bad_requeue is None

    # RINGING session (not in IN_CHAT state)
    session = await create_session(db, site_id="site_demo_123")
    session_id = session["sessionId"]
    ringing_requeue = await requeue_session(db, session_id)
    assert ringing_requeue is None

    # ENDED session
    await claim_session_atomically(db, session_id, agent_id="agent_1")
    await update_session_status(db, session_id, "ENDED")
    ended_requeue = await requeue_session(db, session_id)
    assert ended_requeue is None
