"""
网络拦截和监控模块
"""
import json
from typing import Optional, Callable, Dict, Any, List
from dataclasses import dataclass, field
from playwright.async_api import Page, Request, Response, Route
from loguru import logger
from datetime import datetime
import re


@dataclass
class RequestLog:
    """请求日志"""
    url: str
    method: str
    status: Optional[int] = None
    request_time: datetime = field(default_factory=datetime.now)
    response_time: Optional[datetime] = None
    request_headers: Dict[str, str] = field(default_factory=dict)
    response_headers: Dict[str, str] = field(default_factory=dict)
    request_body: Optional[str] = None
    response_body: Optional[str] = None
    duration_ms: Optional[float] = None
    error: Optional[str] = None


class NetworkMonitor:
    """
    网络监控器
    记录所有网络请求和响应
    """
    
    def __init__(self, page: Page):
        self.page = page
        self.requests: List[RequestLog] = []
        self._request_map: Dict[str, RequestLog] = {}
    
    def start(self):
        """开始监控"""
        self.page.on("request", self._on_request)
        self.page.on("response", self._on_response)
        self.page.on("requestfailed", self._on_request_failed)
        logger.info("网络监控已启动")
    
    def stop(self):
        """停止监控"""
        self.page.remove_listener("request", self._on_request)
        self.page.remove_listener("response", self._on_response)
        self.page.remove_listener("requestfailed", self._on_request_failed)
        logger.info("网络监控已停止")
    
    def _on_request(self, request: Request):
        """请求事件处理"""
        log = RequestLog(
            url=request.url,
            method=request.method,
            request_headers=dict(request.headers),
            request_body=request.post_data,
        )
        self._request_map[request.url] = log
        self.requests.append(log)
    
    async def _on_response(self, response: Response):
        """响应事件处理"""
        request = response.request
        if request.url in self._request_map:
            log = self._request_map[request.url]
            log.status = response.status
            log.response_time = datetime.now()
            log.response_headers = dict(response.headers)
            log.duration_ms = (log.response_time - log.request_time).total_seconds() * 1000

            # 尝试获取响应体
            try:
                if response.status < 400:
                    body = await response.text()
                    log.response_body = body[:10000]  # 限制大小
            except:
                pass
    
    def _on_request_failed(self, request: Request):
        """请求失败事件处理"""
        if request.url in self._request_map:
            log = self._request_map[request.url]
            log.error = request.failure
            log.response_time = datetime.now()
            log.duration_ms = (log.response_time - log.request_time).total_seconds() * 1000
    
    def get_requests(self, url_pattern: Optional[str] = None) -> List[RequestLog]:
        """
        获取请求日志
        
        Args:
            url_pattern: URL正则模式
            
        Returns:
            List[RequestLog]: 请求日志列表
        """
        if url_pattern:
            pattern = re.compile(url_pattern)
            return [r for r in self.requests if pattern.search(r.url)]
        return self.requests
    
    def get_api_calls(self) -> List[RequestLog]:
        """获取API调用（JSON请求）"""
        return [
            r for r in self.requests
            if "application/json" in r.request_headers.get("accept", "")
            or "application/json" in r.response_headers.get("content-type", "")
            or "/api/" in r.url
        ]
    
    def get_failed_requests(self) -> List[RequestLog]:
        """获取失败的请求"""
        return [r for r in self.requests if r.error or (r.status and r.status >= 400)]
    
    def get_slow_requests(self, threshold_ms: float = 1000) -> List[RequestLog]:
        """
        获取慢请求
        
        Args:
            threshold_ms: 阈值（毫秒）
        """
        return [
            r for r in self.requests
            if r.duration_ms and r.duration_ms > threshold_ms
        ]
    
    def clear(self):
        """清空日志"""
        self.requests.clear()
        self._request_map.clear()
    
    def summary(self) -> Dict[str, Any]:
        """生成摘要"""
        total = len(self.requests)
        failed = len(self.get_failed_requests())
        api_calls = len(self.get_api_calls())
        slow = len(self.get_slow_requests())
        
        avg_duration = 0
        if self.requests:
            durations = [r.duration_ms for r in self.requests if r.duration_ms]
            if durations:
                avg_duration = sum(durations) / len(durations)
        
        return {
            "total_requests": total,
            "failed_requests": failed,
            "api_calls": api_calls,
            "slow_requests": slow,
            "avg_duration_ms": round(avg_duration, 2),
        }
    
    def print_summary(self):
        """打印摘要"""
        s = self.summary()
        print(f"\n=== 网络监控摘要 ===")
        print(f"总请求数: {s['total_requests']}")
        print(f"失败请求: {s['failed_requests']}")
        print(f"API调用: {s['api_calls']}")
        print(f"慢请求(>1s): {s['slow_requests']}")
        print(f"平均耗时: {s['avg_duration_ms']}ms")


class NetworkInterceptor:
    """
    网络拦截器
    拦截、修改、阻止网络请求
    """
    
    def __init__(self, page: Page):
        self.page = page
        self._routes: List[Dict[str, Any]] = []
    
    async def block(self, url_pattern: str):
        """
        阻止匹配的请求
        
        Args:
            url_pattern: URL正则模式
        """
        async def handler(route: Route):
            logger.debug(f"阻止请求: {route.request.url}")
            await route.abort()
        
        await self.page.route(url_pattern, handler)
        self._routes.append({"pattern": url_pattern, "action": "block"})
        logger.info(f"已设置拦截规则(阻止): {url_pattern}")
    
    async def mock(
        self, 
        url_pattern: str, 
        response_body: Any,
        status: int = 200,
        headers: Optional[Dict[str, str]] = None,
    ):
        """
        模拟响应
        
        Args:
            url_pattern: URL正则模式
            response_body: 响应体
            status: 状态码
            headers: 响应头
        """
        async def handler(route: Route):
            body = json.dumps(response_body) if isinstance(response_body, (dict, list)) else str(response_body)
            default_headers = {"content-type": "application/json"}
            default_headers.update(headers or {})
            
            logger.debug(f"模拟响应: {route.request.url}")
            await route.fulfill(status=status, body=body, headers=default_headers)
        
        await self.page.route(url_pattern, handler)
        self._routes.append({"pattern": url_pattern, "action": "mock"})
        logger.info(f"已设置拦截规则(模拟): {url_pattern}")
    
    async def modify(
        self,
        url_pattern: str,
        request_modifier: Optional[Callable[[Request], Dict[str, Any]]] = None,
        response_modifier: Optional[Callable[[Response, str], str]] = None,
    ):
        """
        修改请求/响应
        
        Args:
            url_pattern: URL正则模式
            request_modifier: 请求修改函数
            response_modifier: 响应修改函数
        """
        async def handler(route: Route):
            request = route.request
            
            if request_modifier:
                modifications = request_modifier(request)
                await route.continue_(**modifications)
            elif response_modifier:
                response = await route.fetch()
                body = await response.text()
                modified_body = response_modifier(response, body)
                await route.fulfill(response=response, body=modified_body)
            else:
                await route.continue_()
        
        await self.page.route(url_pattern, handler)
        self._routes.append({"pattern": url_pattern, "action": "modify"})
        logger.info(f"已设置拦截规则(修改): {url_pattern}")
    
    async def redirect(self, url_pattern: str, target_url: str):
        """
        重定向请求
        
        Args:
            url_pattern: URL正则模式
            target_url: 目标URL
        """
        async def handler(route: Route):
            logger.debug(f"重定向: {route.request.url} -> {target_url}")
            await route.redirect(target_url)
        
        await self.page.route(url_pattern, handler)
        self._routes.append({"pattern": url_pattern, "action": "redirect", "target": target_url})
        logger.info(f"已设置拦截规则(重定向): {url_pattern} -> {target_url}")
    
    async def clear_all(self):
        """清除所有拦截规则"""
        for route_info in self._routes:
            await self.page.unroute(route_info["pattern"])
        self._routes.clear()
        logger.info("已清除所有拦截规则")
    
    def list_routes(self) -> List[Dict[str, Any]]:
        """列出所有拦截规则"""
        return self._routes.copy()


class BlockResources:
    """
    资源阻止器
    阻止特定类型的资源加载，加速页面加载
    """
    
    RESOURCE_TYPES = {
        "image": ["image", "images"],
        "stylesheet": ["stylesheet", "css"],
        "font": ["font"],
        "media": ["media"],
        "script": ["script", "javascript"],
        "websocket": ["websocket"],
        "other": ["other"],
    }
    
    def __init__(self, page: Page):
        self.page = page
        self._blocked_types: List[str] = []
    
    async def block_images(self):
        """阻止图片"""
        await self._block_resource_type("image")
    
    async def block_styles(self):
        """阻止样式表"""
        await self._block_resource_type("stylesheet")
    
    async def block_fonts(self):
        """阻止字体"""
        await self._block_resource_type("font")
    
    async def block_media(self):
        """阻止媒体"""
        await self._block_resource_type("media")
    
    async def block_scripts(self):
        """阻止脚本"""
        await self._block_resource_type("script")
    
    async def block_all_except_html(self):
        """只加载HTML，阻止其他所有资源"""
        async def handler(route: Route):
            if route.request.resource_type == "document":
                await route.continue_()
            else:
                await route.abort()
        
        await self.page.route("**/*", handler)
        logger.info("已阻止除HTML外的所有资源")
    
    async def _block_resource_type(self, resource_type: str):
        """阻止特定资源类型"""
        async def handler(route: Route):
            if route.request.resource_type == resource_type:
                await route.abort()
            else:
                await route.continue_()

        await self.page.route("**/*", handler)
        self._blocked_types.append(resource_type)
        logger.info(f"已阻止资源类型: {resource_type}")


class HARRecorder:
    """
    HAR 文件录制器

    录制网络请求并导出为 HAR 格式。

    Example:
        async with EasyBrowser() as browser:
            recorder = HARRecorder(browser.page)
            await recorder.start()

            await browser.goto("https://example.com")

            har_data = await recorder.stop()
            await recorder.save("session.har")
    """

    def __init__(self, page: Page):
        self._page = page
        self._entries: List[Dict[str, Any]] = []
        self._recording = False
        self._start_time: Optional[datetime] = None

    async def start(self) -> None:
        """开始录制"""
        self._entries.clear()
        self._recording = True
        self._start_time = datetime.now()

        self._page.on("request", self._on_request)
        self._page.on("response", self._on_response)

        logger.info("HAR 录制已开始")

    async def stop(self) -> Dict[str, Any]:
        """停止录制并返回 HAR 数据"""
        self._recording = False

        self._page.remove_listener("request", self._on_request)
        self._page.remove_listener("response", self._on_response)

        har_data = self._generate_har()
        logger.info(f"HAR 录制已停止，共 {len(self._entries)} 个请求")
        return har_data

    async def _on_request(self, request: Request) -> None:
        """请求事件处理"""
        if not self._recording:
            return

        entry = {
            "startedDateTime": datetime.now().isoformat(),
            "request": {
                "method": request.method,
                "url": request.url,
                "httpVersion": "HTTP/1.1",
                "headers": [{"name": k, "value": v} for k, v in request.headers.items()],
                "queryString": [],
                "postData": request.post_data,
                "headersSize": -1,
                "bodySize": len(request.post_data) if request.post_data else 0,
            },
            "response": None,
            "cache": {},
            "timings": {"send": 0, "wait": 0, "receive": 0},
        }

        # 解析查询参数
        if "?" in request.url:
            query_string = request.url.split("?")[1]
            for param in query_string.split("&"):
                if "=" in param:
                    name, value = param.split("=", 1)
                    entry["request"]["queryString"].append({"name": name, "value": value})

        self._entries.append(entry)

    async def _on_response(self, response: Response) -> None:
        """响应事件处理"""
        if not self._recording:
            return

        request = response.request
        # 查找对应的请求条目
        for entry in reversed(self._entries):
            if entry["request"]["url"] == request.url and entry["response"] is None:
                try:
                    body = await response.text()
                    body_size = len(body)
                except:
                    body = None
                    body_size = 0

                entry["response"] = {
                    "status": response.status,
                    "statusText": response.status_text,
                    "httpVersion": "HTTP/1.1",
                    "headers": [{"name": k, "value": v} for k, v in response.headers.items()],
                    "content": {
                        "size": body_size,
                        "mimeType": response.headers.get("content-type", "text/plain"),
                        "text": body[:10000] if body else None,
                    },
                    "headersSize": -1,
                    "bodySize": body_size,
                }
                break

    def _generate_har(self) -> Dict[str, Any]:
        """生成 HAR 格式数据"""
        return {
            "log": {
                "version": "1.2",
                "creator": {
                    "name": "Playwright-EZ",
                    "version": "2.4.0",
                },
                "browser": {
                    "name": "Playwright",
                    "version": "1.0",
                },
                "pages": [],
                "entries": self._entries,
                "comment": f"Recorded at {self._start_time.isoformat() if self._start_time else ''}",
            }
        }

    async def save(self, filepath: str) -> None:
        """保存为 HAR 文件"""
        har_data = self._generate_har()
        with open(filepath, 'w', encoding='utf-8') as f:
            json.dump(har_data, f, ensure_ascii=False, indent=2)
        logger.info(f"HAR 文件已保存: {filepath}")

    async def load(self, filepath: str) -> Dict[str, Any]:
        """加载 HAR 文件"""
        with open(filepath, 'r', encoding='utf-8') as f:
            har_data = json.load(f)
        self._entries = har_data.get("log", {}).get("entries", [])
        logger.info(f"HAR 文件已加载: {len(self._entries)} 个请求")
        return har_data


class APIMocker:
    """
    API Mock 管理器

    提供场景化的 API Mock 功能。

    Example:
        mocker = APIMocker(page)

        # 添加单个 mock
        mocker.add_mock("/api/users", {"users": []})

        # 添加场景
        mocker.add_scenario("error", [
            {"url": "/api/users", "status": 500, "response": {"error": "Server error"}}
        ])

        # 使用场景
        await mocker.use_scenario("error")
    """

    def __init__(self, page: Page):
        self._page = page
        self._mocks: Dict[str, Dict[str, Any]] = {}
        self._scenarios: Dict[str, List[Dict[str, Any]]] = {}
        self._active_routes: List[str] = []

    def add_mock(
        self,
        url_pattern: str,
        response: Any,
        method: str = "GET",
        status: int = 200,
        delay: float = 0,
        headers: Optional[Dict[str, str]] = None,
    ) -> None:
        """
        添加 Mock 规则

        Args:
            url_pattern: URL 匹配模式
            response: 响应内容
            method: HTTP 方法
            status: 状态码
            delay: 延迟时间（秒）
            headers: 响应头
        """
        self._mocks[url_pattern] = {
            "response": response,
            "method": method,
            "status": status,
            "delay": delay,
            "headers": headers or {"content-type": "application/json"},
        }

    def add_scenario(self, name: str, mocks: List[Dict[str, Any]]) -> None:
        """
        添加场景

        Args:
            name: 场景名称
            mocks: Mock 规则列表
        """
        self._scenarios[name] = mocks

    async def use_scenario(self, name: str) -> None:
        """
        使用场景

        Args:
            name: 场景名称
        """
        if name not in self._scenarios:
            logger.warning(f"场景不存在: {name}")
            return

        # 清除现有路由
        await self.clear()

        # 应用场景中的所有 mock
        for mock in self._scenarios[name]:
            await self._apply_mock(mock)

        logger.info(f"已应用场景: {name}")

    async def _apply_mock(self, mock_config: Dict[str, Any]) -> None:
        """应用单个 mock 规则"""
        url_pattern = mock_config.get("url", "**")
        response = mock_config.get("response", {})
        status = mock_config.get("status", 200)
        delay = mock_config.get("delay", 0)
        method = mock_config.get("method", "GET")
        headers = mock_config.get("headers", {"content-type": "application/json"})

        async def handler(route: Route):
            if delay > 0:
                import asyncio
                await asyncio.sleep(delay)

            body = json.dumps(response) if isinstance(response, (dict, list)) else str(response)
            await route.fulfill(status=status, body=body, headers=headers)

        await self._page.route(url_pattern, handler)
        self._active_routes.append(url_pattern)

    async def mock_response(
        self,
        url_pattern: str,
        response: Any = None,
        status: int = 200,
        **kwargs
    ) -> None:
        """
        快速设置 mock 响应

        Args:
            url_pattern: URL 匹配模式
            response: 响应内容
            status: 状态码
        """
        self.add_mock(url_pattern, response, status=status, **kwargs)
        await self._apply_mock(self._mocks[url_pattern])

    async def clear(self) -> None:
        """清除所有 Mock"""
        for pattern in self._active_routes:
            try:
                await self._page.unroute(pattern)
            except:
                pass
        self._active_routes.clear()
        logger.info("已清除所有 Mock")

    def list_mocks(self) -> Dict[str, Dict[str, Any]]:
        """列出所有 Mock 规则"""
        return self._mocks.copy()

    def list_scenarios(self) -> List[str]:
        """列出所有场景"""
        return list(self._scenarios.keys())


class RequestReplayer:
    """
    请求重放器

    捕获请求并支持重放。

    Example:
        replayer = RequestReplayer(page)

        # 捕获请求
        await replayer.capture("/api/products")

        # 执行操作触发请求
        await page.click(".load-more")

        # 重放捕获的请求
        responses = await replayer.replay_all()
    """

    def __init__(self, page: Page):
        self._page = page
        self._captured: List[Dict[str, Any]] = []

    async def capture(self, url_pattern: str) -> None:
        """
        捕获匹配的请求

        Args:
            url_pattern: URL 匹配模式
        """
        async def handler(route: Route):
            request = route.request

            # 记录请求信息
            captured_request = {
                "url": request.url,
                "method": request.method,
                "headers": dict(request.headers),
                "post_data": request.post_data,
                "resource_type": request.resource_type,
            }
            self._captured.append(captured_request)

            # 继续原始请求
            await route.continue_()

        await self._page.route(url_pattern, handler)
        logger.info(f"开始捕获请求: {url_pattern}")

    async def replay_all(self) -> List[Any]:
        """
        重放所有捕获的请求

        Returns:
            List: 响应列表
        """
        import aiohttp

        responses = []

        async with aiohttp.ClientSession() as session:
            for req in self._captured:
                try:
                    method = req["method"].lower()
                    url = req["url"]
                    headers = req["headers"]
                    data = req["post_data"]

                    async with session.request(
                        method,
                        url,
                        headers=headers,
                        data=data,
                    ) as response:
                        body = await response.text()
                        responses.append({
                            "url": url,
                            "status": response.status,
                            "body": body,
                        })

                except Exception as e:
                    responses.append({
                        "url": req["url"],
                        "error": str(e),
                    })

        logger.info(f"重放了 {len(responses)} 个请求")
        return responses

    async def replay_modified(
        self,
        url_pattern: str,
        modifications: Dict[str, Any],
    ) -> Any:
        """
        修改后重放

        Args:
            url_pattern: URL 匹配模式
            modifications: 修改项（headers, body, method 等）

        Returns:
            响应
        """
        import aiohttp

        # 找到匹配的请求
        matched = None
        for req in self._captured:
            if url_pattern in req["url"]:
                matched = req
                break

        if not matched:
            logger.warning(f"未找到匹配的请求: {url_pattern}")
            return None

        # 应用修改
        modified = matched.copy()
        if "headers" in modifications:
            modified["headers"].update(modifications["headers"])
        if "post_data" in modifications:
            modified["post_data"] = modifications["post_data"]
        if "method" in modifications:
            modified["method"] = modifications["method"]

        # 重放
        async with aiohttp.ClientSession() as session:
            method = modified["method"].lower()
            url = modified["url"]
            headers = modified["headers"]
            data = modified["post_data"]

            async with session.request(method, url, headers=headers, data=data) as response:
                body = await response.text()
                return {
                    "url": url,
                    "status": response.status,
                    "body": body,
                    "headers": dict(response.headers),
                }

    def get_captured(self) -> List[Dict[str, Any]]:
        """获取捕获的请求列表"""
        return self._captured.copy()

    def clear(self) -> None:
        """清空捕获的请求"""
        self._captured.clear()
