import pytest
from fastapi import status
from app.database import get_database
from app.repositories.agent_repository import create_agent
from app.repositories.session_repository import create_session, claim_session_atomically, update_session_status, get_session_by_id
from app.repositories.conversation_repository import get_or_create_conversation
from app.repositories.message_repository import create_message
from app.utils.security import create_access_token

@pytest.mark.anyio
async def test_session_history_unknown_session_returns_404(client):
    res = client.get("/api/v1/session/sess_nonexistent_99999/history?siteId=site_demo_123")
    assert res.status_code == status.HTTP_404_NOT_FOUND
    assert "not found" in res.json()["detail"]

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

    # 1. No siteId and no agent token -> 403
    res1 = client.get(f"/api/v1/session/{sid}/history")
    assert res1.status_code == status.HTTP_403_FORBIDDEN

    # 2. Mismatched siteId and no agent token -> 403
    res2 = client.get(f"/api/v1/session/{sid}/history?siteId=site_wrong_222")
    assert res2.status_code == status.HTTP_403_FORBIDDEN

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

    res = client.get(f"/api/v1/session/{sid}/history?siteId=site_demo_123")
    assert res.status_code == status.HTTP_200_OK
    data = res.json()
    assert data["sessionId"] == sid
    assert data["siteId"] == "site_demo_123"
    assert data["status"] == "RINGING"
    assert data["assignedAgentId"] is None
    assert data["conversationId"] is None
    assert data["messages"] == []

@pytest.mark.anyio
async def test_session_history_restores_messages_chronologically(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", "First visitor message")
    await create_message(db, conv_id, sid, "agent", "First agent response")
    await create_message(db, conv_id, sid, "user", "Second visitor message")

    res = client.get(f"/api/v1/session/{sid}/history?siteId=site_demo_123")
    assert res.status_code == status.HTTP_200_OK
    data = res.json()

    assert data["conversationId"] == conv_id
    assert len(data["messages"]) == 3
    assert data["messages"][0]["role"] == "user"
    assert data["messages"][0]["content"] == "First visitor message"
    assert data["messages"][1]["role"] == "agent"
    assert data["messages"][1]["content"] == "First agent response"
    assert data["messages"][2]["role"] == "user"
    assert data["messages"][2]["content"] == "Second visitor message"

@pytest.mark.anyio
async def test_in_chat_assignment_fields_survive_restoration(client):
    db = get_database()
    agent = await create_agent(db, email="titan@example.com", password="Password123!", name="Titan Agent")
    agent_id = agent["agentId"]

    session = await create_session(db, site_id="site_demo_123")
    sid = session["sessionId"]

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

    conv = await get_or_create_conversation(db, sid, "site_demo_123")
    await create_message(db, conv["conversationId"], sid, "user", "Are you available?")

    # Fetch history via authenticated agent token
    agent_token = create_access_token({"sub": agent_id, "role": "AGENT"})
    headers = {"Authorization": f"Bearer {agent_token}"}

    res = client.get(f"/api/v1/session/{sid}/history", headers=headers)
    assert res.status_code == status.HTTP_200_OK
    data = res.json()

    assert data["status"] == "IN_CHAT"
    assert data["mode"] == "human"
    assert data["assignedAgentId"] == agent_id
    assert data["assignedAt"] is not None
    assert len(data["messages"]) == 1

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

    await claim_session_atomically(db, sid, agent_id="agent_1")
    conv = await get_or_create_conversation(db, sid, "site_demo_123")
    await create_message(db, conv["conversationId"], sid, "user", "Bye!")

    await update_session_status(db, sid, "ENDED")
    await db["conversations"].update_one({"conversationId": conv["conversationId"]}, {"$set": {"endedBy": "agent"}})

    res = client.get(f"/api/v1/session/{sid}/history?siteId=site_demo_123")
    assert res.status_code == status.HTTP_200_OK
    data = res.json()

    assert data["status"] == "ENDED"
    assert data["endedBy"] == "agent"
    assert len(data["messages"]) == 1

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

    session_before = await get_session_by_id(db, sid)

    # Call history API 3 times
    for _ in range(3):
        res = client.get(f"/api/v1/session/{sid}/history?siteId=site_demo_123")
        assert res.status_code == status.HTTP_200_OK

    session_after = await get_session_by_id(db, sid)
    assert session_before["sessionId"] == session_after["sessionId"]
    assert session_before["status"] == session_after["status"]
    assert session_before["mode"] == session_after["mode"]
    assert await db["conversations"].find_one({"sessionId": sid}) is None
