from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.common.exceptions import TimeoutException, WebDriverException
from webdriver_manager.chrome import ChromeDriverManager
import asyncio
import logging
from typing import Optional, List, Dict, Any
from contextlib import asynccontextmanager
import random
import time
from selenium.webdriver.chrome.options import Options as ChromeOptions
from selenium.webdriver.chrome.service import Service as ChromeService

logger = logging.getLogger(__name__)

class SeleniumManager:
    def __init__(self, headless: bool = True, proxy: Optional[str] = None):
        self.headless = headless
        self.proxy = proxy
        self.driver: Optional[webdriver.Chrome] = None
        self.wait: Optional[WebDriverWait] = None
        
    def _get_chrome_options(self) -> Options:
        """Get Chrome options for the driver"""
        
        options = ChromeOptions()
        
        if self.headless:
            options.add_argument('--headless=new')
        
        # Performance and security options
        options.add_argument('--no-sandbox')
        options.add_argument('--disable-dev-shm-usage')
        options.add_argument('--disable-gpu')
        options.add_argument('--disable-extensions')
        options.add_argument('--disable-plugins')
        # options.add_argument('--disable-images')
        # options.add_argument('--disable-javascript')  # Can be removed if JS is needed
        options.add_argument('--window-size=1920,1080')
        options.add_argument("--disable-blink-features=AutomationControlled")
        # 

        # User agent rotation
        user_agents = [
            'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36',
            'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36',
            'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'
        ]
        options.add_argument(f'--user-agent={random.choice(user_agents)}')
        
        # Proxy configuration
        if self.proxy:
            options.add_argument(f'--proxy-server={self.proxy}')
        
        # Additional anti-detection measures
        options.add_experimental_option("excludeSwitches", ["enable-automation"])
        options.add_experimental_option('useAutomationExtension', False)
        
        return options

    async def start_driver(self):
        """Start the Chrome driver"""
        try:
            options = self._get_chrome_options()
            # service = ChromeService(ChromeDriverManager().install())
            service = ChromeService('/usr/bin/chromedriver')
            
            self.driver = webdriver.Chrome(service=service, options=options)
            self.driver.execute_script("Object.defineProperty(navigator, 'webdriver', {get: () => undefined})")
            logger.info("OKAY........")
            self.wait = WebDriverWait(self.driver, 10)
            logger.info("OKAY 2 .......")
            logger.info("Chrome driver started successfully")
            
        except Exception as e:
            logger.error(f"Error starting Chrome driver: {e}")
            raise
    
    async def stop_driver(self):
        """Stop the Chrome driver"""
        if self.driver:
            try:
                self.driver.quit()
                logger.info("Chrome driver stopped")
            except Exception as e:
                logger.error(f"Error stopping Chrome driver: {e}")
    
    async def navigate_to(self, url: str, wait_for_element: Optional[str] = None):
        """Navigate to a URL"""
        try:
            self.driver.get(url)
            
            # Random delay to mimic human behavior
            await asyncio.sleep(random.uniform(1, 3))
            
            # Wait for specific element if provided
            if wait_for_element:
                self.wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, wait_for_element)))
            
            logger.debug(f"Navigated to: {url}")
            
        except TimeoutException:
            logger.warning(f"Timeout waiting for element: {wait_for_element}")
        except Exception as e:
            logger.error(f"Error navigating to {url}: {e}")
            raise
    
    async def find_elements(self, selector: str, timeout: int = 10) -> List[Any]:
        """Find elements by CSS selector"""
        try:
            wait = WebDriverWait(self.driver, timeout)
            elements = wait.until(EC.presence_of_all_elements_located((By.CSS_SELECTOR, selector)))
            return elements
        except TimeoutException:
            logger.warning(f"No elements found for selector: {selector}")
            return []
        except Exception as e:
            logger.error(f"Error finding elements: {e}")
            return []
    
    async def find_element(self, selector: str, timeout: int = 10) -> Optional[Any]:
        """Find single element by CSS selector"""
        try:
            wait = WebDriverWait(self.driver, timeout)
            element = wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, selector)))
            return element
        except TimeoutException:
            logger.warning(f"Element not found for selector: {selector}")
            return None
        except Exception as e:
            logger.error(f"Error finding element: {e}")
            return None
    
    async def scroll_page(self, pixels: int = 1000):
        """Scroll the page"""
        try:
            self.driver.execute_script(f"window.scrollBy(0, {pixels});")
            await asyncio.sleep(random.uniform(0.5, 1.5))
        except Exception as e:
            logger.error(f"Error scrolling page: {e}")
    
    async def wait_for_page_load(self):
        """Wait for page to fully load"""
        try:
            self.wait.until(lambda driver: driver.execute_script("return document.readyState") == "complete")
        except Exception as e:
            logger.error(f"Error waiting for page load: {e}")
    
    @asynccontextmanager
    async def driver_context(self):
        """Context manager for driver lifecycle"""
        await self.start_driver()
        try:
            yield self
        finally:
            await self.stop_driver()
