"""
移动端模拟模块
模拟各种移动设备
"""
from typing import Optional, Dict, Any
from playwright.async_api import BrowserContext
from loguru import logger
from dataclasses import dataclass


@dataclass
class DeviceConfig:
    """设备配置"""
    name: str
    user_agent: str
    viewport_width: int
    viewport_height: int
    device_scale_factor: float = 1.0
    is_mobile: bool = True
    has_touch: bool = True
    default_browser_type: str = "webkit"
    
    def to_context_options(self) -> Dict[str, Any]:
        """转换为上下文选项"""
        return {
            "user_agent": self.user_agent,
            "viewport": {
                "width": self.viewport_width,
                "height": self.viewport_height,
            },
            "device_scale_factor": self.device_scale_factor,
            "is_mobile": self.is_mobile,
            "has_touch": self.has_touch,
        }


# 预定义设备配置
DEVICES = {
    # iPhone 系列
    "iphone-14": DeviceConfig(
        name="iPhone 14",
        user_agent="Mozilla/5.0 (iPhone; CPU iPhone OS 17_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.0 Mobile/15E148 Safari/604.1",
        viewport_width=390,
        viewport_height=844,
        device_scale_factor=3.0,
    ),
    "iphone-14-pro": DeviceConfig(
        name="iPhone 14 Pro",
        user_agent="Mozilla/5.0 (iPhone; CPU iPhone OS 17_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.0 Mobile/15E148 Safari/604.1",
        viewport_width=393,
        viewport_height=852,
        device_scale_factor=3.0,
    ),
    "iphone-14-pro-max": DeviceConfig(
        name="iPhone 14 Pro Max",
        user_agent="Mozilla/5.0 (iPhone; CPU iPhone OS 17_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.0 Mobile/15E148 Safari/604.1",
        viewport_width=430,
        viewport_height=932,
        device_scale_factor=3.0,
    ),
    "iphone-15": DeviceConfig(
        name="iPhone 15",
        user_agent="Mozilla/5.0 (iPhone; CPU iPhone OS 17_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.2 Mobile/15E148 Safari/604.1",
        viewport_width=393,
        viewport_height=852,
        device_scale_factor=3.0,
    ),
    "iphone-se": DeviceConfig(
        name="iPhone SE",
        user_agent="Mozilla/5.0 (iPhone; CPU iPhone OS 17_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.0 Mobile/15E148 Safari/604.1",
        viewport_width=375,
        viewport_height=667,
        device_scale_factor=2.0,
    ),
    
    # iPad 系列
    "ipad-pro-11": DeviceConfig(
        name="iPad Pro 11",
        user_agent="Mozilla/5.0 (iPad; CPU OS 17_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.0 Mobile/15E148 Safari/604.1",
        viewport_width=834,
        viewport_height=1194,
        device_scale_factor=2.0,
        is_mobile=False,
    ),
    "ipad-pro-12.9": DeviceConfig(
        name="iPad Pro 12.9",
        user_agent="Mozilla/5.0 (iPad; CPU OS 17_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.0 Mobile/15E148 Safari/604.1",
        viewport_width=1024,
        viewport_height=1366,
        device_scale_factor=2.0,
        is_mobile=False,
    ),
    "ipad-mini": DeviceConfig(
        name="iPad Mini",
        user_agent="Mozilla/5.0 (iPad; CPU OS 17_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.0 Mobile/15E148 Safari/604.1",
        viewport_width=744,
        viewport_height=1133,
        device_scale_factor=2.0,
        is_mobile=False,
    ),
    
    # Android 系列
    "pixel-7": DeviceConfig(
        name="Pixel 7",
        user_agent="Mozilla/5.0 (Linux; Android 13; Pixel 7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Mobile Safari/537.36",
        viewport_width=412,
        viewport_height=915,
        device_scale_factor=2.625,
    ),
    "pixel-7-pro": DeviceConfig(
        name="Pixel 7 Pro",
        user_agent="Mozilla/5.0 (Linux; Android 13; Pixel 7 Pro) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Mobile Safari/537.36",
        viewport_width=412,
        viewport_height=892,
        device_scale_factor=3.5,
    ),
    "samsung-s23": DeviceConfig(
        name="Samsung Galaxy S23",
        user_agent="Mozilla/5.0 (Linux; Android 13; SM-S911B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Mobile Safari/537.36",
        viewport_width=360,
        viewport_height=780,
        device_scale_factor=3.0,
    ),
    "samsung-s23-ultra": DeviceConfig(
        name="Samsung Galaxy S23 Ultra",
        user_agent="Mozilla/5.0 (Linux; Android 13; SM-S918B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Mobile Safari/537.36",
        viewport_width=412,
        viewport_height=903,
        device_scale_factor=3.5,
    ),
    "samsung-fold": DeviceConfig(
        name="Samsung Galaxy Fold",
        user_agent="Mozilla/5.0 (Linux; Android 13; SM-F936B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Mobile Safari/537.36",
        viewport_width=884,
        viewport_height=1104,
        device_scale_factor=2.75,
    ),
    
    # 其他设备
    "kindle-fire-hdx": DeviceConfig(
        name="Kindle Fire HDX",
        user_agent="Mozilla/5.0 (Linux; U; Android 4.3; en-us; KFTHWI Build/JSS15R) AppleWebKit/537.36 (KHTML, like Gecko) Silk/3.58 like Chrome/120.0.0.0 Safari/537.36",
        viewport_width=800,
        viewport_height=1280,
        device_scale_factor=2.0,
    ),
}


class DeviceEmulator:
    """
    设备模拟器
    模拟各种移动设备
    """
    
    def __init__(self):
        self.devices = DEVICES.copy()
        self._current_device: Optional[DeviceConfig] = None
    
    def get_device(self, name: str) -> Optional[DeviceConfig]:
        """
        获取设备配置
        
        Args:
            name: 设备名称
            
        Returns:
            DeviceConfig: 设备配置
        """
        return self.devices.get(name)
    
    def list_devices(self) -> list:
        """列出所有可用设备"""
        return list(self.devices.keys())
    
    def add_device(self, name: str, config: DeviceConfig):
        """添加自定义设备"""
        self.devices[name] = config
        logger.info(f"添加设备: {name}")
    
    def get_context_options(self, device_name: str) -> Dict[str, Any]:
        """
        获取设备对应的上下文选项
        
        Args:
            device_name: 设备名称
            
        Returns:
            Dict: 上下文选项
        """
        device = self.get_device(device_name)
        if not device:
            raise ValueError(f"未知设备: {device_name}")
        
        self._current_device = device
        logger.info(f"使用设备: {device.name}")
        
        return device.to_context_options()
    
    @property
    def current_device(self) -> Optional[DeviceConfig]:
        """获取当前设备"""
        return self._current_device


class Geolocation:
    """
    地理位置
    模拟GPS定位
    """
    
    # 预定义位置
    LOCATIONS = {
        "beijing": {"latitude": 39.9042, "longitude": 116.4074, "accuracy": 100},
        "shanghai": {"latitude": 31.2304, "longitude": 121.4737, "accuracy": 100},
        "guangzhou": {"latitude": 23.1291, "longitude": 113.2644, "accuracy": 100},
        "shenzhen": {"latitude": 22.5431, "longitude": 114.0579, "accuracy": 100},
        "new_york": {"latitude": 40.7128, "longitude": -74.0060, "accuracy": 100},
        "tokyo": {"latitude": 35.6762, "longitude": 139.6503, "accuracy": 100},
        "london": {"latitude": 51.5074, "longitude": -0.1278, "accuracy": 100},
    }
    
    @classmethod
    def get(cls, name: str) -> Optional[Dict[str, float]]:
        """获取预定义位置"""
        return cls.LOCATIONS.get(name)
    
    @classmethod
    def list_locations(cls) -> list:
        """列出所有预定义位置"""
        return list(cls.LOCATIONS.keys())
    
    @classmethod
    def custom(cls, latitude: float, longitude: float, accuracy: float = 100) -> Dict[str, float]:
        """创建自定义位置"""
        return {
            "latitude": latitude,
            "longitude": longitude,
            "accuracy": accuracy,
        }


class NetworkCondition:
    """
    网络条件
    模拟不同网络环境
    """
    
    CONDITIONS = {
        "offline": {
            "offline": True,
            "download": 0,
            "upload": 0,
            "latency": 0,
        },
        "slow-3g": {
            "offline": False,
            "download": 500000,  # 500 Kbps
            "upload": 500000,
            "latency": 2000,  # 2s
        },
        "fast-3g": {
            "offline": False,
            "download": 1500000,  # 1.5 Mbps
            "upload": 750000,
            "latency": 500,
        },
        "4g": {
            "offline": False,
            "download": 5000000,  # 5 Mbps
            "upload": 2000000,
            "latency": 100,
        },
        "5g": {
            "offline": False,
            "download": 50000000,  # 50 Mbps
            "upload": 10000000,
            "latency": 20,
        },
        "wifi": {
            "offline": False,
            "download": 100000000,  # 100 Mbps
            "upload": 50000000,
            "latency": 5,
        },
    }
    
    @classmethod
    def get(cls, name: str) -> Optional[Dict[str, Any]]:
        """获取网络条件"""
        return cls.CONDITIONS.get(name)
    
    @classmethod
    def list_conditions(cls) -> list:
        """列出所有网络条件"""
        return list(cls.CONDITIONS.keys())
    
    @classmethod
    def custom(
        cls,
        download: float,
        upload: float,
        latency: float,
        offline: bool = False,
    ) -> Dict[str, Any]:
        """创建自定义网络条件"""
        return {
            "offline": offline,
            "download": download,
            "upload": upload,
            "latency": latency,
        }


class MobileHelper:
    """
    移动端辅助工具
    提供移动端特有的操作方法
    """
    
    def __init__(self, page):
        self.page = page
    
    async def tap(self, selector: str, **kwargs):
        """模拟触摸点击"""
        await self.page.locator(selector).tap(**kwargs)
        logger.debug(f"触摸点击: {selector}")
    
    async def pinch(self, scale: float = 0.5):
        """
        模拟双指缩放
        
        Args:
            scale: 缩放比例 (<1 缩小, >1 放大)
        """
        await self.page.evaluate(f"""
            () => {{
                const viewport = {{
                    width: window.innerWidth,
                    height: window.innerHeight
                }};
                
                const centerX = viewport.width / 2;
                const centerY = viewport.height / 2;
                
                // 模拟双指手势
                document.body.style.transform = 'scale({scale})';
                document.body.style.transformOrigin = 'center center';
            }}
        """)
        logger.debug(f"双指缩放: scale={scale}")
    
    async def rotate_screen(self):
        """旋转屏幕（横竖屏切换）"""
        # 获取当前视口
        viewport = self.page.viewport_size
        if viewport:
            # 交换宽高
            new_viewport = {
                "width": viewport["height"],
                "height": viewport["width"],
            }
            await self.page.set_viewport_size(new_viewport)
            logger.debug(f"屏幕旋转: {new_viewport}")
    
    async def scroll_to_bottom(self):
        """滚动到页面底部"""
        await self.page.evaluate("""
            () => {
                window.scrollTo(0, document.body.scrollHeight);
            }
        """)
        logger.debug("滚动到底部")
    
    async def scroll_to_top(self):
        """滚动到页面顶部"""
        await self.page.evaluate("""
            () => {
                window.scrollTo(0, 0);
            }
        """)
        logger.debug("滚动到顶部")
    
    async def pull_to_refresh(self):
        """模拟下拉刷新"""
        await self.page.evaluate("""
            () => {
                // 触发touch事件模拟下拉刷新
                const startY = 50;
                const endY = 300;
                
                const touchStart = new TouchEvent('touchstart', {
                    touches: [new Touch({
                        identifier: 0,
                        target: document.body,
                        clientX: window.innerWidth / 2,
                        clientY: startY
                    })]
                });
                
                const touchMove = new TouchEvent('touchmove', {
                    touches: [new Touch({
                        identifier: 0,
                        target: document.body,
                        clientX: window.innerWidth / 2,
                        clientY: endY
                    })]
                });
                
                const touchEnd = new TouchEvent('touchend', {
                    touches: [],
                    changedTouches: [new Touch({
                        identifier: 0,
                        target: document.body,
                        clientX: window.innerWidth / 2,
                        clientY: endY
                    })]
                });
                
                document.body.dispatchEvent(touchStart);
                document.body.dispatchEvent(touchMove);
                document.body.dispatchEvent(touchEnd);
            }
        """)
        logger.debug("下拉刷新")
    
    async def swipe(
        self,
        direction: str = "up",
        distance: int = 300,
        duration: int = 300,
    ):
        """
        模拟滑动
        
        Args:
            direction: 方向
            distance: 距离
            duration: 持续时间
        """
        viewport = self.page.viewport_size
        if not viewport:
            return
        
        center_x = viewport["width"] // 2
        center_y = viewport["height"] // 2
        
        directions = {
            "up": (center_y + distance // 2, center_y - distance // 2),
            "down": (center_y - distance // 2, center_y + distance // 2),
            "left": (center_x + distance // 2, center_x - distance // 2),
            "right": (center_x - distance // 2, center_x + distance // 2),
        }
        
        if direction not in directions:
            raise ValueError(f"无效方向: {direction}")
        
        start, end = directions[direction]
        
        if direction in ["up", "down"]:
            await self.page.mouse.move(center_x, start)
            await self.page.mouse.down()
            await self.page.mouse.move(center_x, end, steps=10)
            await self.page.mouse.up()
        else:
            await self.page.mouse.move(start, center_y)
            await self.page.mouse.down()
            await self.page.mouse.move(end, center_y, steps=10)
            await self.page.mouse.up()
        
        logger.debug(f"滑动: direction={direction}, distance={distance}")
    
    async def long_press(self, selector: str, duration: int = 500):
        """
        长按
        
        Args:
            selector: 选择器
            duration: 持续时间
        """
        element = self.page.locator(selector)
        box = await element.bounding_box()
        
        if box:
            x = box["x"] + box["width"] / 2
            y = box["y"] + box["height"] / 2
            
            await self.page.mouse.move(x, y)
            await self.page.mouse.down()
            await self.page.wait_for_timeout(duration)
            await self.page.mouse.up()
            
            logger.debug(f"长按: selector={selector}, duration={duration}ms")
