import asyncio
import random
from abc import ABC, abstractmethod
from typing import Any, Optional

from loguru import logger
from playwright.async_api import Page
from yt_dlp import YoutubeDL
from yt_dlp.extractor.common import InfoExtractor


class PlatformActionableError(Exception):
    """Exception carrying structured details for upstream/platform issues."""

    def __init__(self, message: str = "", *, details: dict | None = None, code: str = "INTERNAL_ERROR"):
        super().__init__(message)
        self.details = details
        self.code = code


class RiskControlException(PlatformActionableError):
    """Exception raised when platform risk control is detected."""

    def __init__(self, message: str = "命中风控", *, details: dict | None = None):
        super().__init__(message, details=details, code="RISK_CONTROL_TRIGGERED")


class RobustnessMixin:
    """Mixin providing human-like interaction and risk detection."""
    page: Page

    async def human_scroll(self, container_locator=None, distance_range=(800, 1500), delay_range=(1.0, 2.5)):
        """Simulate human-like scrolling with random distance and delays."""
        distance = random.randint(*distance_range)
        logger.info(f"Human scroll: {distance}px")
        if container_locator:
            await container_locator.evaluate(f"el => el.scrollBy(0, {distance})")
        else:
            await self.page.mouse.wheel(0, distance)
        await asyncio.sleep(random.uniform(*delay_range))

    async def human_click(self, selector: str):
        """Simulate human-like click using ghost-cursor (pseudo-implementation)."""
        # Note: Actual ghost-cursor requires more complex setup, using a simplified version here
        # that moves the mouse before clicking.
        element = self.page.locator(selector).first
        if await element.is_visible():
            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 + random.uniform(-5, 5), y + random.uniform(-5, 5), steps=10)
                await asyncio.sleep(random.uniform(0.2, 0.5))
                await element.click()


class BaseExtractor(InfoExtractor, RobustnessMixin):
    """Base class for platform-specific data extraction."""

    def __init__(self, page: Page):
        super().__init__()
        self.page = page
        self.set_downloader(YoutubeDL({'quiet': True}))

    @abstractmethod
    async def get_self_info(self):
        """Extract information about the currently logged-in user."""
        pass

    @abstractmethod
    async def get_user_info(self, user_id: str):
        """Extract information about a specific user."""
        pass
