"""
Playwright-based renderer with anti-detection and proxy support.
"""

import asyncio
import logging
import random
from typing import Optional, Dict, Any
from .render_proxy import ProxyManager

logger = logging.getLogger("iploop.renderer")

try:
    from playwright.async_api import async_playwright, Browser, BrowserContext, Page
    PLAYWRIGHT_AVAILABLE = True
except ImportError:
    PLAYWRIGHT_AVAILABLE = False
    async_playwright = None


class RenderError(Exception):
    """Rendering-related error."""
    pass


class Renderer:
    """Playwright-based renderer with anti-detection."""
    
    def __init__(self, api_key: str, headless: bool = True, timeout: int = 30):
        if not PLAYWRIGHT_AVAILABLE:
            raise RenderError(
                "Playwright not installed. Run: pip install iploop[render] or pip install playwright"
            )
        
        self.api_key = api_key
        self.headless = headless
        self.timeout = timeout * 1000  # Convert to milliseconds
        self.proxy_manager = ProxyManager()
        self._browser: Optional[Browser] = None
        self._context: Optional[BrowserContext] = None
        self._proxy_port: Optional[int] = None
    
    async def _get_anti_detection_args(self) -> list:
        """Get Chrome args for anti-detection."""
        return [
            '--disable-blink-features=AutomationControlled',
            '--disable-web-security',
            '--disable-dev-shm-usage',
            '--no-sandbox',
            '--disable-setuid-sandbox',
            '--disable-gpu',
            '--disable-background-timer-throttling',
            '--disable-backgrounding-occluded-windows',
            '--disable-renderer-backgrounding',
            '--disable-features=TranslateUI',
            '--disable-extensions',
            '--disable-plugins',
            '--disable-default-apps',
            '--disable-popup-blocking',
            '--disable-notifications',
            '--disable-infobars',
            '--user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36'
        ]
    
    async def _inject_anti_detection(self, page: Page):
        """Inject anti-detection JavaScript."""
        await page.add_init_script("""
            // Remove webdriver property
            Object.defineProperty(navigator, 'webdriver', {
                get: () => undefined,
            });
            
            // Mock chrome object
            window.chrome = {
                runtime: {},
                app: {
                    isInstalled: false,
                },
            };
            
            // Mock plugins
            Object.defineProperty(navigator, 'plugins', {
                get: () => [
                    {
                        0: {type: "application/x-google-chrome-pdf", suffixes: "pdf", description: "Portable Document Format"},
                        description: "Portable Document Format",
                        filename: "internal-pdf-viewer",
                        length: 1,
                        name: "Chrome PDF Plugin"
                    },
                    {
                        0: {type: "application/pdf", suffixes: "pdf", description: "Portable Document Format"},
                        description: "Portable Document Format", 
                        filename: "mhjfbmdgcfjbbpaeojofohoefgiehjai",
                        length: 1,
                        name: "Chrome PDF Viewer"
                    }
                ],
            });
            
            // Mock languages
            Object.defineProperty(navigator, 'languages', {
                get: () => ['en-US', 'en'],
            });
            
            // Mock permissions
            const originalQuery = window.navigator.permissions.query;
            window.navigator.permissions.query = (parameters) => (
                parameters.name === 'notifications' ?
                    Promise.resolve({ state: Notification.permission }) :
                    originalQuery(parameters)
            );
        """)
    
    async def _start_browser(self):
        """Start the browser with proxy."""
        # Get proxy port
        self._proxy_port = await self.proxy_manager.get_proxy_port(self.api_key)
        
        # Launch browser
        playwright = await async_playwright().start()
        
        self._browser = await playwright.chromium.launch(
            headless=self.headless,
            args=await self._get_anti_detection_args(),
            proxy={
                "server": f"http://127.0.0.1:{self._proxy_port}"
            }
        )
        
        # Create context with realistic settings
        self._context = await self._browser.new_context(
            viewport={'width': 1920, 'height': 1080},
            locale='en-US',
            timezone_id='America/New_York',
            user_agent='Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
            java_script_enabled=True,
            permissions=['geolocation'],
            extra_http_headers={
                'Accept-Language': 'en-US,en;q=0.9',
                'Accept-Encoding': 'gzip, deflate, br',
                'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8',
                'Cache-Control': 'no-cache',
                'Pragma': 'no-cache',
                'Sec-Fetch-Dest': 'document',
                'Sec-Fetch-Mode': 'navigate',
                'Sec-Fetch-Site': 'none',
                'Sec-Fetch-User': '?1',
                'Upgrade-Insecure-Requests': '1'
            }
        )
    
    async def render(self, url: str, wait_for: Optional[str] = None, wait_time: Optional[int] = None) -> str:
        """
        Render a URL and return the full HTML.
        
        Args:
            url: URL to render
            wait_for: CSS selector to wait for (optional)
            wait_time: Time to wait in seconds (optional, default 3)
            
        Returns:
            Full HTML content after JavaScript execution
        """
        if not self._browser:
            await self._start_browser()
        
        page = await self._context.new_page()
        
        try:
            # Inject anti-detection
            await self._inject_anti_detection(page)
            
            # Navigate to URL — use domcontentloaded (faster, more reliable than networkidle)
            response = await page.goto(url, wait_until='domcontentloaded', timeout=self.timeout)
            
            if not response or response.status >= 400:
                logger.warning(f"Page load failed: {response.status if response else 'No response'}")
            
            # Wait for JS to render content
            wait_secs = wait_time or 5
            await asyncio.sleep(wait_secs)
            
            # Wait for specific element if specified
            if wait_for:
                try:
                    await page.wait_for_selector(wait_for, timeout=self.timeout)
                except Exception as e:
                    logger.debug(f"Wait for selector '{wait_for}' failed: {e}")
            
            # Get the full HTML
            html = await page.content()
            return html
            
        except Exception as e:
            logger.error(f"Rendering failed for {url}: {e}")
            raise RenderError(f"Failed to render {url}: {e}")
        finally:
            await page.close()
    
    async def screenshot(self, url: str, path: Optional[str] = None, full_page: bool = True) -> bytes:
        """Take a screenshot of a URL."""
        if not self._browser:
            await self._start_browser()
        
        page = await self._context.new_page()
        
        try:
            await self._inject_anti_detection(page)
            await page.goto(url, wait_until='networkidle', timeout=self.timeout)
            await asyncio.sleep(2)  # Let page settle
            
            screenshot = await page.screenshot(path=path, full_page=full_page)
            return screenshot
            
        finally:
            await page.close()
    
    async def close(self):
        """Clean up browser and proxy."""
        if self._browser:
            await self._browser.close()
            self._browser = None
            self._context = None
        
        if self._proxy_port:
            await self.proxy_manager.cleanup_proxy(self.api_key)
            self._proxy_port = None


class RendererManager:
    """Manages renderer instances."""
    
    _renderers: Dict[str, Renderer] = {}
    
    @classmethod
    async def get_renderer(cls, api_key: str, **kwargs) -> Renderer:
        """Get or create a renderer for the API key."""
        if api_key not in cls._renderers:
            cls._renderers[api_key] = Renderer(api_key, **kwargs)
        return cls._renderers[api_key]
    
    @classmethod
    async def cleanup_renderer(cls, api_key: str):
        """Clean up a specific renderer."""
        if api_key in cls._renderers:
            await cls._renderers[api_key].close()
            del cls._renderers[api_key]
    
    @classmethod
    async def cleanup_all(cls):
        """Clean up all renderers."""
        for renderer in list(cls._renderers.values()):
            await renderer.close()
        cls._renderers.clear()


def render_url(api_key: str, url: str, **kwargs) -> str:
    """Synchronous wrapper for rendering a URL."""
    async def _render():
        renderer = await RendererManager.get_renderer(api_key)
        return await renderer.render(url, **kwargs)
    
    try:
        # Try to use existing event loop
        loop = asyncio.get_event_loop()
        if loop.is_running():
            # Create a new thread for the async operation
            import concurrent.futures
            with concurrent.futures.ThreadPoolExecutor() as executor:
                future = executor.submit(asyncio.run, _render())
                return future.result()
        else:
            return loop.run_until_complete(_render())
    except RuntimeError:
        # No event loop, create one
        return asyncio.run(_render())