"""
弹窗自动关闭处理器
自动识别并关闭各类弹窗（登录弹窗、广告弹窗、Cookie提示等）

增强功能：
- 后台自动监控机制
- 广告过滤列表
- 配置化弹窗规则
"""
import asyncio
from typing import Optional, List, Callable, Dict, Any
from dataclasses import dataclass, field
from playwright.async_api import Page
from loguru import logger


# ==================== 广告过滤列表 ====================

AD_SELECTORS = [
    # 通用广告选择器
    "[class*='ad-']",
    "[class*='ads-']",
    "[class*='advertisement']",
    "[class*='Advertising']",
    "[id*='ad-']",
    "[id*='ads-']",
    "[data-ad]",
    "[data-ad-slot]",

    # 常见广告容器
    ".ad-container",
    ".ads-container",
    ".advertisement",
    ".ad-banner",
    ".ad-wrapper",
    ".ad-block",

    # 弹窗广告
    "[class*='popup-ad']",
    "[class*='modal-ad']",
    ".interstitial-ad",
    ".overlay-ad",

    # Google AdSense
    ".adsbygoogle",
    "[id*='google_ads']",
    "[id*='google-ad']",

    # 百度广告
    "[id*='baidu_ad']",
    ".ec_wise_ad",

    # 视频广告
    ".video-ad",
    ".preroll-ad",
    ".midroll-ad",
    "[class*='video-ad']",
]

# 需要隐藏的广告选择器（不点击，直接隐藏）
AD_HIDE_SELECTORS = [
    "[class*='ad-banner']",
    "[class*='sidebar-ad']",
    ".ad-slot",
    "[id*='div-gpt-ad']",
]


# ==================== 配置化弹窗规则 ====================

@dataclass
class PopupRule:
    """
    弹窗规则

    定义特定弹窗的识别和关闭策略。
    """
    name: str
    selectors: List[str]
    action: str = "click"  # click, esc, wait, hide
    priority: int = 0  # 优先级，数字越大优先级越高
    description: str = ""
    enabled: bool = True

    def __lt__(self, other: "PopupRule") -> bool:
        """用于排序，优先级高的排前面"""
        return self.priority > other.priority


# 预定义的弹窗规则
DEFAULT_POPUP_RULES: List[PopupRule] = [
    # 登录弹窗规则
    PopupRule(
        name="login_popup",
        selectors=[
            "[class*='login-modal']",
            "[class*='login-dialog']",
            "[class*='LoginModal']",
        ],
        action="click",
        priority=10,
        description="登录弹窗",
    ),

    # Cookie 提示规则
    PopupRule(
        name="cookie_banner",
        selectors=[
            "[class*='cookie-banner']",
            "[class*='cookie-consent']",
            "[class*='CookieBanner']",
            "#cookie-banner",
        ],
        action="click",
        priority=5,
        description="Cookie 提示横幅",
    ),

    # 通讯录请求规则
    PopupRule(
        name="newsletter_popup",
        selectors=[
            "[class*='newsletter-popup']",
            "[class*='subscribe-popup']",
            "[class*='NewsletterPopup']",
        ],
        action="click",
        priority=8,
        description="订阅弹窗",
    ),

    # 应用下载弹窗
    PopupRule(
        name="app_download_popup",
        selectors=[
            "[class*='app-download']",
            "[class*='download-app']",
            "[class*='AppDownload']",
        ],
        action="click",
        priority=7,
        description="应用下载弹窗",
    ),
]


class PopupHandler:
    """
    弹窗处理器
    自动识别并关闭各类弹窗
    """

    # 常见弹窗关闭按钮选择器模式
    CLOSE_BUTTON_PATTERNS = [
        # 通用关闭按钮
        "[class*='close']",
        "[class*='Close']",
        "[class*='dismiss']",
        "[class*='cancel']",
        "[aria-label*='关闭']",
        "[aria-label*='close']",
        "[aria-label*='Close']",
        "[title*='关闭']",
        "[title*='close']",
        "button.close",
        ".close-btn",
        ".close-btn",
        ".modal-close",
        ".dialog-close",
        ".popup-close",
        "a.close",
        "span.close",

        # 图标类关闭按钮
        "[class*='icon-close']",
        "[class*='icon-close']",
        "[class*='IconClose']",
        "svg[class*='close']",
        "i[class*='close']",

        # 登录弹窗
        "[class*='login'] [class*='close']",
        "[class*='Login'] [class*='close']",
        ".login-modal .close",
        ".login-dialog .close",

        # X 按钮
        "button[aria-label='×']",
        "button[aria-label='x']",
        "a[aria-label='×']",
        ".x-btn",
        ".btn-x",
    ]

    # 关闭按钮文本模式（中文）
    CLOSE_TEXTS_CN = [
        "关闭",
        "取消",
        "跳过",
        "暂不登录",
        "稍后再说",
        "以后再说",
        "暂不注册",
        "我知道了",
        "不再提示",
        "×",
        "✕",
        "✖",
    ]

    # 关闭按钮文本模式（英文）
    CLOSE_TEXTS_EN = [
        "Close",
        "Cancel",
        "Skip",
        "Later",
        "Not now",
        "No thanks",
        "Dismiss",
        "×",
        "x",
    ]

    # 常见弹窗容器选择器
    POPUP_CONTAINERS = [
        # 模态框
        "[class*='modal']",
        "[class*='Modal']",
        "[class*='dialog']",
        "[class*='Dialog']",
        "[class*='popup']",
        "[class*='Popup']",
        "[role='dialog']",
        "[role='alertdialog']",

        # 登录弹窗
        "[class*='login-modal']",
        "[class*='LoginModal']",
        "[class*='login-dialog']",
        "[class*='LoginDialog']",

        # Cookie提示
        "[class*='cookie']",
        "[class*='Cookie']",

        # 广告弹窗
        "[class*='advertisement']",
        "[class*='ads-popup']",
    ]

    def __init__(self, page: Page):
        self.page = page
        self._closed_popups: List[str] = []

    async def detect_and_close(self) -> bool:
        """
        检测并关闭弹窗

        Returns:
            bool: 是否成功关闭弹窗
        """
        try:
            # 等待页面稳定
            await asyncio.sleep(0.5)

            # 方法1: 尝试通过选择器关闭
            if await self._close_by_selector():
                return True

            # 方法2: 尝试通过文本关闭
            if await self._close_by_text():
                return True

            # 方法3: 尝试按 ESC 键关闭
            if await self._close_by_esc():
                return True

            return False

        except Exception as e:
            logger.debug(f"弹窗检测失败: {e}")
            return False

    async def _close_by_selector(self) -> bool:
        """通过选择器关闭弹窗"""
        for selector in self.CLOSE_BUTTON_PATTERNS:
            try:
                elements = await self.page.locator(selector).all()
                for elem in elements:
                    if await elem.is_visible():
                        # 检查是否在弹窗容器内
                        parent_html = await elem.evaluate(
                            "el => el.closest('[class*=\\'modal\\'], [class*=\\'dialog\\'], [class*=\\'popup\\'], [role=\\'dialog\\']')"
                        )

                        # 如果是关闭按钮或在弹窗内，点击关闭
                        if parent_html or 'close' in selector.lower():
                            await elem.click(timeout=1000)
                            logger.info(f"已关闭弹窗: {selector}")
                            await asyncio.sleep(0.3)
                            return True
            except:
                continue

        return False

    async def _close_by_text(self) -> bool:
        """通过文本关闭弹窗"""
        all_texts = self.CLOSE_TEXTS_CN + self.CLOSE_TEXTS_EN

        for text in all_texts:
            try:
                # 尝试精确匹配
                locator = self.page.get_by_text(text, exact=True)
                if await locator.is_visible():
                    await locator.click(timeout=1000)
                    logger.info(f"已关闭弹窗: 文本 '{text}'")
                    await asyncio.sleep(0.3)
                    return True
            except:
                continue

        return False

    async def _close_by_esc(self) -> bool:
        """通过 ESC 键关闭弹窗"""
        try:
            # 检查是否有可见的弹窗
            for container in self.POPUP_CONTAINERS:
                if await self.page.locator(container).is_visible():
                    await self.page.keyboard.press("Escape")
                    logger.info("已通过 ESC 键关闭弹窗")
                    await asyncio.sleep(0.3)
                    return True
        except:
            pass

        return False

    async def close_login_popup(self) -> bool:
        """
        专门关闭登录弹窗

        Returns:
            bool: 是否成功关闭
        """
        # 登录弹窗特定的关闭策略
        login_close_selectors = [
            # 小红书
            ".login-container .close-btn",
            ".login-modal .close",
            "[class*='login'] [class*='close']",

            # 通用
            ".login-dialog .close",
            ".auth-modal .close",
            "button[class*='close']",

            # 文本关闭
            "text=暂不登录",
            "text=跳过",
            "text=稍后再说",
        ]

        for selector in login_close_selectors:
            try:
                locator = self.page.locator(selector)
                if await locator.is_visible():
                    await locator.click(timeout=1000)
                    logger.info(f"已关闭登录弹窗: {selector}")
                    return True
            except:
                continue

        return False

    async def close_cookie_popup(self) -> bool:
        """
        关闭 Cookie 提示弹窗

        Returns:
            bool: 是否成功关闭
        """
        cookie_close_selectors = [
            # 通用 Cookie 弹窗
            "[class*='cookie'] button",
            "[class*='Cookie'] button",
            "button[class*='accept']",
            "button[class*='reject']",

            # 文本
            "text=接受",
            "text=拒绝",
            "text=同意",
            "text=Accept",
            "text=Reject",
        ]

        for selector in cookie_close_selectors:
            try:
                locator = self.page.locator(selector)
                if await locator.is_visible():
                    await locator.click(timeout=1000)
                    logger.info(f"已关闭 Cookie 弹窗: {selector}")
                    return True
            except:
                continue

        return False

    async def wait_and_close(
        self,
        timeout: float = 5.0,
        check_interval: float = 0.5
    ) -> bool:
        """
        等待弹窗出现并关闭

        Args:
            timeout: 超时时间
            check_interval: 检查间隔

        Returns:
            bool: 是否成功关闭
        """
        start_time = asyncio.get_event_loop().time()

        while asyncio.get_event_loop().time() - start_time < timeout:
            if await self.detect_and_close():
                return True
            await asyncio.sleep(check_interval)

        return False


async def auto_close_popup(page: Page, timeout: float = 5.0) -> bool:
    """
    自动关闭弹窗的便捷函数

    Args:
        page: 页面对象
        timeout: 超时时间

    Returns:
        bool: 是否成功关闭
    """
    handler = PopupHandler(page)
    return await handler.wait_and_close(timeout)


async def smart_close_popup(page: Page) -> bool:
    """
    智能关闭弹窗
    尝试多种策略关闭弹窗

    Args:
        page: 页面对象

    Returns:
        bool: 是否成功关闭
    """
    handler = PopupHandler(page)

    # 依次尝试不同策略
    strategies = [
        handler.close_login_popup,
        handler.close_cookie_popup,
        handler.detect_and_close,
    ]

    for strategy in strategies:
        try:
            if await strategy():
                return True
        except:
            continue

    return False


class AutoPopupWatcher:
    """
    后台自动监控并关闭弹窗

    在后台持续监控页面，自动关闭出现的弹窗。

    Example:
        async with EasyBrowser() as browser:
            watcher = AutoPopupWatcher(browser.page)

            # 启动监控
            await watcher.start()

            # 执行其他操作...
            await browser.goto("https://example.com")

            # 停止监控
            await watcher.stop()
    """

    def __init__(
        self,
        page: Page,
        interval: float = 1.0,
        rules: Optional[List[PopupRule]] = None,
    ):
        """
        初始化弹窗监控器

        Args:
            page: 页面对象
            interval: 检查间隔（秒）
            rules: 自定义弹窗规则列表
        """
        self._page = page
        self._interval = interval
        self._rules = rules or DEFAULT_POPUP_RULES.copy()
        self._running = False
        self._task: Optional[asyncio.Task] = None
        self._closed_count = 0
        self._custom_rules: Dict[str, PopupRule] = {}

    async def start(self) -> None:
        """启动后台监控"""
        if self._running:
            logger.warning("弹窗监控已在运行中")
            return

        self._running = True
        self._task = asyncio.create_task(self._watch_loop())
        logger.info(f"弹窗监控已启动，检查间隔: {self._interval}秒")

    async def stop(self) -> None:
        """停止监控"""
        self._running = False

        if self._task:
            self._task.cancel()
            try:
                await self._task
            except asyncio.CancelledError:
                pass
            self._task = None

        logger.info(f"弹窗监控已停止，共关闭 {self._closed_count} 个弹窗")

    def add_custom_rule(self, rule: PopupRule) -> None:
        """
        添加自定义弹窗规则

        Args:
            rule: 弹窗规则
        """
        self._custom_rules[rule.name] = rule
        self._rules.append(rule)
        # 按优先级排序
        self._rules.sort()
        logger.debug(f"添加弹窗规则: {rule.name}")

    def add_simple_rule(
        self,
        name: str,
        selectors: List[str],
        action: str = "click",
        priority: int = 0,
    ) -> None:
        """
        添加简单的弹窗规则

        Args:
            name: 规则名称
            selectors: 选择器列表
            action: 动作类型
            priority: 优先级
        """
        rule = PopupRule(
            name=name,
            selectors=selectors,
            action=action,
            priority=priority,
        )
        self.add_custom_rule(rule)

    def remove_rule(self, name: str) -> bool:
        """
        移除弹窗规则

        Args:
            name: 规则名称

        Returns:
            bool: 是否成功移除
        """
        # 从自定义规则中移除
        if name in self._custom_rules:
            del self._custom_rules[name]

        # 从规则列表中移除
        for i, rule in enumerate(self._rules):
            if rule.name == name:
                self._rules.pop(i)
                return True

        return False

    def get_stats(self) -> Dict[str, Any]:
        """获取监控统计信息"""
        return {
            "running": self._running,
            "interval": self._interval,
            "closed_count": self._closed_count,
            "rules_count": len(self._rules),
            "custom_rules_count": len(self._custom_rules),
        }

    async def _watch_loop(self) -> None:
        """监控循环"""
        while self._running:
            try:
                await self._check_and_close()
            except Exception as e:
                logger.debug(f"弹窗检查异常: {e}")

            await asyncio.sleep(self._interval)

    async def _check_and_close(self) -> bool:
        """检查并关闭弹窗"""
        # 按优先级处理规则
        for rule in self._rules:
            if not rule.enabled:
                continue

            for selector in rule.selectors:
                try:
                    locator = self._page.locator(selector)
                    if await locator.is_visible():
                        closed = await self._execute_action(rule.action, selector)
                        if closed:
                            self._closed_count += 1
                            logger.info(f"已关闭弹窗 [{rule.name}]: {selector}")
                            return True
                except:
                    continue

        # 使用默认的检测方法
        handler = PopupHandler(self._page)
        if await handler.detect_and_close():
            self._closed_count += 1
            return True

        return False

    async def _execute_action(self, action: str, selector: str) -> bool:
        """执行关闭动作"""
        try:
            if action == "click":
                # 查找关闭按钮
                close_selectors = [
                    f"{selector} [class*='close']",
                    f"{selector} button[class*='close']",
                    f"{selector} .close",
                    f"{selector} [aria-label*='close']",
                ]

                for close_sel in close_selectors:
                    try:
                        locator = self._page.locator(close_sel)
                        if await locator.is_visible():
                            await locator.click(timeout=1000)
                            return True
                    except:
                        continue

                # 直接点击容器（某些弹窗点击外部关闭）
                await self._page.locator(selector).click(timeout=1000)
                return True

            elif action == "esc":
                await self._page.keyboard.press("Escape")
                return True

            elif action == "hide":
                await self._page.evaluate(f"""
                    document.querySelectorAll('{selector}').forEach(el => {{
                        el.style.display = 'none';
                    }});
                """)
                return True

            elif action == "wait":
                # 等待弹窗自动消失
                await asyncio.sleep(2)
                return not await self._page.locator(selector).is_visible()

        except Exception as e:
            logger.debug(f"执行动作失败: {e}")

        return False


class PopupRuleManager:
    """
    弹窗规则管理器

    集中管理所有弹窗规则，支持导入导出。
    """

    def __init__(self):
        self._rules: Dict[str, PopupRule] = {
            rule.name: rule for rule in DEFAULT_POPUP_RULES
        }

    def add_rule(self, rule: PopupRule) -> None:
        """添加规则"""
        self._rules[rule.name] = rule

    def remove_rule(self, name: str) -> bool:
        """移除规则"""
        if name in self._rules:
            del self._rules[name]
            return True
        return False

    def get_rule(self, name: str) -> Optional[PopupRule]:
        """获取规则"""
        return self._rules.get(name)

    def get_all_rules(self) -> List[PopupRule]:
        """获取所有规则"""
        return sorted(self._rules.values(), reverse=True)

    def get_enabled_rules(self) -> List[PopupRule]:
        """获取启用的规则"""
        return sorted(
            [r for r in self._rules.values() if r.enabled],
            reverse=True,
        )

    def enable_rule(self, name: str) -> bool:
        """启用规则"""
        rule = self._rules.get(name)
        if rule:
            rule.enabled = True
            return True
        return False

    def disable_rule(self, name: str) -> bool:
        """禁用规则"""
        rule = self._rules.get(name)
        if rule:
            rule.enabled = False
            return True
        return False

    def export_rules(self) -> List[Dict[str, Any]]:
        """导出规则为字典列表"""
        return [
            {
                "name": rule.name,
                "selectors": rule.selectors,
                "action": rule.action,
                "priority": rule.priority,
                "description": rule.description,
                "enabled": rule.enabled,
            }
            for rule in self._rules.values()
        ]

    def import_rules(self, rules_data: List[Dict[str, Any]]) -> int:
        """
        从字典列表导入规则

        Args:
            rules_data: 规则数据列表

        Returns:
            int: 导入的规则数量
        """
        count = 0
        for data in rules_data:
            try:
                rule = PopupRule(**data)
                self._rules[rule.name] = rule
                count += 1
            except:
                continue
        return count
