"""
分页爬取器
支持多种分页模式
"""
import re
from typing import Optional, List, Any, Dict, Callable
from playwright.async_api import Page
from loguru import logger

from .basic import DataExtractor


class PaginationExtractor:
    """
    分页爬取器
    支持多种分页模式
    """

    def __init__(self, page: Page):
        self.page = page
        self.extractor = DataExtractor(page)

    async def extract_with_pagination(
        self,
        item_selector: str,
        extract_func: Callable,
        next_selector: str,
        max_pages: int = 10,
        wait_after_click: float = 1.0,
        stop_condition: Optional[Callable] = None,
    ) -> List[Any]:
        """
        传统分页爬取

        Args:
            item_selector: 列表项选择器
            extract_func: 提取函数 async (item) -> dict
            next_selector: 下一页按钮选择器
            max_pages: 最大页数
            wait_after_click: 点击后等待时间
            stop_condition: 停止条件函数

        Returns:
            List: 所有页面的数据
        """
        all_data = []

        for page_num in range(max_pages):
            logger.info(f"正在爬取第 {page_num + 1} 页")

            # 提取当前页数据
            items = self.page.locator(item_selector)
            count = await items.count()

            for i in range(count):
                item = items.nth(i)
                data = await extract_func(item)
                if data:
                    all_data.append(data)

            # 检查停止条件
            if stop_condition and stop_condition(all_data):
                logger.info("满足停止条件，结束爬取")
                break

            # 点击下一页
            next_btn = self.page.locator(next_selector)
            if not await next_btn.is_visible():
                logger.info("没有更多页面")
                break

            await next_btn.click()
            await self.page.wait_for_timeout(int(wait_after_click * 1000))

        return all_data

    async def extract_with_load_more(
        self,
        item_selector: str,
        extract_func: Callable,
        load_more_selector: str,
        max_clicks: int = 20,
        wait_after_click: float = 1.5,
        stop_condition: Optional[Callable] = None,
    ) -> List[Any]:
        """
        点击加载更多模式

        Args:
            item_selector: 列表项选择器
            extract_func: 提取函数
            load_more_selector: 加载更多按钮选择器
            max_clicks: 最大点击次数
            wait_after_click: 点击后等待时间
            stop_condition: 停止条件

        Returns:
            List: 所有数据
        """
        all_data = []
        prev_count = 0

        for click_num in range(max_clicks):
            # 提取当前可见数据
            items = self.page.locator(item_selector)
            current_count = await items.count()

            # 只提取新增的数据
            for i in range(prev_count, current_count):
                item = items.nth(i)
                data = await extract_func(item)
                if data:
                    all_data.append(data)

            prev_count = current_count

            # 检查停止条件
            if stop_condition and stop_condition(all_data):
                logger.info("满足停止条件，结束爬取")
                break

            # 点击加载更多
            load_more = self.page.locator(load_more_selector)
            if not await load_more.is_visible():
                logger.info("加载更多按钮不可见")
                break

            await load_more.click()
            await self.page.wait_for_timeout(int(wait_after_click * 1000))

        return all_data

    async def extract_with_infinite_scroll(
        self,
        item_selector: str,
        extract_func: Callable,
        max_scrolls: int = 50,
        scroll_pause: float = 1.0,
        stop_condition: Optional[Callable] = None,
        scroll_distance: int = 1000,
    ) -> List[Any]:
        """
        无限滚动加载模式

        Args:
            item_selector: 列表项选择器
            extract_func: 提取函数
            max_scrolls: 最大滚动次数
            scroll_pause: 滚动后暂停时间
            stop_condition: 停止条件
            scroll_distance: 滚动距离

        Returns:
            List: 所有数据
        """
        all_data = []
        prev_count = 0
        no_new_items_count = 0

        for scroll_num in range(max_scrolls):
            # 提取当前可见数据
            items = self.page.locator(item_selector)
            current_count = await items.count()

            # 只提取新增的数据
            for i in range(prev_count, current_count):
                item = items.nth(i)
                data = await extract_func(item)
                if data:
                    all_data.append(data)

            # 检查是否有新数据
            if current_count == prev_count:
                no_new_items_count += 1
                if no_new_items_count >= 3:
                    logger.info("连续3次没有新数据，结束爬取")
                    break
            else:
                no_new_items_count = 0

            prev_count = current_count

            # 检查停止条件
            if stop_condition and stop_condition(all_data):
                logger.info("满足停止条件，结束爬取")
                break

            # 滚动页面
            await self.page.evaluate(f"window.scrollBy(0, {scroll_distance})")
            await self.page.wait_for_timeout(int(scroll_pause * 1000))

        return all_data

    async def extract_with_url_pattern(
        self,
        url_pattern: str,
        extract_func: Callable,
        start_page: int = 1,
        max_pages: int = 100,
        page_param: str = "page",
        stop_condition: Optional[Callable] = None,
    ) -> List[Any]:
        """
        URL分页模式（通过修改URL参数翻页）

        Args:
            url_pattern: URL模式，如 "https://example.com/list?page={page}"
            extract_func: 提取函数
            start_page: 起始页
            max_pages: 最大页数
            page_param: 页码参数名
            stop_condition: 停止条件

        Returns:
            List: 所有数据
        """
        all_data = []

        for page_num in range(start_page, max_pages + 1):
            url = url_pattern.format(page=page_num)
            logger.info(f"正在爬取: {url}")

            await self.page.goto(url)
            await self.page.wait_for_load_state("networkidle")

            # 提取数据
            data = await extract_func(self.page)

            if not data:
                logger.info("没有数据，结束爬取")
                break

            all_data.extend(data)

            # 检查停止条件
            if stop_condition and stop_condition(all_data):
                logger.info("满足停止条件，结束爬取")
                break

        return all_data

    async def extract_with_page_numbers(
        self,
        item_selector: str,
        extract_func: Callable,
        page_number_selector: str,
        current_page_selector: Optional[str] = None,
        max_pages: int = 100,
        wait_after_click: float = 1.0,
        stop_condition: Optional[Callable] = None,
    ) -> List[Any]:
        """
        点击页码模式（点击页码按钮翻页）

        Args:
            item_selector: 列表项选择器
            extract_func: 提取函数 async (item) -> dict
            page_number_selector: 页码按钮选择器
            current_page_selector: 当前页标识选择器
            max_pages: 最大页数
            wait_after_click: 点击后等待时间
            stop_condition: 停止条件

        Returns:
            List: 所有数据
        """
        all_data = []
        visited_pages = set()

        for _ in range(max_pages):
            # 获取当前页码
            current_page = 1
            if current_page_selector:
                try:
                    current_el = self.page.locator(current_page_selector)
                    current_text = await current_el.text_content()
                    current_page = int(re.search(r'\d+', current_text or "1").group())
                except:
                    pass

            if current_page in visited_pages:
                logger.info("已访问过当前页，结束爬取")
                break

            visited_pages.add(current_page)
            logger.info(f"正在爬取第 {current_page} 页")

            # 提取当前页数据
            items = self.page.locator(item_selector)
            count = await items.count()

            for i in range(count):
                item = items.nth(i)
                data = await extract_func(item)
                if data:
                    all_data.append(data)

            # 检查停止条件
            if stop_condition and stop_condition(all_data):
                logger.info("满足停止条件，结束爬取")
                break

            # 点击下一页码
            next_page = current_page + 1
            next_btn = self.page.locator(f"{page_number_selector}:text('{next_page}')")

            if not await next_btn.is_visible():
                logger.info("没有更多页面")
                break

            await next_btn.click()
            await self.page.wait_for_timeout(int(wait_after_click * 1000))

        return all_data

    async def extract_with_page_jump(
        self,
        item_selector: str,
        extract_func: Callable,
        page_input_selector: str,
        jump_button_selector: Optional[str] = None,
        start_page: int = 1,
        max_pages: int = 100,
        wait_after_jump: float = 1.5,
        stop_condition: Optional[Callable] = None,
    ) -> List[Any]:
        """
        页码跳转模式（输入页码跳转）

        Args:
            item_selector: 列表项选择器
            extract_func: 提取函数
            page_input_selector: 页码输入框选择器
            jump_button_selector: 跳转按钮选择器
            start_page: 起始页
            max_pages: 最大页数
            wait_after_jump: 跳转后等待时间
            stop_condition: 停止条件

        Returns:
            List: 所有数据
        """
        all_data = []

        for page_num in range(start_page, max_pages + 1):
            logger.info(f"正在爬取第 {page_num} 页")

            # 提取当前页数据
            items = self.page.locator(item_selector)
            count = await items.count()

            for i in range(count):
                item = items.nth(i)
                data = await extract_func(item)
                if data:
                    all_data.append(data)

            # 检查停止条件
            if stop_condition and stop_condition(all_data):
                logger.info("满足停止条件，结束爬取")
                break

            # 输入页码并跳转
            page_input = self.page.locator(page_input_selector)
            await page_input.fill(str(page_num + 1))

            if jump_button_selector:
                jump_btn = self.page.locator(jump_button_selector)
                await jump_btn.click()
            else:
                await page_input.press("Enter")

            await self.page.wait_for_timeout(int(wait_after_jump * 1000))

        return all_data

    async def extract_with_dropdown_pagination(
        self,
        item_selector: str,
        extract_func: Callable,
        dropdown_selector: str,
        max_pages: int = 100,
        stop_condition: Optional[Callable] = None,
    ) -> List[Any]:
        """
        下拉选择分页模式

        Args:
            item_selector: 列表项选择器
            extract_func: 提取函数
            dropdown_selector: 下拉框选择器
            max_pages: 最大页数
            stop_condition: 停止条件

        Returns:
            List: 所有数据
        """
        all_data = []

        for page_num in range(1, max_pages + 1):
            logger.info(f"正在爬取第 {page_num} 页")

            # 提取当前页数据
            items = self.page.locator(item_selector)
            count = await items.count()

            for i in range(count):
                item = items.nth(i)
                data = await extract_func(item)
                if data:
                    all_data.append(data)

            # 检查停止条件
            if stop_condition and stop_condition(all_data):
                logger.info("满足停止条件，结束爬取")
                break

            # 选择下一页
            dropdown = self.page.locator(dropdown_selector)
            try:
                await dropdown.select_option(value=str(page_num + 1))
            except:
                logger.info("无法选择下一页，结束爬取")
                break

        return all_data

    async def smart_paginate(
        self,
        item_selector: str,
        extract_func: Callable,
        max_pages: int = 10,
        pagination_info: Optional[Dict[str, Any]] = None,
        stop_condition: Optional[Callable] = None,
    ) -> List[Any]:
        """
        智能分页爬取（自动检测分页类型）

        Args:
            item_selector: 列表项选择器
            extract_func: 提取函数
            max_pages: 最大页数
            pagination_info: 分页信息（可选，自动检测如果为空）
            stop_condition: 停止条件

        Returns:
            List: 所有数据
        """
        # 自动检测分页类型
        if not pagination_info:
            pagination_info = await self._detect_pagination_type()

        if not pagination_info:
            logger.info("未检测到分页，尝试无限滚动模式")
            return await self.extract_with_infinite_scroll(
                item_selector=item_selector,
                extract_func=extract_func,
                max_scrolls=max_pages,
                stop_condition=stop_condition,
            )

        # 根据类型选择方法
        pagination_type = pagination_info.get('type', 'unknown')

        if pagination_type == 'next_button':
            return await self.extract_with_pagination(
                item_selector=item_selector,
                extract_func=extract_func,
                next_selector=pagination_info['selectors'].get('nextButton', '.next'),
                max_pages=max_pages,
                stop_condition=stop_condition,
            )

        elif pagination_type == 'page_numbers':
            return await self.extract_with_page_numbers(
                item_selector=item_selector,
                extract_func=extract_func,
                page_number_selector=pagination_info['selectors'].get('pageNumbers', '.pagination a'),
                current_page_selector=pagination_info['selectors'].get('currentPage'),
                max_pages=max_pages,
                stop_condition=stop_condition,
            )

        elif pagination_type == 'page_jump':
            return await self.extract_with_page_jump(
                item_selector=item_selector,
                extract_func=extract_func,
                page_input_selector=pagination_info['selectors'].get('pageInput', 'input[name="page"]'),
                jump_button_selector=pagination_info['selectors'].get('jumpButton'),
                max_pages=max_pages,
                stop_condition=stop_condition,
            )

        elif pagination_type == 'load_more':
            return await self.extract_with_load_more(
                item_selector=item_selector,
                extract_func=extract_func,
                load_more_selector=pagination_info['selectors'].get('loadMoreButton', '.load-more'),
                max_clicks=max_pages,
                stop_condition=stop_condition,
            )

        elif pagination_type == 'dropdown':
            return await self.extract_with_dropdown_pagination(
                item_selector=item_selector,
                extract_func=extract_func,
                dropdown_selector=pagination_info['selectors'].get('dropdown', 'select[name="page"]'),
                max_pages=max_pages,
                stop_condition=stop_condition,
            )

        else:
            logger.info("未检测到明确的分页类型，尝试无限滚动模式")
            return await self.extract_with_infinite_scroll(
                item_selector=item_selector,
                extract_func=extract_func,
                max_scrolls=max_pages,
                stop_condition=stop_condition,
            )

    async def _detect_pagination_type(self) -> Optional[Dict[str, Any]]:
        """检测页面的分页类型"""
        return await self.page.evaluate("""
            () => {
                // 检测下一页按钮
                const nextBtn = document.querySelector('.next, [rel="next"], .pagination .next, .pager-next');
                if (nextBtn) {
                    return { type: 'next_button', selectors: { nextButton: '.next' } };
                }

                // 检测页码
                const pageNumbers = document.querySelector('.pagination a, .page-numbers a, .pager a');
                if (pageNumbers) {
                    return { type: 'page_numbers', selectors: { pageNumbers: '.pagination a' } };
                }

                // 检测页码输入框
                const pageInput = document.querySelector('input[name="page"], input[placeholder*="页"]');
                if (pageInput) {
                    return { type: 'page_jump', selectors: { pageInput: 'input[name="page"]' } };
                }

                // 检测加载更多按钮
                const loadMore = document.querySelector('.load-more, .loadmore, [class*="load-more"]');
                if (loadMore) {
                    return { type: 'load_more', selectors: { loadMoreButton: '.load-more' } };
                }

                // 检测下拉分页
                const dropdown = document.querySelector('select[name="page"], select.page-select');
                if (dropdown) {
                    return { type: 'dropdown', selectors: { dropdown: 'select[name="page"]' } };
                }

                return null;
            }
        """)
