# =============================================================================
# 企微IT智能服务台 — 坐席认证与管理测试
# =============================================================================
# 测试覆盖：
#   1. 坐席登录（新坐席注册 + 已有坐席重新登录）
#   2. Token 存 Redis（验证 TTL 和格式）
#   3. 获取当前坐席信息（有效 Token / 无效 Token / 过期 Token）
#   4. 更新坐席状态（online/busy/offline）
#   5. 无效状态值校验
#   6. 获取坐席列表
#   7. 缺少 Authorization 头返回未授权
# =============================================================================

import pytest
import pytest_asyncio
from unittest.mock import patch

from app.models.agent import Agent
from tests.conftest import create_test_agent, MockRedis


class TestAgentLogin:
    """测试坐席登录。"""

    @pytest.mark.asyncio
    async def test_login_new_agent(self, client, db_session, mock_redis):
        """验证新坐席首次登录自动注册。"""
        response = await client.post(
            "/agents/login",
            json={"user_id": "new_agent_001", "name": "新坐席"},
        )

        assert response.status_code == 200
        data = response.json()
        assert data["code"] == 0
        assert data["data"]["user_id"] == "new_agent_001"
        assert data["data"]["name"] == "新坐席"
        assert data["data"]["status"] == "online"
        assert "token" in data["data"]

    @pytest.mark.asyncio
    async def test_login_existing_agent(self, client, db_session, mock_redis):
        """验证已有坐席重新登录更新信息。"""
        # 先创建坐席
        agent = create_test_agent(user_id="exist_agent", name="旧名字")
        db_session.add(agent)
        await db_session.flush()

        response = await client.post(
            "/agents/login",
            json={"user_id": "exist_agent", "name": "新名字"},
        )

        assert response.status_code == 200
        data = response.json()
        assert data["data"]["name"] == "新名字"
        assert data["data"]["status"] == "online"

    @pytest.mark.asyncio
    async def test_login_returns_token(self, client, db_session, mock_redis):
        """验证登录返回 Token 存入 Redis。"""
        response = await client.post(
            "/agents/login",
            json={"user_id": "token_test_agent", "name": "Token测试"},
        )

        data = response.json()
        assert "token" in data["data"]

        # 验证 Redis 中存储了 token（key 格式：agent:token:{token}）
        token = data["data"]["token"]
        redis_key = f"agent:token:{token}"
        stored_value = await mock_redis.get(redis_key)
        assert stored_value is not None


class TestAgentAuthentication:
    """测试坐席认证。"""

    @pytest.mark.asyncio
    async def test_get_agent_me_with_valid_token(self, client, db_session, mock_redis):
        """验证有效 Token 获取坐席信息。"""
        # 先登录获取 token
        login_resp = await client.post(
            "/agents/login",
            json={"user_id": "me_test_agent", "name": "我测试"},
        )
        token = login_resp.json()["data"]["token"]

        # 用 token 获取坐席信息
        response = await client.get(
            "/agents/me",
            headers={"Authorization": f"Bearer {token}"},
        )

        assert response.status_code == 200
        data = response.json()
        assert data["code"] == 0
        assert data["data"]["user_id"] == "me_test_agent"

    @pytest.mark.asyncio
    async def test_get_agent_me_without_token(self, client, db_session, mock_redis):
        """验证缺少 Token 返回未授权。"""
        response = await client.get("/agents/me")

        data = response.json()
        assert data["code"] == 1002  # ERR_UNAUTHORIZED

    @pytest.mark.asyncio
    async def test_get_agent_me_with_invalid_token(self, client, db_session, mock_redis):
        """验证无效 Token 返回未授权。"""
        response = await client.get(
            "/agents/me",
            headers={"Authorization": "Bearer invalid_token_12345"},
        )

        data = response.json()
        assert data["code"] == 1002


class TestAgentStatusUpdate:
    """测试坐席状态更新。"""

    @pytest.mark.asyncio
    async def test_update_status_to_busy(self, client, db_session, mock_redis):
        """验证更新坐席状态为忙碌。"""
        # 先登录
        login_resp = await client.post(
            "/agents/login",
            json={"user_id": "status_test_agent", "name": "状态测试"},
        )
        token = login_resp.json()["data"]["token"]

        # 更新状态
        response = await client.put(
            "/agents/me/status",
            json={"status": "busy"},
            headers={"Authorization": f"Bearer {token}"},
        )

        assert response.status_code == 200
        data = response.json()
        assert data["data"]["status"] == "busy"

    @pytest.mark.asyncio
    async def test_update_status_to_offline(self, client, db_session, mock_redis):
        """验证更新坐席状态为离线。"""
        login_resp = await client.post(
            "/agents/login",
            json={"user_id": "offline_test_agent", "name": "离线测试"},
        )
        token = login_resp.json()["data"]["token"]

        response = await client.put(
            "/agents/me/status",
            json={"status": "offline"},
            headers={"Authorization": f"Bearer {token}"},
        )

        data = response.json()
        assert data["data"]["status"] == "offline"

    @pytest.mark.asyncio
    async def test_update_status_invalid_value(self, client, db_session, mock_redis):
        """验证无效状态值返回校验错误。"""
        login_resp = await client.post(
            "/agents/login",
            json={"user_id": "invalid_status_agent", "name": "无效状态测试"},
        )
        token = login_resp.json()["data"]["token"]

        response = await client.put(
            "/agents/me/status",
            json={"status": "invalid_status"},
            headers={"Authorization": f"Bearer {token}"},
        )

        # Pydantic 校验应返回 422
        assert response.status_code == 422


class TestAgentList:
    """测试坐席列表。"""

    @pytest.mark.asyncio
    async def test_list_agents(self, client, db_session, mock_redis):
        """验证获取坐席列表。"""
        agent1 = create_test_agent(user_id="list_agent_1", name="坐席一")
        agent2 = create_test_agent(user_id="list_agent_2", name="坐席二")
        db_session.add_all([agent1, agent2])
        await db_session.flush()

        response = await client.get("/agents")

        assert response.status_code == 200
        data = response.json()
        assert data["code"] == 0
        assert len(data["data"]["items"]) >= 2

    @pytest.mark.asyncio
    async def test_list_agents_by_status(self, client, db_session, mock_redis):
        """验证按状态过滤坐席列表。"""
        online_agent = create_test_agent(user_id="online_filter_agent", name="在线坐席", status="online")
        offline_agent = create_test_agent(user_id="offline_filter_agent", name="离线坐席", status="offline")
        db_session.add_all([online_agent, offline_agent])
        await db_session.flush()

        response = await client.get("/agents?status=online")

        data = response.json()
        assert data["code"] == 0
        for item in data["data"]["items"]:
            assert item["status"] == "online"
