import pytest
from fastapi import status
from app.database import get_database
from app.repositories.session_repository import create_session, claim_session_atomically, get_session_by_id
from app.repositories.conversation_repository import get_or_create_conversation
from app.repositories.message_repository import create_message
from app.services.presence import active_sessions, add_active_session, get_active_sessions, remove_active_session
from app.repositories.agent_repository import create_agent
from app.utils.security import create_access_token
from unittest.mock import AsyncMock

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

    # Register in active_sessions memory
    mock_ws = AsyncMock()
    add_active_session(sid, "site_demo_123", mock_ws)
    assert sid in active_sessions

    # Visitor ends chat
    res = client.post("/api/v1/handoff/end", json={"sessionId": sid, "endedBy": "visitor"})
    assert res.status_code == status.HTTP_200_OK
    assert res.json()["status"] == "ENDED"
    assert res.json()["endedBy"] == "visitor"

    # 1. Session stays in active_sessions (socket still open) but with ENDED status
    assert sid in active_sessions
    assert active_sessions[sid]["status"] == "ENDED"

    # 2. MongoDB session document preserved with status = ENDED
    db_session = await get_session_by_id(db, sid)
    assert db_session["status"] == "ENDED"

    # 3. GET /admin/active-sessions does NOT return the ENDED session
    #    (get_active_sessions filters out ENDED/ABANDONED)
    res_admin = client.get("/api/v1/admin/active-sessions")
    assert res_admin.status_code == status.HTTP_200_OK
    sids_returned = [s["sessionId"] for s in res_admin.json()]
    assert sid not in sids_returned

    # 4. Cleanup: remove from active_sessions to mirror real socket disconnect
    remove_active_session(sid)

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

    claimed = await claim_session_atomically(db, sid, agent_id="agent_clean_1")
    assert claimed is not None

    mock_ws = AsyncMock()
    add_active_session(sid, "site_demo_123", mock_ws, status_val="IN_CHAT")
    assert sid in active_sessions

    # Agent ends chat
    res = client.post("/api/v1/handoff/end", json={"sessionId": sid, "endedBy": "agent"})
    assert res.status_code == status.HTTP_200_OK
    assert res.json()["status"] == "ENDED"
    assert res.json()["endedBy"] == "agent"

    # 1. Session stays in active_sessions (socket still open) but with ENDED status
    assert sid in active_sessions
    assert active_sessions[sid]["status"] == "ENDED"

    # 2. Preserved in MongoDB
    db_session = await get_session_by_id(db, sid)
    assert db_session["status"] == "ENDED"
    assert db_session["assignedAgentId"] == "agent_clean_1"

    # 3. GET /admin/active-sessions does not list it
    #    (get_active_sessions filters out ENDED/ABANDONED)
    res_admin = client.get("/api/v1/admin/active-sessions")
    assert sid not in [s["sessionId"] for s in res_admin.json()]

    # 4. Cleanup: remove from active_sessions to mirror real socket disconnect
    remove_active_session(sid)

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

    conv = await get_or_create_conversation(db, sid, "site_demo_123")
    conv_id = conv["conversationId"]
    await create_message(db, conv_id, sid, "user", "Message before ending")

    # End chat
    client.post("/api/v1/handoff/end", json={"sessionId": sid, "endedBy": "agent"})

    # History API still returns ENDED session & messages intact
    res_hist = client.get(f"/api/v1/session/{sid}/history?siteId=site_demo_123")
    assert res_hist.status_code == status.HTTP_200_OK
    data = res_hist.json()
    assert data["sessionId"] == sid
    assert data["status"] == "ENDED"
    assert data["endedBy"] == "agent"
    assert len(data["messages"]) == 1
    assert data["messages"][0]["content"] == "Message before ending"
