"""
页面状态管理
管理页面状态和快照
"""
import json
import hashlib
from typing import Optional, Dict, Any, List
from playwright.async_api import Page
from loguru import logger
from dataclasses import dataclass, field
from datetime import datetime
from pathlib import Path


@dataclass
class PageSnapshot:
    """页面快照"""
    url: str
    title: str
    html_hash: str
    timestamp: datetime = field(default_factory=datetime.now)
    cookies: List[Dict] = field(default_factory=list)
    localStorage: Dict[str, str] = field(default_factory=dict)
    sessionStorage: Dict[str, str] = field(default_factory=dict)
    scroll_position: Dict[str, int] = field(default_factory=dict)
    form_values: Dict[str, str] = field(default_factory=dict)
    
    def to_dict(self) -> Dict[str, Any]:
        return {
            "url": self.url,
            "title": self.title,
            "html_hash": self.html_hash,
            "timestamp": self.timestamp.isoformat(),
            "cookies": self.cookies,
            "localStorage": self.localStorage,
            "sessionStorage": self.sessionStorage,
            "scroll_position": self.scroll_position,
            "form_values": self.form_values,
        }


class PageStateManager:
    """
    页面状态管理器
    保存、恢复、比较页面状态
    """
    
    def __init__(self, page: Page):
        self.page = page
        self._snapshots: Dict[str, PageSnapshot] = {}
    
    async def capture(self, name: str = "default") -> PageSnapshot:
        """
        捕获页面状态快照
        
        Args:
            name: 快照名称
            
        Returns:
            PageSnapshot: 页面快照
        """
        # 获取基本信息
        url = self.page.url
        title = await self.page.title()
        
        # 获取HTML哈希
        html = await self.page.content()
        html_hash = hashlib.md5(html.encode()).hexdigest()
        
        # 获取Cookies
        context = self.page.context
        cookies = await context.cookies()
        
        # 获取存储
        localStorage = await self.page.evaluate("""
            () => {
                const items = {};
                for (let i = 0; i < localStorage.length; i++) {
                    const key = localStorage.key(i);
                    items[key] = localStorage.getItem(key);
                }
                return items;
            }
        """)
        
        sessionStorage = await self.page.evaluate("""
            () => {
                const items = {};
                for (let i = 0; i < sessionStorage.length; i++) {
                    const key = sessionStorage.key(i);
                    items[key] = sessionStorage.getItem(key);
                }
                return items;
            }
        """)
        
        # 获取滚动位置
        scroll_position = await self.page.evaluate("""
            () => ({
                x: window.scrollX,
                y: window.scrollY
            })
        """)
        
        # 获取表单值
        form_values = await self.page.evaluate("""
            () => {
                const values = {};
                document.querySelectorAll('input, textarea, select').forEach(el => {
                    const name = el.name || el.id;
                    if (name) {
                        values[name] = el.value;
                    }
                });
                return values;
            }
        """)
        
        snapshot = PageSnapshot(
            url=url,
            title=title,
            html_hash=html_hash,
            cookies=list(cookies) if cookies else [],
            localStorage=localStorage or {},
            sessionStorage=sessionStorage or {},
            scroll_position=scroll_position or {},
            form_values=form_values or {},
        )
        
        self._snapshots[name] = snapshot
        logger.info(f"页面快照已捕获: {name}")
        
        return snapshot
    
    async def restore(self, name: str = "default") -> bool:
        """
        恢复页面状态
        
        Args:
            name: 快照名称
            
        Returns:
            bool: 是否成功
        """
        if name not in self._snapshots:
            logger.warning(f"快照不存在: {name}")
            return False
        
        snapshot = self._snapshots[name]
        
        # 导航到URL
        if self.page.url != snapshot.url:
            await self.page.goto(snapshot.url)
        
        # 恢复Cookies
        if snapshot.cookies:
            await self.page.context.add_cookies(snapshot.cookies)
        
        # 恢复localStorage
        for key, value in snapshot.localStorage.items():
            await self.page.evaluate(
                f"localStorage.setItem('{key}', '{value}')"
            )
        
        # 恢复sessionStorage
        for key, value in snapshot.sessionStorage.items():
            await self.page.evaluate(
                f"sessionStorage.setItem('{key}', '{value}')"
            )
        
        # 恢复滚动位置
        if snapshot.scroll_position:
            await self.page.evaluate(
                f"window.scrollTo({snapshot.scroll_position.get('x', 0)}, "
                f"{snapshot.scroll_position.get('y', 0)})"
            )
        
        # 恢复表单值
        for name, value in snapshot.form_values.items():
            try:
                await self.page.fill(f"[name='{name}']", value)
            except:
                pass
        
        logger.info(f"页面状态已恢复: {name}")
        return True
    
    def compare(self, name1: str, name2: str) -> Dict[str, Any]:
        """
        比较两个快照
        
        Args:
            name1: 第一个快照名称
            name2: 第二个快照名称
            
        Returns:
            Dict: 比较结果
        """
        if name1 not in self._snapshots or name2 not in self._snapshots:
            return {"error": "快照不存在"}
        
        s1 = self._snapshots[name1]
        s2 = self._snapshots[name2]
        
        return {
            "url_changed": s1.url != s2.url,
            "title_changed": s1.title != s2.title,
            "html_changed": s1.html_hash != s2.html_hash,
            "cookies_changed": len(s1.cookies) != len(s2.cookies),
            "form_values_changed": s1.form_values != s2.form_values,
            "time_diff_seconds": (s2.timestamp - s1.timestamp).total_seconds(),
        }
    
    def get_snapshot(self, name: str = "default") -> Optional[PageSnapshot]:
        """获取快照"""
        return self._snapshots.get(name)
    
    def list_snapshots(self) -> List[str]:
        """列出所有快照"""
        return list(self._snapshots.keys())
    
    def delete_snapshot(self, name: str):
        """删除快照"""
        if name in self._snapshots:
            del self._snapshots[name]
            logger.info(f"快照已删除: {name}")
    
    def save_snapshot(self, name: str, filepath: str):
        """保存快照到文件"""
        if name not in self._snapshots:
            return
        
        snapshot = self._snapshots[name]
        Path(filepath).parent.mkdir(parents=True, exist_ok=True)
        
        with open(filepath, 'w', encoding='utf-8') as f:
            json.dump(snapshot.to_dict(), f, ensure_ascii=False, indent=2)
        
        logger.info(f"快照已保存: {filepath}")
    
    def load_snapshot(self, filepath: str) -> PageSnapshot:
        """从文件加载快照"""
        with open(filepath, 'r', encoding='utf-8') as f:
            data = json.load(f)
        
        snapshot = PageSnapshot(
            url=data["url"],
            title=data["title"],
            html_hash=data["html_hash"],
            timestamp=datetime.fromisoformat(data["timestamp"]),
            cookies=data.get("cookies", []),
            localStorage=data.get("localStorage", {}),
            sessionStorage=data.get("sessionStorage", {}),
            scroll_position=data.get("scroll_position", {}),
            form_values=data.get("form_values", {}),
        )
        
        name = Path(filepath).stem
        self._snapshots[name] = snapshot
        
        logger.info(f"快照已加载: {filepath}")
        return snapshot


class PageHistory:
    """
    页面历史记录
    记录用户浏览历史
    """
    
    def __init__(self, page: Page):
        self.page = page
        self._history: List[Dict[str, Any]] = []
        self._current_index = -1
    
    def record(self, action: str, details: Dict[str, Any] = None):
        """记录操作"""
        entry = {
            "action": action,
            "url": self.page.url,
            "timestamp": datetime.now().isoformat(),
            "details": details or {},
        }
        self._history.append(entry)
        self._current_index = len(self._history) - 1
        
        logger.debug(f"记录操作: {action}")
    
    def get_current(self) -> Optional[Dict[str, Any]]:
        """获取当前记录"""
        if 0 <= self._current_index < len(self._history):
            return self._history[self._current_index]
        return None
    
    def get_previous(self) -> Optional[Dict[str, Any]]:
        """获取上一条记录"""
        if self._current_index > 0:
            self._current_index -= 1
            return self._history[self._current_index]
        return None
    
    def get_next(self) -> Optional[Dict[str, Any]]:
        """获取下一条记录"""
        if self._current_index < len(self._history) - 1:
            self._current_index += 1
            return self._history[self._current_index]
        return None
    
    def get_history(self, limit: int = 10) -> List[Dict[str, Any]]:
        """获取历史记录"""
        return self._history[-limit:]
    
    def clear(self):
        """清除历史"""
        self._history.clear()
        self._current_index = -1
        logger.info("历史记录已清除")


class NavigationHelper:
    """
    导航辅助工具
    提供高级导航功能
    """
    
    def __init__(self, page: Page):
        self.page = page
    
    async def go_back(self, wait: bool = True):
        """后退"""
        await self.page.go_back(wait_until="load" if wait else None)
        logger.info("后退")
    
    async def go_forward(self, wait: bool = True):
        """前进"""
        await self.page.go_forward(wait_until="load" if wait else None)
        logger.info("前进")
    
    async def refresh(self, wait: bool = True):
        """刷新"""
        await self.page.reload(wait_until="load" if wait else None)
        logger.info("刷新")
    
    async def navigate_with_retry(
        self,
        url: str,
        max_retries: int = 3,
        timeout: float = 30,
    ) -> bool:
        """
        带重试的导航
        
        Args:
            url: 目标URL
            max_retries: 最大重试次数
            timeout: 超时时间
            
        Returns:
            bool: 是否成功
        """
        import asyncio
        
        for attempt in range(max_retries):
            try:
                await self.page.goto(url, timeout=timeout * 1000)
                logger.info(f"导航成功: {url}")
                return True
            except Exception as e:
                logger.warning(f"导航失败 (尝试 {attempt + 1}/{max_retries}): {e}")
                if attempt < max_retries - 1:
                    await asyncio.sleep(2)
        
        logger.error(f"导航失败: {url}")
        return False
    
    async def wait_for_navigation(
        self,
        trigger: callable,
        expected_url: str = None,
        timeout: float = 30,
    ):
        """
        等待导航完成
        
        Args:
            trigger: 触发导航的函数
            expected_url: 期望的URL（可选）
            timeout: 超时时间
        """
        async with self.page.expect_navigation(
            url=expected_url,
            timeout=timeout * 1000
        ):
            await trigger()
        
        logger.info(f"导航完成: {self.page.url}")
    
    async def navigate_in_new_tab(self, url: str) -> Page:
        """
        在新标签页中导航
        
        Args:
            url: 目标URL
            
        Returns:
            Page: 新标签页
        """
        context = self.page.context
        new_page = await context.new_page()
        await new_page.goto(url)
        
        logger.info(f"新标签页: {url}")
        return new_page
