"""
API测试支持模块
HTTP请求、API测试、Mock服务
"""
import json
import asyncio
from typing import Optional, Dict, Any, List, Union
from playwright.async_api import APIRequestContext, Page
from loguru import logger
from dataclasses import dataclass, field
from datetime import datetime
from enum import Enum
import re


class HttpMethod(Enum):
    """HTTP方法"""
    GET = "GET"
    POST = "POST"
    PUT = "PUT"
    DELETE = "DELETE"
    PATCH = "PATCH"
    HEAD = "HEAD"
    OPTIONS = "OPTIONS"


@dataclass
class APIResponse:
    """API响应"""
    status: int
    status_text: str
    headers: Dict[str, str]
    body: Any
    json: Optional[Dict] = None
    text: Optional[str] = None
    duration_ms: float = 0
    url: str = ""
    request_method: str = ""
    
    @property
    def ok(self) -> bool:
        """是否成功(2xx)"""
        return 200 <= self.status < 300
    
    @property
    def is_json(self) -> bool:
        """是否JSON响应"""
        return "application/json" in self.headers.get("content-type", "")


@dataclass
class APIRequest:
    """API请求"""
    method: HttpMethod
    url: str
    headers: Dict[str, str] = field(default_factory=dict)
    params: Dict[str, str] = field(default_factory=dict)
    body: Any = None
    timeout: float = 30.0


class APIClient:
    """
    API客户端
    发送HTTP请求，支持认证、拦截、重试
    """
    
    def __init__(
        self,
        base_url: str = "",
        default_headers: Optional[Dict[str, str]] = None,
        timeout: float = 30.0,
    ):
        self.base_url = base_url.rstrip("/")
        self.default_headers = default_headers or {}
        self.timeout = timeout
        self._request_context: Optional[APIRequestContext] = None
        self._interceptors: List = []
        self._auth_token: Optional[str] = None
        self._auth_type: str = "Bearer"
    
    async def init(self, playwright):
        """初始化请求上下文"""
        self._request_context = await playwright.request.new_context(
            base_url=self.base_url,
            extra_http_headers=self.default_headers,
        )
        logger.info(f"API客户端已初始化: base_url={self.base_url}")
    
    async def close(self):
        """关闭请求上下文"""
        if self._request_context:
            await self._request_context.dispose()
            self._request_context = None
    
    def set_auth(self, token: str, auth_type: str = "Bearer"):
        """设置认证令牌"""
        self._auth_token = token
        self._auth_type = auth_type
        self.default_headers["Authorization"] = f"{auth_type} {token}"
        logger.info(f"认证已设置: {auth_type}")
    
    def set_header(self, key: str, value: str):
        """设置默认请求头"""
        self.default_headers[key] = value
    
    async def request(
        self,
        method: HttpMethod,
        endpoint: str,
        headers: Optional[Dict[str, str]] = None,
        params: Optional[Dict[str, str]] = None,
        body: Any = None,
        timeout: Optional[float] = None,
    ) -> APIResponse:
        """
        发送请求
        
        Args:
            method: HTTP方法
            endpoint: 端点路径
            headers: 请求头
            params: 查询参数
            body: 请求体
            timeout: 超时时间
            
        Returns:
            APIResponse: 响应对象
        """
        if not self._request_context:
            raise RuntimeError("API客户端未初始化，请先调用 init()")
        
        url = f"{self.base_url}{endpoint}" if self.base_url else endpoint
        request_headers = {**self.default_headers, **(headers or {})}
        timeout = timeout or self.timeout
        
        start_time = datetime.now()
        
        try:
            # 构建请求选项
            options = {
                "headers": request_headers,
                "timeout": int(timeout * 1000),
            }
            
            if params:
                options["params"] = params
            
            if body:
                if isinstance(body, (dict, list)):
                    options["data"] = body
                elif isinstance(body, str):
                    options["data"] = body
                else:
                    options["data"] = body
            
            # 发送请求
            response = await getattr(self._request_context, method.value.lower())(
                url,
                **options
            )
            
            # 解析响应
            response_headers = dict(response.headers)
            content_type = response_headers.get("content-type", "")
            
            response_body = None
            response_json = None
            response_text = None
            
            try:
                if "application/json" in content_type:
                    response_json = await response.json()
                    response_body = response_json
                else:
                    response_text = await response.text()
                    response_body = response_text
            except:
                pass
            
            duration = (datetime.now() - start_time).total_seconds() * 1000
            
            result = APIResponse(
                status=response.status,
                status_text=response.status_text,
                headers=response_headers,
                body=response_body,
                json=response_json,
                text=response_text,
                duration_ms=duration,
                url=url,
                request_method=method.value,
            )
            
            logger.info(
                f"API请求: {method.value} {endpoint} -> {result.status} ({duration:.0f}ms)"
            )
            
            return result
            
        except Exception as e:
            duration = (datetime.now() - start_time).total_seconds() * 1000
            logger.error(f"API请求失败: {method.value} {endpoint}, error={e}")
            raise
    
    async def get(
        self,
        endpoint: str,
        params: Optional[Dict[str, str]] = None,
        **kwargs,
    ) -> APIResponse:
        """GET请求"""
        return await self.request(HttpMethod.GET, endpoint, params=params, **kwargs)
    
    async def post(
        self,
        endpoint: str,
        body: Any = None,
        **kwargs,
    ) -> APIResponse:
        """POST请求"""
        return await self.request(HttpMethod.POST, endpoint, body=body, **kwargs)
    
    async def put(
        self,
        endpoint: str,
        body: Any = None,
        **kwargs,
    ) -> APIResponse:
        """PUT请求"""
        return await self.request(HttpMethod.PUT, endpoint, body=body, **kwargs)
    
    async def patch(
        self,
        endpoint: str,
        body: Any = None,
        **kwargs,
    ) -> APIResponse:
        """PATCH请求"""
        return await self.request(HttpMethod.PATCH, endpoint, body=body, **kwargs)
    
    async def delete(
        self,
        endpoint: str,
        **kwargs,
    ) -> APIResponse:
        """DELETE请求"""
        return await self.request(HttpMethod.DELETE, endpoint, **kwargs)
    
    async def head(
        self,
        endpoint: str,
        **kwargs,
    ) -> APIResponse:
        """HEAD请求"""
        return await self.request(HttpMethod.HEAD, endpoint, **kwargs)


class APITestSuite:
    """
    API测试套件
    提供断言和测试辅助方法
    """
    
    def __init__(self, client: APIClient):
        self.client = client
        self._test_results: List[Dict[str, Any]] = []
    
    async def test_endpoint(
        self,
        name: str,
        method: HttpMethod,
        endpoint: str,
        expected_status: int = 200,
        validators: Optional[List[callable]] = None,
        **kwargs,
    ) -> bool:
        """
        测试API端点
        
        Args:
            name: 测试名称
            method: HTTP方法
            endpoint: 端点路径
            expected_status: 期望状态码
            validators: 响应验证函数列表
            
        Returns:
            bool: 是否通过
        """
        logger.info(f"运行测试: {name}")
        
        try:
            response = await self.client.request(method, endpoint, **kwargs)
            
            # 状态码验证
            if response.status != expected_status:
                self._test_results.append({
                    "name": name,
                    "passed": False,
                    "error": f"状态码不匹配: 期望{expected_status}, 实际{response.status}",
                })
                return False
            
            # 自定义验证
            if validators:
                for validator in validators:
                    try:
                        result = validator(response)
                        if result is False:
                            self._test_results.append({
                                "name": name,
                                "passed": False,
                                "error": "验证函数返回False",
                            })
                            return False
                    except Exception as e:
                        self._test_results.append({
                            "name": name,
                            "passed": False,
                            "error": f"验证失败: {e}",
                        })
                        return False
            
            self._test_results.append({
                "name": name,
                "passed": True,
                "status": response.status,
                "duration_ms": response.duration_ms,
            })
            
            logger.info(f"测试通过: {name}")
            return True
            
        except Exception as e:
            self._test_results.append({
                "name": name,
                "passed": False,
                "error": str(e),
            })
            logger.error(f"测试失败: {name}, error={e}")
            return False
    
    def get_results(self) -> List[Dict[str, Any]]:
        """获取测试结果"""
        return self._test_results.copy()
    
    def get_summary(self) -> Dict[str, Any]:
        """获取测试摘要"""
        total = len(self._test_results)
        passed = sum(1 for r in self._test_results if r["passed"])
        
        return {
            "total": total,
            "passed": passed,
            "failed": total - passed,
            "pass_rate": f"{passed/total*100:.1f}%" if total > 0 else "N/A",
        }
    
    def print_summary(self):
        """打印测试摘要"""
        summary = self.get_summary()
        
        print(f"\n=== API测试摘要 ===")
        print(f"总计: {summary['total']}")
        print(f"通过: {summary['passed']}")
        print(f"失败: {summary['failed']}")
        print(f"通过率: {summary['pass_rate']}")


class ResponseValidator:
    """响应验证器"""
    
    @staticmethod
    def status_code(expected: int):
        """验证状态码"""
        def validator(response: APIResponse):
            assert response.status == expected, f"状态码不匹配: {response.status}"
        return validator
    
    @staticmethod
    def status_ok():
        """验证状态码为2xx"""
        def validator(response: APIResponse):
            assert response.ok, f"请求失败: {response.status}"
        return validator
    
    @staticmethod
    def json_contains(key: str):
        """验证JSON包含指定键"""
        def validator(response: APIResponse):
            assert response.json is not None, "响应不是JSON"
            assert key in response.json, f"JSON不包含键: {key}"
        return validator
    
    @staticmethod
    def json_has_value(key: str, expected: Any):
        """验证JSON值"""
        def validator(response: APIResponse):
            assert response.json is not None, "响应不是JSON"
            assert key in response.json, f"JSON不包含键: {key}"
            assert response.json[key] == expected, f"值不匹配: {response.json[key]}"
        return validator
    
    @staticmethod
    def response_time_under(max_ms: float):
        """验证响应时间"""
        def validator(response: APIResponse):
            assert response.duration_ms < max_ms, f"响应时间过长: {response.duration_ms}ms"
        return validator
    
    @staticmethod
    def header_exists(name: str):
        """验证响应头存在"""
        def validator(response: APIResponse):
            assert name in response.headers, f"响应头不存在: {name}"
        return validator
    
    @staticmethod
    def json_schema(schema: Dict):
        """验证JSON Schema"""
        def validator(response: APIResponse):
            assert response.json is not None, "响应不是JSON"
            # 简单验证，可以集成jsonschema库
            for key, expected_type in schema.items():
                assert key in response.json, f"缺少字段: {key}"
        return validator


class MockServer:
    """
    Mock服务器
    模拟API响应
    """
    
    def __init__(self, page: Page):
        self.page = page
        self._mocks: Dict[str, Dict[str, Any]] = {}
    
    async def mock_get(
        self,
        url_pattern: str,
        response: Any,
        status: int = 200,
        headers: Optional[Dict[str, str]] = None,
    ):
        """Mock GET请求"""
        await self.page.route(
            url_pattern,
            lambda route: route.fulfill(
                status=status,
                content_type="application/json",
                body=json.dumps(response) if isinstance(response, (dict, list)) else str(response),
                headers=headers or {},
            )
        )
        
        self._mocks[url_pattern] = {
            "method": "GET",
            "response": response,
            "status": status,
        }
        
        logger.info(f"Mock已设置: GET {url_pattern}")
    
    async def mock_post(
        self,
        url_pattern: str,
        response: Any,
        status: int = 200,
        headers: Optional[Dict[str, str]] = None,
    ):
        """Mock POST请求"""
        await self.page.route(
            url_pattern,
            lambda route: route.fulfill(
                status=status,
                content_type="application/json",
                body=json.dumps(response) if isinstance(response, (dict, list)) else str(response),
                headers=headers or {},
            )
        )
        
        self._mocks[url_pattern] = {
            "method": "POST",
            "response": response,
            "status": status,
        }
        
        logger.info(f"Mock已设置: POST {url_pattern}")
    
    async def mock_error(
        self,
        url_pattern: str,
        status: int = 500,
        message: str = "Internal Server Error",
    ):
        """Mock错误响应"""
        await self.page.route(
            url_pattern,
            lambda route: route.fulfill(
                status=status,
                body=json.dumps({"error": message}),
            )
        )
        
        logger.info(f"Mock错误已设置: {url_pattern} -> {status}")
    
    async def mock_delay(
        self,
        url_pattern: str,
        response: Any,
        delay_ms: int = 1000,
    ):
        """Mock延迟响应"""
        async def handler(route):
            await asyncio.sleep(delay_ms / 1000)
            await route.fulfill(
                status=200,
                content_type="application/json",
                body=json.dumps(response),
            )
        
        await self.page.route(url_pattern, handler)
        logger.info(f"Mock延迟已设置: {url_pattern} -> {delay_ms}ms")
    
    async def clear(self, url_pattern: Optional[str] = None):
        """清除Mock"""
        if url_pattern:
            await self.page.unroute(url_pattern)
            self._mocks.pop(url_pattern, None)
        else:
            for pattern in list(self._mocks.keys()):
                await self.page.unroute(pattern)
            self._mocks.clear()
        
        logger.info("Mock已清除")
    
    def list_mocks(self) -> Dict[str, Dict[str, Any]]:
        """列出所有Mock"""
        return self._mocks.copy()


class GraphQLClient:
    """
    GraphQL客户端
    发送GraphQL查询和变更
    """
    
    def __init__(
        self,
        endpoint: str,
        client: APIClient,
    ):
        self.endpoint = endpoint
        self.client = client
    
    async def query(
        self,
        query: str,
        variables: Optional[Dict[str, Any]] = None,
        operation_name: Optional[str] = None,
    ) -> APIResponse:
        """
        执行GraphQL查询
        
        Args:
            query: GraphQL查询字符串
            variables: 变量
            operation_name: 操作名
            
        Returns:
            APIResponse: 响应
        """
        body = {"query": query}
        
        if variables:
            body["variables"] = variables
        
        if operation_name:
            body["operationName"] = operation_name
        
        response = await self.client.post(self.endpoint, body=body)
        
        # 检查GraphQL错误
        if response.json and "errors" in response.json:
            logger.warning(f"GraphQL错误: {response.json['errors']}")
        
        return response
    
    async def mutation(
        self,
        mutation: str,
        variables: Optional[Dict[str, Any]] = None,
        operation_name: Optional[str] = None,
    ) -> APIResponse:
        """执行GraphQL变更"""
        return await self.query(mutation, variables, operation_name)
    
    async def introspection(self) -> Optional[Dict]:
        """获取Schema"""
        response = await self.query("""
            query IntrospectionQuery {
                __schema {
                    queryType { name }
                    mutationType { name }
                    types {
                        ...FullType
                    }
                }
            }
            fragment FullType on __Type {
                kind
                name
                description
                fields(includeDeprecated: true) {
                    name
                    description
                    args {
                        ...InputValue
                    }
                    type {
                        ...TypeRef
                    }
                }
                inputFields {
                    ...InputValue
                }
            }
            fragment InputValue on __InputValue {
                name
                description
                type {
                    ...TypeRef
                }
            }
            fragment TypeRef on __Type {
                kind
                name
            }
        """)
        
        if response.json:
            return response.json.get("data", {}).get("__schema")
        return None