"""
分页导航器
智能分页导航，自动识别下一页按钮
"""
import asyncio
from dataclasses import dataclass, field
from typing import Optional, Callable, List
from playwright.async_api import Page, Locator
from loguru import logger


@dataclass
class PaginationConfig:
    """分页配置"""
    next_selector: str = 'a.next, button.next, [aria-label="下一页"], [aria-label="Next"]'
    page_selector: Optional[str] = None  # 页码选择器
    max_pages: int = 100
    wait_time: float = 0.5  # 秒
    stop_condition: Optional[Callable[[], bool]] = None
    on_before_page: Optional[Callable[[int], None]] = None
    on_after_page: Optional[Callable[[int], None]] = None


@dataclass
class PaginationResult:
    """分页结果"""
    total_pages: int = 0
    reached_end: bool = False
    stopped_by_condition: bool = False
    duration: float = 0.0  # 毫秒


class PaginationNavigator:
    """
    分页导航器
    自动处理分页导航
    """

    # 常用选择器列表
    NEXT_SELECTORS = [
        'a.next',
        'button.next',
        '[aria-label="下一页"]',
        '[aria-label="Next"]',
        '.pagination a:has-text("下一页")',
        '.pagination a:has-text(">")',
        '.pagination a:has-text("›")',
        '.pagination a:has-text("»")',
        'a[rel="next"]',
        'li.next a',
    ]

    def __init__(self, page: Page, config: Optional[PaginationConfig] = None):
        self.page = page
        self.config = config or PaginationConfig()
        self._current_page = 1
        self._visited_pages: set = set()

    async def navigate(self) -> PaginationResult:
        """
        执行分页导航

        Returns:
            PaginationResult: 导航结果
        """
        import time
        start_time = time.time()

        self._current_page = 1
        self._visited_pages.clear()
        total_pages = 1
        reached_end = False
        stopped_by_condition = False

        while self._current_page <= self.config.max_pages:
            # 记录当前页
            self._visited_pages.add(self._current_page)

            # 检查停止条件
            if self.config.stop_condition:
                should_stop = self.config.stop_condition()
                if asyncio.iscoroutinefunction(self.config.stop_condition):
                    should_stop = await should_stop
                if should_stop:
                    stopped_by_condition = True
                    break

            # 翻页前回调
            if self.config.on_before_page:
                if asyncio.iscoroutinefunction(self.config.on_before_page):
                    await self.config.on_before_page(self._current_page)
                else:
                    self.config.on_before_page(self._current_page)

            # 尝试翻到下一页
            has_next_page = await self.go_to_next_page()

            if not has_next_page:
                reached_end = True
                break

            total_pages += 1
            self._current_page += 1

            # 翻页后回调
            if self.config.on_after_page:
                if asyncio.iscoroutinefunction(self.config.on_after_page):
                    await self.config.on_after_page(self._current_page)
                else:
                    self.config.on_after_page(self._current_page)

            # 等待页面加载
            await self._wait_for_page_load()

        duration = (time.time() - start_time) * 1000

        logger.debug(
            f"分页导航完成: pages={total_pages}, "
            f"reached_end={reached_end}, duration={duration:.0f}ms"
        )

        return PaginationResult(
            total_pages=total_pages,
            reached_end=reached_end,
            stopped_by_condition=stopped_by_condition,
            duration=duration,
        )

    async def navigate_all(
        self,
        next_selector: str,
        max_pages: int = 100,
        on_page: Optional[Callable[[int], None]] = None,
    ) -> PaginationResult:
        """
        遍历所有分页

        Args:
            next_selector: 下一页按钮选择器
            max_pages: 最大页数
            on_page: 每页回调函数

        Returns:
            PaginationResult: 导航结果
        """
        config = PaginationConfig(
            next_selector=next_selector,
            max_pages=max_pages,
            on_after_page=on_page,
        )
        old_config = self.config
        self.config = config
        result = await self.navigate()
        self.config = old_config
        return result

    async def extract_all_pages(
        self,
        item_selector: str,
        next_selector: str,
        max_pages: int = 100,
    ) -> list:
        """
        提取所有页面的数据

        Args:
            item_selector: 元素选择器
            next_selector: 下一页选择器
            max_pages: 最大页数

        Returns:
            list: 所有页面提取的数据
        """
        all_items = []

        async def extract_page(page_num: int):
            items = await self.page.locator(item_selector).all_text_contents()
            all_items.extend(items)

        config = PaginationConfig(
            next_selector=next_selector,
            max_pages=max_pages,
            on_after_page=extract_page,
        )
        old_config = self.config
        self.config = config
        await self.navigate()
        self.config = old_config

        return all_items

    async def go_to_next_page(self) -> bool:
        """翻到下一页"""
        next_button = self.page.locator(self.config.next_selector)

        # 检查下一页按钮是否存在且可点击
        try:
            is_visible = await next_button.is_visible()
        except Exception:
            return False

        if not is_visible:
            return False

        if await self._is_button_disabled(next_button):
            return False

        # 点击下一页
        await next_button.click()
        logger.debug(f"翻到下一页: {self._current_page + 1}")
        return True

    async def go_to_page(self, page_number: int) -> bool:
        """跳转到指定页"""
        if self.config.page_selector:
            page_button = self.page.locator(
                f'{self.config.page_selector}:has-text("{page_number}")'
            )

            try:
                is_visible = await page_button.is_visible()
            except Exception:
                return False

            if is_visible:
                await page_button.click()
                self._current_page = page_number
                await self._wait_for_page_load()
                return True

        return False

    async def go_to_first_page(self) -> bool:
        """跳转到第一页"""
        first_button = self.page.locator(
            'a.first, button.first, [aria-label="第一页"], [aria-label="First"]'
        )

        try:
            is_visible = await first_button.is_visible()
        except Exception:
            return False

        if is_visible:
            await first_button.click()
            self._current_page = 1
            await self._wait_for_page_load()
            return True

        return False

    async def go_to_last_page(self) -> bool:
        """跳转到最后一页"""
        last_button = self.page.locator(
            'a.last, button.last, [aria-label="最后一页"], [aria-label="Last"]'
        )

        try:
            is_visible = await last_button.is_visible()
        except Exception:
            return False

        if is_visible:
            await last_button.click()
            await self._wait_for_page_load()
            return True

        return False

    def get_current_page(self) -> int:
        """获取当前页码"""
        return self._current_page

    def get_visited_pages(self) -> List[int]:
        """获取已访问的页数"""
        return sorted(list(self._visited_pages))

    async def has_next_page(self) -> bool:
        """检测是否有下一页"""
        next_button = self.page.locator(self.config.next_selector)
        try:
            is_visible = await next_button.is_visible()
        except Exception:
            return False

        if not is_visible:
            return False
        return not await self._is_button_disabled(next_button)

    async def has_previous_page(self) -> bool:
        """检测是否有上一页"""
        prev_button = self.page.locator(
            'a.prev, button.prev, [aria-label="上一页"], [aria-label="Previous"]'
        )
        try:
            is_visible = await prev_button.is_visible()
        except Exception:
            return False

        if not is_visible:
            return False
        return not await self._is_button_disabled(prev_button)

    async def get_total_pages(self) -> Optional[int]:
        """获取总页数（如果页面显示）"""
        try:
            total_text = await self.page.locator(
                '.pagination-total, .page-total, [class*="total"]'
            ).text_content()

            if total_text:
                import re
                match = re.search(r'(\d+)', total_text)
                if match:
                    return int(match.group(1))
        except Exception:
            pass

        return None

    async def _is_button_disabled(self, button: Locator) -> bool:
        """检测按钮是否禁用"""
        try:
            class_name = await button.get_attribute("class") or ""
            disabled = await button.get_attribute("disabled")
            aria_disabled = await button.get_attribute("aria-disabled")

            return (
                disabled is not None or
                aria_disabled == "true" or
                "disabled" in class_name
            )
        except Exception:
            return True

    async def _wait_for_page_load(self):
        """等待页面加载"""
        await asyncio.sleep(self.config.wait_time)
        try:
            await self.page.wait_for_load_state("domcontentloaded", timeout=5000)
        except Exception:
            pass

    @classmethod
    async def detect_pagination_selector(cls, page: Page) -> Optional[str]:
        """
        自动检测分页选择器

        Args:
            page: 页面对象

        Returns:
            Optional[str]: 检测到的选择器，未找到返回 None
        """
        for selector in cls.NEXT_SELECTORS:
            try:
                element = page.locator(selector)
                is_visible = await element.is_visible()
                if is_visible:
                    return selector
            except Exception:
                continue

        return None


def create_pagination_navigator(
    page: Page,
    config: Optional[PaginationConfig] = None
) -> PaginationNavigator:
    """创建分页导航器实例"""
    return PaginationNavigator(page, config)
