"""
设备类型管理模块API测试

测试内容:
- 设备类型CRUD测试(15+用例)
- 动态字段配置测试(10+用例)
- 字段验证测试(10+用例)
- 参数验证测试(10+用例)
- 异常处理测试(5+用例)
"""

import pytest
from httpx import AsyncClient
from datetime import datetime


# ==================== 设备类型CRUD测试 ====================

class TestDeviceTypeCRUD:
    """测试设备类型CRUD操作"""

    @pytest.mark.asyncio
    async def test_create_device_type_success(
        self,
        client: AsyncClient,
        admin_headers: dict,
        sample_device_type_data: dict
    ):
        """测试创建设备类型成功"""
        response = await client.post(
            "/api/v1/device-types",
            headers=admin_headers,
            json=sample_device_type_data
        )

        assert response.status_code == 200
        data = response.json()
        assert data["code"] == 200
        assert data["data"]["type_code"] == sample_device_type_data["type_code"]
        assert data["data"]["type_name"] == sample_device_type_data["type_name"]
        assert "id" in data["data"]

    @pytest.mark.asyncio
    async def test_create_device_type_duplicate_code(
        self,
        client: AsyncClient,
        admin_headers: dict,
        test_device_type
    ):
        """测试创建重复代码的设备类型"""
        response = await client.post(
            "/api/v1/device-types",
            headers=admin_headers,
            json={
                "type_code": test_device_type.type_code,
                "type_name": "另一个类型"
            }
        )

        assert response.status_code in [400, 409]

    @pytest.mark.asyncio
    async def test_get_device_type_list(
        self,
        client: AsyncClient,
        admin_headers: dict,
        test_device_type
    ):
        """测试获取设备类型列表"""
        response = await client.get(
            "/api/v1/device-types",
            headers=admin_headers
        )

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

    @pytest.mark.asyncio
    async def test_get_device_type_by_id(
        self,
        client: AsyncClient,
        admin_headers: dict,
        test_device_type
    ):
        """测试根据ID获取设备类型"""
        response = await client.get(
            f"/api/v1/device-types/{test_device_type.id}",
            headers=admin_headers
        )

        assert response.status_code == 200
        data = response.json()
        assert data["code"] == 200
        assert data["data"]["id"] == test_device_type.id
        assert data["data"]["type_code"] == test_device_type.type_code

    @pytest.mark.asyncio
    async def test_get_device_type_by_code(
        self,
        client: AsyncClient,
        admin_headers: dict,
        test_device_type
    ):
        """测试根据代码获取设备类型"""
        response = await client.get(
            f"/api/v1/device-types/code/{test_device_type.type_code}",
            headers=admin_headers
        )

        assert response.status_code == 200
        data = response.json()
        assert data["code"] == 200
        assert data["data"]["type_code"] == test_device_type.type_code

    @pytest.mark.asyncio
    async def test_get_device_type_with_fields(
        self,
        client: AsyncClient,
        admin_headers: dict,
        test_device_type_with_fields
    ):
        """测试获取设备类型及其字段"""
        response = await client.get(
            f"/api/v1/device-types/{test_device_type_with_fields.id}",
            headers=admin_headers
        )

        assert response.status_code == 200
        data = response.json()
        assert data["code"] == 200
        # 验证字段存在
        # assert "fields" in data["data"]

    @pytest.mark.asyncio
    async def test_update_device_type_success(
        self,
        client: AsyncClient,
        admin_headers: dict,
        test_device_type
    ):
        """测试更新设备类型成功"""
        response = await client.put(
            f"/api/v1/device-types/{test_device_type.id}",
            headers=admin_headers,
            json={
                "type_name": "更新后的类型名称",
                "description": "更新后的描述"
            }
        )

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

    @pytest.mark.asyncio
    async def test_update_device_type_status(
        self,
        client: AsyncClient,
        admin_headers: dict,
        test_device_type
    ):
        """测试更新设备类型状态"""
        response = await client.put(
            f"/api/v1/device-types/{test_device_type.id}",
            headers=admin_headers,
            json={"status": "inactive"}
        )

        assert response.status_code == 200

    @pytest.mark.asyncio
    async def test_delete_device_type_success(
        self,
        client: AsyncClient,
        admin_headers: dict,
        db_session,
        test_device_type
    ):
        """测试删除设备类型成功"""
        response = await client.delete(
            f"/api/v1/device-types/{test_device_type.id}",
            headers=admin_headers
        )

        assert response.status_code == 200

        # 验证软删除
        get_response = await client.get(
            f"/api/v1/device-types/{test_device_type.id}",
            headers=admin_headers
        )
        # 应该返回404或显示已删除
        assert get_response.status_code in [404, 200]

    @pytest.mark.asyncio
    async def test_delete_device_type_with_assets_forbidden(
        self,
        client: AsyncClient,
        admin_headers: dict,
        test_device_type
    ):
        """测试删除有关联资产的设备类型(应该失败)"""
        # 假设test_device_type有关联资产
        # 实际测试中需要先创建资产
        response = await client.delete(
            f"/api/v1/device-types/{test_device_type.id}",
            headers=admin_headers
        )

        # 如果有关联资产应该返回400或403
        # assert response.status_code in [400, 403]

    @pytest.mark.asyncio
    async def test_filter_device_type_by_category(
        self,
        client: AsyncClient,
        admin_headers: dict,
        test_device_type
    ):
        """测试按分类筛选设备类型"""
        response = await client.get(
            f"/api/v1/device-types?category={test_device_type.category}",
            headers=admin_headers
        )

        assert response.status_code == 200
        data = response.json()
        assert data["code"] == 200
        # 验证筛选结果
        # for item in data["data"]:
        #     assert item["category"] == test_device_type.category

    @pytest.mark.asyncio
    async def test_filter_device_type_by_status(
        self,
        client: AsyncClient,
        admin_headers: dict,
        test_device_type
    ):
        """测试按状态筛选设备类型"""
        response = await client.get(
            f"/api/v1/device-types?status={test_device_type.status}",
            headers=admin_headers
        )

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

    @pytest.mark.asyncio
    async def test_get_device_type_not_found(
        self,
        client: AsyncClient,
        admin_headers: dict
    ):
        """测试获取不存在的设备类型"""
        response = await client.get(
            "/api/v1/device-types/999999",
            headers=admin_headers
        )

        assert response.status_code == 404

    @pytest.mark.asyncio
    async def test_update_device_type_not_found(
        self,
        client: AsyncClient,
        admin_headers: dict
    ):
        """测试更新不存在的设备类型"""
        response = await client.put(
            "/api/v1/device-types/999999",
            headers=admin_headers,
            json={"type_name": "新名称"}
        )

        assert response.status_code == 404

    @pytest.mark.asyncio
    async def test_create_device_type_unauthorized(
        self,
        client: AsyncClient,
        sample_device_type_data: dict
    ):
        """测试未授权创建设备类型"""
        response = await client.post(
            "/api/v1/device-types",
            json=sample_device_type_data
        )

        assert response.status_code == 401


# ==================== 动态字段配置测试 ====================

class TestDynamicFieldConfig:
    """测试动态字段配置"""

    @pytest.mark.asyncio
    async def test_add_field_to_device_type(
        self,
        client: AsyncClient,
        admin_headers: dict,
        test_device_type: DeviceType,
        sample_field_data: dict
    ):
        """测试为设备类型添加字段"""
        response = await client.post(
            f"/api/v1/device-types/{test_device_type.id}/fields",
            headers=admin_headers,
            json=sample_field_data
        )

        assert response.status_code == 200
        data = response.json()
        assert data["code"] == 200
        assert data["data"]["field_code"] == sample_field_data["field_code"]

    @pytest.mark.asyncio
    async def test_add_required_field(
        self,
        client: AsyncClient,
        admin_headers: dict,
        test_device_type: DeviceType
    ):
        """测试添加必填字段"""
        response = await client.post(
            f"/api/v1/device-types/{test_device_type.id}/fields",
            headers=admin_headers,
            json={
                "field_code": "required_field",
                "field_name": "必填字段",
                "field_type": "text",
                "is_required": True,
                "sort_order": 10
            }
        )

        assert response.status_code == 200
        data = response.json()
        assert data["data"]["is_required"] is True

    @pytest.mark.asyncio
    async def test_add_select_field_with_options(
        self,
        client: AsyncClient,
        admin_headers: dict,
        test_device_type: DeviceType
    ):
        """测试添加下拉选择字段"""
        response = await client.post(
            f"/api/v1/device-types/{test_device_type.id}/fields",
            headers=admin_headers,
            json={
                "field_code": "status",
                "field_name": "状态",
                "field_type": "select",
                "is_required": True,
                "options": [
                    {"label": "启用", "value": "enabled"},
                    {"label": "禁用", "value": "disabled"}
                ],
                "sort_order": 10
            }
        )

        assert response.status_code == 200
        data = response.json()
        assert data["data"]["field_type"] == "select"
        assert len(data["data"]["options"]) == 2

    @pytest.mark.asyncio
    async def test_add_number_field_with_validation(
        self,
        client: AsyncClient,
        admin_headers: dict,
        test_device_type: DeviceType
    ):
        """测试添加数字字段并设置验证规则"""
        response = await client.post(
            f"/api/v1/device-types/{test_device_type.id}/fields",
            headers=admin_headers,
            json={
                "field_code": "price",
                "field_name": "价格",
                "field_type": "number",
                "is_required": False,
                "validation_rules": {
                    "min": 0,
                    "max": 1000000
                },
                "sort_order": 10
            }
        )

        assert response.status_code == 200
        data = response.json()
        assert data["data"]["field_type"] == "number"
        assert "validation_rules" in data["data"]

    @pytest.mark.asyncio
    async def test_get_device_type_fields(
        self,
        client: AsyncClient,
        admin_headers: dict,
        test_device_type_with_fields: DeviceType
    ):
        """测试获取设备类型的字段列表"""
        response = await client.get(
            f"/api/v1/device-types/{test_device_type_with_fields.id}/fields",
            headers=admin_headers
        )

        assert response.status_code == 200
        data = response.json()
        assert data["code"] == 200
        assert len(data["data"]) >= 3  # 至少3个字段

    @pytest.mark.asyncio
    async def test_update_field_success(
        self,
        client: AsyncClient,
        admin_headers: dict,
        db_session,
        test_device_type_with_fields: DeviceType
    ):
        """测试更新字段成功"""
        # 获取第一个字段
        fields_response = await client.get(
            f"/api/v1/device-types/{test_device_type_with_fields.id}/fields",
            headers=admin_headers
        )
        field_id = fields_response.json()["data"][0]["id"]

        response = await client.put(
            f"/api/v1/device-types/{test_device_type_with_fields.id}/fields/{field_id}",
            headers=admin_headers,
            json={
                "field_name": "更新后的字段名",
                "is_required": False
            }
        )

        assert response.status_code == 200

    @pytest.mark.asyncio
    async def test_delete_field_success(
        self,
        client: AsyncClient,
        admin_headers: dict,
        test_device_type_with_fields: DeviceType
    ):
        """测试删除字段成功"""
        fields_response = await client.get(
            f"/api/v1/device-types/{test_device_type_with_fields.id}/fields",
            headers=admin_headers
        )
        field_id = fields_response.json()["data"][0]["id"]

        response = await client.delete(
            f"/api/v1/device-types/{test_device_type_with_fields.id}/fields/{field_id}",
            headers=admin_headers
        )

        assert response.status_code == 200

    @pytest.mark.asyncio
    async def test_add_duplicate_field_code(
        self,
        client: AsyncClient,
        admin_headers: dict,
        test_device_type_with_fields: DeviceType,
        sample_field_data: dict
    ):
        """测试添加重复的字段代码"""
        # 第一次添加
        await client.post(
            f"/api/v1/device-types/{test_device_type_with_fields.id}/fields",
            headers=admin_headers,
            json=sample_field_data
        )

        # 第二次添加相同代码
        response = await client.post(
            f"/api/v1/device-types/{test_device_type_with_fields.id}/fields",
            headers=admin_headers,
            json=sample_field_data
        )

        assert response.status_code in [400, 409]

    @pytest.mark.asyncio
    async def test_fields_sorted_by_order(
        self,
        client: AsyncClient,
        admin_headers: dict,
        test_device_type_with_fields: DeviceType
    ):
        """测试字段按sort_order排序"""
        response = await client.get(
            f"/api/v1/device-types/{test_device_type_with_fields.id}/fields",
            headers=admin_headers
        )

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

        # 验证排序
        for i in range(len(fields) - 1):
            assert fields[i]["sort_order"] <= fields[i + 1]["sort_order"]


# ==================== 字段验证测试 ====================

class TestFieldValidation:
    """测试字段验证"""

    @pytest.mark.asyncio
    @pytest.mark.parametrize("field_code,field_name,expected_status", [
        ("", "字段名", 422),  # 空字段代码
        ("a" * 51, "字段名", 422),  # 字段代码过长
        ("valid_code", "", 422),  # 空字段名称
        ("valid_code", "a" * 101, 422),  # 字段名称过长
    ])
    async def test_field_name_validation(
        self,
        client: AsyncClient,
        admin_headers: dict,
        test_device_type: DeviceType,
        field_code: str,
        field_name: str,
        expected_status: int
    ):
        """测试字段名称验证"""
        response = await client.post(
            f"/api/v1/device-types/{test_device_type.id}/fields",
            headers=admin_headers,
            json={
                "field_code": field_code,
                "field_name": field_name,
                "field_type": "text",
                "sort_order": 1
            }
        )

        assert response.status_code == expected_status

    @pytest.mark.asyncio
    @pytest.mark.parametrize("field_type", [
        "text", "textarea", "number", "date", "select",
        "multiselect", "boolean", "email", "phone", "url"
    ])
    async def test_valid_field_types(
        self,
        client: AsyncClient,
        admin_headers: dict,
        test_device_type: DeviceType,
        field_type: str
    ):
        """测试有效的字段类型"""
        response = await client.post(
            f"/api/v1/device-types/{test_device_type.id}/fields",
            headers=admin_headers,
            json={
                "field_code": f"test_{field_type}",
                "field_name": f"测试{field_type}",
                "field_type": field_type,
                "sort_order": 1
            }
        )

        assert response.status_code == 200

    @pytest.mark.asyncio
    async def test_invalid_field_type(
        self,
        client: AsyncClient,
        admin_headers: dict,
        test_device_type: DeviceType
    ):
        """测试无效的字段类型"""
        response = await client.post(
            f"/api/v1/device-types/{test_device_type.id}/fields",
            headers=admin_headers,
            json={
                "field_code": "test",
                "field_name": "测试",
                "field_type": "invalid_type",
                "sort_order": 1
            }
        )

        assert response.status_code in [400, 422]

    @pytest.mark.asyncio
    async def test_select_field_without_options(
        self,
        client: AsyncClient,
        admin_headers: dict,
        test_device_type: DeviceType
    ):
        """测试select类型字段缺少options"""
        response = await client.post(
            f"/api/v1/device-types/{test_device_type.id}/fields",
            headers=admin_headers,
            json={
                "field_code": "test_select",
                "field_name": "测试选择",
                "field_type": "select",
                "sort_order": 1
            }
        )

        # select类型应该有options
        assert response.status_code in [400, 422]

    @pytest.mark.asyncio
    async def test_validation_rules_json_format(
        self,
        client: AsyncClient,
        admin_headers: dict,
        test_device_type: DeviceType
    ):
        """测试验证规则的JSON格式"""
        response = await client.post(
            f"/api/v1/device-types/{test_device_type.id}/fields",
            headers=admin_headers,
            json={
                "field_code": "test_validation",
                "field_name": "测试验证",
                "field_type": "text",
                "validation_rules": {
                    "min_length": 1,
                    "max_length": 100,
                    "pattern": "^[A-Za-z0-9]+$"
                },
                "sort_order": 1
            }
        )

        assert response.status_code == 200
        data = response.json()
        assert "validation_rules" in data["data"]

    @pytest.mark.asyncio
    async def test_placeholder_and_help_text(
        self,
        client: AsyncClient,
        admin_headers: dict,
        test_device_type: DeviceType
    ):
        """测试placeholder和help_text"""
        response = await client.post(
            f"/api/v1/device-types/{test_device_type.id}/fields",
            headers=admin_headers,
            json={
                "field_code": "test_help",
                "field_name": "测试帮助",
                "field_type": "text",
                "placeholder": "请输入...",
                "help_text": "这是帮助文本",
                "sort_order": 1
            }
        )

        assert response.status_code == 200
        data = response.json()
        assert data["data"]["placeholder"] == "请输入..."
        assert data["data"]["help_text"] == "这是帮助文本"


# ==================== 参数验证测试 ====================

class TestDeviceTypeParameterValidation:
    """测试设备类型参数验证"""

    @pytest.mark.asyncio
    @pytest.mark.parametrize("type_code,expected_status", [
        ("", 422),  # 空代码
        ("AB", 422),  # 太短
        ("a" * 51, 422),  # 太长
        ("VALID_CODE", 200),  # 有效
    ])
    async def test_type_code_validation(
        self,
        client: AsyncClient,
        admin_headers: dict,
        type_code: str,
        expected_status: int
    ):
        """测试类型代码验证"""
        response = await client.post(
            "/api/v1/device-types",
            headers=admin_headers,
            json={
                "type_code": type_code,
                "type_name": "测试类型",
                "category": "IT设备"
            }
        )

        assert response.status_code == expected_status

    @pytest.mark.asyncio
    @pytest.mark.parametrize("type_name,expected_status", [
        ("", 422),  # 空名称
        ("a" * 201, 422),  # 太长
        ("有效名称", 200),  # 有效
    ])
    async def test_type_name_validation(
        self,
        client: AsyncClient,
        admin_headers: dict,
        type_name: str,
        expected_status: int
    ):
        """测试类型名称验证"""
        response = await client.post(
            "/api/v1/device-types",
            headers=admin_headers,
            json={
                "type_code": "TEST_CODE",
                "type_name": type_name
            }
        )

        assert response.status_code == expected_status

    @pytest.mark.asyncio
    async def test_sort_order_validation(
        self,
        client: AsyncClient,
        admin_headers: dict
    ):
        """测试排序验证"""
        response = await client.post(
            "/api/v1/device-types",
            headers=admin_headers,
            json={
                "type_code": "TEST_SORT",
                "type_name": "测试排序",
                "sort_order": -1  # 负数
            }
        )

        # 排序可以是负数,或者应该返回422
        # assert response.status_code in [200, 422]

    @pytest.mark.asyncio
    @pytest.mark.parametrize("status", [
        "active", "inactive", "invalid_status"
    ])
    async def test_status_validation(
        self,
        client: AsyncClient,
        admin_headers: dict,
        test_device_type: DeviceType,
        status: str
    ):
        """测试状态验证"""
        response = await client.put(
            f"/api/v1/device-types/{test_device_type.id}",
            headers=admin_headers,
            json={"status": status}
        )

        # 有效状态应该是200,无效状态应该是422
        if status in ["active", "inactive"]:
            assert response.status_code == 200
        else:
            assert response.status_code in [400, 422]


# ==================== 异常处理测试 ====================

class TestDeviceTypeExceptionHandling:
    """测试异常处理"""

    @pytest.mark.asyncio
    async def test_concurrent_device_type_creation(
        self,
        client: AsyncClient,
        admin_headers: dict
    ):
        """测试并发创建相同代码的设备类型"""
        import asyncio

        data = {
            "type_code": "CONCURRENT_TEST",
            "type_name": "并发测试"
        }

        # 并发创建
        tasks = [
            client.post("/api/v1/device-types", headers=admin_headers, json=data)
            for _ in range(2)
        ]
        responses = await asyncio.gather(*tasks)

        # 应该只有一个成功,另一个失败
        success_count = sum(1 for r in responses if r.status_code == 200)
        assert success_count == 1

    @pytest.mark.asyncio
    async def test_update_non_existent_field(
        self,
        client: AsyncClient,
        admin_headers: dict,
        test_device_type: DeviceType
    ):
        """测试更新不存在的字段"""
        response = await client.put(
            f"/api/v1/device-types/{test_device_type.id}/fields/999999",
            headers=admin_headers,
            json={"field_name": "更新"}
        )

        assert response.status_code == 404

    @pytest.mark.asyncio
    async def test_delete_non_existent_device_type(
        self,
        client: AsyncClient,
        admin_headers: dict
    ):
        """测试删除不存在的设备类型"""
        response = await client.delete(
            "/api/v1/device-types/999999",
            headers=admin_headers
        )

        assert response.status_code == 404

    @pytest.mark.asyncio
    async def test_field_with_invalid_json_validation(
        self,
        client: AsyncClient,
        admin_headers: dict,
        test_device_type: DeviceType
    ):
        """测试字段包含无效的JSON验证规则"""
        response = await client.post(
            f"/api/v1/device-types/{test_device_type.id}/fields",
            headers=admin_headers,
            json={
                "field_code": "test",
                "field_name": "测试",
                "field_type": "text",
                "validation_rules": "invalid json string",  # 应该是对象
                "sort_order": 1
            }
        )

        # 应该返回验证错误
        assert response.status_code in [400, 422]

    @pytest.mark.asyncio
    async def test_field_with_invalid_options_format(
        self,
        client: AsyncClient,
        admin_headers: dict,
        test_device_type: DeviceType
    ):
        """测试select字段包含无效的options格式"""
        response = await client.post(
            f"/api/v1/device-types/{test_device_type.id}/fields",
            headers=admin_headers,
            json={
                "field_code": "test",
                "field_name": "测试",
                "field_type": "select",
                "options": "invalid options",  # 应该是数组
                "sort_order": 1
            }
        )

        assert response.status_code in [400, 422]
