"""Anti-detection patterns for browser automation.

Human-like interaction patterns to avoid bot detection on recurring tasks:
- Random delays between actions
- Natural scrolling with variable speed
- Bezier curve mouse movements
- Consistent browser fingerprint
- Warm-up browsing before taking actions
"""

from __future__ import annotations

import asyncio
import math
import random
from typing import Any


async def human_delay(min_ms: int = 300, max_ms: int = 1500) -> None:
    """Wait a random human-like duration between actions."""
    delay = random.uniform(min_ms / 1000, max_ms / 1000)
    await asyncio.sleep(delay)


async def human_type_delay() -> None:
    """Brief delay between keystrokes, simulating human typing speed."""
    await asyncio.sleep(random.uniform(0.03, 0.12))


async def natural_scroll(page: Any, direction: str = "down", amount: int = 600) -> None:
    """Scroll with variable speed and occasional pauses, like a human."""
    remaining = amount
    while remaining > 0:
        chunk = min(remaining, random.randint(80, 200))
        delta = -chunk if direction == "up" else chunk
        await page.mouse.wheel(0, delta)
        remaining -= chunk
        # Random micro-pause between scroll chunks
        await asyncio.sleep(random.uniform(0.02, 0.08))

    # Occasional longer pause after scrolling (reading time)
    if random.random() < 0.3:
        await asyncio.sleep(random.uniform(0.5, 1.5))


async def move_mouse_naturally(page: Any, target_x: float, target_y: float) -> None:
    """Move mouse along a bezier curve to target coordinates.

    Humans don't teleport the mouse — they move in smooth curves.
    Uses a quadratic bezier with a random control point.
    """
    try:
        # Get current mouse position (approximate from viewport center if unknown)
        current = await page.evaluate("""() => ({
            x: window.__lastMouseX || window.innerWidth / 2,
            y: window.__lastMouseY || window.innerHeight / 2,
        })""")
        start_x = current.get("x", 640)
        start_y = current.get("y", 360)
    except Exception:
        start_x, start_y = 640, 360

    # Random control point for bezier curve
    mid_x = (start_x + target_x) / 2 + random.uniform(-100, 100)
    mid_y = (start_y + target_y) / 2 + random.uniform(-50, 50)

    steps = random.randint(8, 20)
    for i in range(1, steps + 1):
        t = i / steps
        # Quadratic bezier
        x = (1 - t) ** 2 * start_x + 2 * (1 - t) * t * mid_x + t ** 2 * target_x
        y = (1 - t) ** 2 * start_y + 2 * (1 - t) * t * mid_y + t ** 2 * target_y
        await page.mouse.move(x, y)
        await asyncio.sleep(random.uniform(0.005, 0.02))

    # Track position for next movement
    try:
        await page.evaluate(
            f"() => {{ window.__lastMouseX = {target_x}; window.__lastMouseY = {target_y}; }}"
        )
    except Exception:
        pass


async def warm_up_browsing(page: Any, duration_seconds: float = 3.0) -> None:
    """Simulate natural browsing behavior before starting real actions.

    Scrolls a bit, moves the mouse, waits — like a human who just opened the page.
    """
    start = asyncio.get_event_loop().time()

    while asyncio.get_event_loop().time() - start < duration_seconds:
        action = random.choice(["scroll", "mouse", "wait"])

        if action == "scroll":
            direction = random.choice(["down", "down", "up"])  # Bias toward down
            amount = random.randint(100, 400)
            await natural_scroll(page, direction, amount)

        elif action == "mouse":
            try:
                viewport = await page.evaluate(
                    "() => ({w: window.innerWidth, h: window.innerHeight})"
                )
                x = random.uniform(100, viewport.get("w", 1280) - 100)
                y = random.uniform(100, viewport.get("h", 720) - 100)
                await move_mouse_naturally(page, x, y)
            except Exception:
                pass

        else:
            await asyncio.sleep(random.uniform(0.5, 1.5))

        await asyncio.sleep(random.uniform(0.3, 0.8))


async def apply_stealth_scripts(page: Any) -> None:
    """Inject stealth scripts to avoid bot detection.

    Patches common detection vectors:
    - navigator.webdriver
    - chrome.runtime
    - Permissions API
    - Plugin/language arrays
    """
    stealth_js = """() => {
        // Remove webdriver flag
        Object.defineProperty(navigator, 'webdriver', {
            get: () => undefined,
        });

        // Fake plugins
        Object.defineProperty(navigator, 'plugins', {
            get: () => [
                {name: 'Chrome PDF Plugin', filename: 'internal-pdf-viewer'},
                {name: 'Chrome PDF Viewer', filename: 'mhjfbmdgcfjbbpaeojofohoefgiehjai'},
                {name: 'Native Client', filename: 'internal-nacl-plugin'},
            ],
        });

        // Fake languages
        Object.defineProperty(navigator, 'languages', {
            get: () => ['en-US', 'en'],
        });

        // Patch permissions query
        const origQuery = window.navigator.permissions?.query;
        if (origQuery) {
            window.navigator.permissions.query = (params) => {
                if (params.name === 'notifications') {
                    return Promise.resolve({state: Notification.permission});
                }
                return origQuery.call(window.navigator.permissions, params);
            };
        }

        // Hide automation indicators
        if (window.chrome) {
            window.chrome.runtime = window.chrome.runtime || {};
        }
    }""".strip()

    try:
        await page.evaluate(stealth_js)
    except Exception:
        pass


class StealthConfig:
    """Configuration for anti-detection behavior per domain."""

    def __init__(
        self,
        min_action_delay_ms: int = 300,
        max_action_delay_ms: int = 1500,
        enable_mouse_curves: bool = True,
        enable_natural_scroll: bool = True,
        enable_warm_up: bool = True,
        warm_up_duration_s: float = 2.0,
        enable_stealth_scripts: bool = True,
    ):
        self.min_action_delay_ms = min_action_delay_ms
        self.max_action_delay_ms = max_action_delay_ms
        self.enable_mouse_curves = enable_mouse_curves
        self.enable_natural_scroll = enable_natural_scroll
        self.enable_warm_up = enable_warm_up
        self.warm_up_duration_s = warm_up_duration_s
        self.enable_stealth_scripts = enable_stealth_scripts

    @classmethod
    def for_domain(cls, domain: str) -> StealthConfig:
        """Get appropriate stealth config for a domain.

        Some domains are more aggressive with bot detection and need
        longer delays and more careful behavior.
        """
        strict_domains = {
            "linkedin.com", "instagram.com", "facebook.com",
            "twitter.com", "x.com",
        }
        moderate_domains = {
            "alibaba.com", "amazon.com", "ebay.com",
            "gmail.com", "mail.google.com",
        }

        clean = domain.lower().strip().removeprefix("www.")

        if clean in strict_domains:
            return cls(
                min_action_delay_ms=1000,
                max_action_delay_ms=3000,
                warm_up_duration_s=4.0,
            )
        elif clean in moderate_domains:
            return cls(
                min_action_delay_ms=500,
                max_action_delay_ms=2000,
                warm_up_duration_s=3.0,
            )
        return cls()  # defaults
