"""
Mock 服务器
支持请求模拟、响应拦截、错误模拟
"""
import json
import re
from dataclasses import dataclass, field
from typing import Optional, List, Dict, Any, Callable, Union
from datetime import datetime
from playwright.async_api import Page, Route, Request
from loguru import logger


@dataclass
class MockResponse:
    """Mock 响应配置"""
    status: int = 200
    headers: Dict[str, str] = field(default_factory=dict)
    body: Any = None
    delay: float = 0  # 延迟毫秒数


@dataclass
class MockConfig:
    """Mock 服务器配置"""
    default_delay: float = 0
    log_requests: bool = True
    persist_logs: bool = False
    log_file: str = "./mock_logs.json"


@dataclass
class RequestLog:
    """请求日志"""
    url: str
    method: str
    timestamp: str
    headers: Dict[str, str]
    post_data: Optional[str] = None
    response_status: Optional[int] = None
    matched_mock: Optional[str] = None


class MockServer:
    """
    Mock 服务器
    拦截并模拟 HTTP 请求响应
    """

    def __init__(
        self,
        page: Page,
        config: Optional[MockConfig] = None,
    ):
        self.page = page
        self.config = config or MockConfig()

        self._mocks: Dict[str, MockResponse] = {}
        self._pattern_mocks: List[tuple] = []  # (pattern, MockResponse)
        self._request_logs: List[RequestLog] = []
        self._is_active = False

    # ==================== 启动/停止 ====================

    async def start(self):
        """启动 Mock 服务器"""
        if self._is_active:
            logger.warning("Mock 服务器已在运行")
            return

        # 设置请求拦截
        await self.page.route("**/*", self._handle_request)
        self._is_active = True
        logger.info("Mock 服务器已启动")

    async def stop(self):
        """停止 Mock 服务器"""
        if not self._is_active:
            return

        await self.page.unroute("**/*", self._handle_request)
        self._is_active = False
        logger.info("Mock 服务器已停止")

        # 保存日志
        if self.config.persist_logs:
            self._save_logs()

    def is_active(self) -> bool:
        """是否处于活动状态"""
        return self._is_active

    # ==================== Mock 配置 ====================

    async def mock_get(
        self,
        url: str,
        response: Union[Dict[str, Any], MockResponse],
    ):
        """
        Mock GET 请求

        Args:
            url: URL 或 URL 模式（支持 * 通配符）
            response: 响应数据或 MockResponse 对象
        """
        mock_response = self._normalize_response(response)
        self._add_mock("GET", url, mock_response)
        logger.debug(f"已添加 GET Mock: {url}")

    async def mock_post(
        self,
        url: str,
        response: Union[Dict[str, Any], MockResponse],
    ):
        """
        Mock POST 请求

        Args:
            url: URL 或 URL 模式
            response: 响应数据或 MockResponse 对象
        """
        mock_response = self._normalize_response(response)
        self._add_mock("POST", url, mock_response)
        logger.debug(f"已添加 POST Mock: {url}")

    async def mock_put(
        self,
        url: str,
        response: Union[Dict[str, Any], MockResponse],
    ):
        """Mock PUT 请求"""
        mock_response = self._normalize_response(response)
        self._add_mock("PUT", url, mock_response)

    async def mock_delete(
        self,
        url: str,
        response: Union[Dict[str, Any], MockResponse],
    ):
        """Mock DELETE 请求"""
        mock_response = self._normalize_response(response)
        self._add_mock("DELETE", url, mock_response)

    async def mock_request(
        self,
        method: str,
        url: str,
        response: Union[Dict[str, Any], MockResponse],
    ):
        """
        Mock 任意方法的请求

        Args:
            method: HTTP 方法
            url: URL 或 URL 模式
            response: 响应数据
        """
        mock_response = self._normalize_response(response)
        self._add_mock(method.upper(), url, mock_response)

    # ==================== 便捷 Mock 方法 ====================

    async def mock_success(
        self,
        url: str,
        data: Any,
    ):
        """
        Mock 成功响应

        Args:
            url: URL 或 URL 模式
            data: 响应数据
        """
        response = MockResponse(
            status=200,
            body={"success": True, "data": data},
        )
        self._add_mock("*", url, response)

    async def mock_error(
        self,
        url: str,
        status: int = 500,
        message: str = "Internal Server Error",
    ):
        """
        Mock 错误响应

        Args:
            url: URL 或 URL 模式
            status: HTTP 状态码
            message: 错误消息
        """
        response = MockResponse(
            status=status,
            body={"success": False, "error": message},
        )
        self._add_mock("*", url, response)

    async def mock_timeout(
        self,
        url: str,
        delay: float = 60000,
    ):
        """
        Mock 超时响应

        Args:
            url: URL 或 URL 模式
            delay: 延迟时间（毫秒）
        """
        response = MockResponse(
            status=408,
            body={"success": False, "error": "Request Timeout"},
            delay=delay,
        )
        self._add_mock("*", url, response)

    async def mock_not_found(self, url: str):
        """Mock 404 响应"""
        await self.mock_error(url, 404, "Not Found")

    async def mock_unauthorized(self, url: str):
        """Mock 401 响应"""
        await self.mock_error(url, 401, "Unauthorized")

    async def mock_forbidden(self, url: str):
        """Mock 403 响应"""
        await self.mock_error(url, 403, "Forbidden")

    async def mock_rate_limited(self, url: str, retry_after: int = 60):
        """Mock 429 限流响应"""
        response = MockResponse(
            status=429,
            headers={"Retry-After": str(retry_after)},
            body={"success": False, "error": "Too Many Requests"},
        )
        self._add_mock("*", url, response)

    # ==================== Mock 管理 ====================

    def clear_mocks(self):
        """清除所有 Mock"""
        self._mocks.clear()
        self._pattern_mocks.clear()
        logger.debug("已清除所有 Mock")

    def remove_mock(self, method: str, url: str):
        """移除特定 Mock"""
        key = f"{method.upper()}:{url}"
        if key in self._mocks:
            del self._mocks[key]
        logger.debug(f"已移除 Mock: {key}")

    def list_mocks(self) -> List[str]:
        """列出所有 Mock"""
        return list(self._mocks.keys()) + [
            f"{m}:{p.pattern}" for m, p, _ in self._pattern_mocks
        ]

    # ==================== 请求日志 ====================

    def get_request_logs(self) -> List[RequestLog]:
        """获取请求日志"""
        return self._request_logs.copy()

    def get_logs_for_url(self, url_pattern: str) -> List[RequestLog]:
        """获取特定 URL 的请求日志"""
        pattern = re.compile(url_pattern.replace("*", ".*"))
        return [
            log for log in self._request_logs
            if pattern.match(log.url)
        ]

    def was_request_made(self, url: str, method: Optional[str] = None) -> bool:
        """
        检查是否发送过请求

        Args:
            url: URL 或 URL 模式
            method: 可选的 HTTP 方法过滤

        Returns:
            bool: 是否发送过请求
        """
        pattern = re.compile(url.replace("*", ".*"))
        for log in self._request_logs:
            if pattern.match(log.url):
                if method is None or log.method == method.upper():
                    return True
        return False

    def get_request_count(self, url: str, method: Optional[str] = None) -> int:
        """获取请求次数"""
        pattern = re.compile(url.replace("*", ".*"))
        count = 0
        for log in self._request_logs:
            if pattern.match(log.url):
                if method is None or log.method == method.upper():
                    count += 1
        return count

    def clear_logs(self):
        """清除请求日志"""
        self._request_logs.clear()

    # ==================== 私有方法 ====================

    async def _handle_request(self, route: Route, request: Request):
        """处理请求拦截"""
        url = request.url
        method = request.method

        # 记录请求
        log_entry = RequestLog(
            url=url,
            method=method,
            timestamp=datetime.now().isoformat(),
            headers=dict(request.headers),
            post_data=request.post_data,
        )

        if self.config.log_requests:
            logger.debug(f"拦截请求: {method} {url}")

        # 查找匹配的 Mock
        mock_response = self._find_mock(method, url)

        if mock_response:
            log_entry.matched_mock = f"{method}:{url}"

            # 延迟
            if mock_response.delay > 0:
                import asyncio
                await asyncio.sleep(mock_response.delay / 1000)

            # 构建响应
            body = self._serialize_body(mock_response.body)
            log_entry.response_status = mock_response.status

            await route.fulfill(
                status=mock_response.status,
                headers=mock_response.headers,
                body=body,
            )
        else:
            # 没有匹配的 Mock，继续请求
            await route.continue_()

        # 保存日志
        self._request_logs.append(log_entry)

    def _add_mock(self, method: str, url: str, response: MockResponse):
        """添加 Mock"""
        # 检查是否是模式匹配
        if "*" in url or "?" in url or "+" in url:
            pattern = re.compile(url.replace("*", ".*").replace("?", ".").replace("+", ".+"))
            self._pattern_mocks.append((method, pattern, response))
        else:
            key = f"{method.upper()}:{url}"
            self._mocks[key] = response

    def _find_mock(self, method: str, url: str) -> Optional[MockResponse]:
        """查找匹配的 Mock"""
        # 精确匹配
        key = f"{method.upper()}:{url}"
        if key in self._mocks:
            return self._mocks[key]

        # 任意方法匹配
        key = f"*:{url}"
        if key in self._mocks:
            return self._mocks[key]

        # 模式匹配
        for mock_method, pattern, response in self._pattern_mocks:
            if mock_method == method.upper() or mock_method == "*":
                if pattern.match(url):
                    return response

        return None

    def _normalize_response(self, response: Union[Dict[str, Any], MockResponse]) -> MockResponse:
        """标准化响应"""
        if isinstance(response, MockResponse):
            return response
        return MockResponse(body=response)

    def _serialize_body(self, body: Any) -> str:
        """序列化响应体"""
        if body is None:
            return ""
        if isinstance(body, str):
            return body
        if isinstance(body, (dict, list)):
            return json.dumps(body, ensure_ascii=False)
        return str(body)

    def _save_logs(self):
        """保存日志到文件"""
        try:
            logs = [
                {
                    "url": log.url,
                    "method": log.method,
                    "timestamp": log.timestamp,
                    "headers": log.headers,
                    "post_data": log.post_data,
                    "response_status": log.response_status,
                    "matched_mock": log.matched_mock,
                }
                for log in self._request_logs
            ]

            from pathlib import Path
            Path(self.config.log_file).parent.mkdir(parents=True, exist_ok=True)

            with open(self.config.log_file, 'w', encoding='utf-8') as f:
                json.dump(logs, f, ensure_ascii=False, indent=2)

            logger.info(f"请求日志已保存: {self.config.log_file}")

        except Exception as e:
            logger.error(f"保存日志失败: {e}")


def create_mock_server(
    page: Page,
    config: Optional[MockConfig] = None,
) -> MockServer:
    """创建 Mock 服务器实例"""
    return MockServer(page, config)
