"""
Web Browser Tool

This tool provides web browsing capabilities including navigation,
content extraction, and basic interaction with web pages.
"""

import requests
from bs4 import BeautifulSoup
import time
from typing import Dict, Any, List, Optional
from urllib.parse import urljoin, urlparse
from tools.tool_registry import BaseTool


class WebBrowserTool(BaseTool):
    """Tool for web browsing and content extraction"""
    
    def __init__(self):
        super().__init__(
            name="web_browser_tool",
            description="Browse websites, extract content, and interact with web pages. Supports navigation, content extraction, and basic web scraping."
        )
        self.session = requests.Session()
        self.session.headers.update({
            'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'
        })
        self.current_url = None
        self.current_content = None
    
    def execute(self, query: str, context: Dict[str, Any]) -> Any:
        """
        Execute web browsing based on query
        
        Args:
            query: User query containing URL or browsing instruction
            context: Task analysis and other context
            
        Returns:
            Web browsing result including content and metadata
        """
        # Extract URL from query
        url = self._extract_url(query)
        
        if not url:
            return "No valid URL found in the query. Please provide a URL to browse."
        
        # Determine action based on query
        if 'extract' in query.lower() or 'scrape' in query.lower():
            return self._extract_content(url, query)
        elif 'navigate' in query.lower() or 'visit' in query.lower() or 'go to' in query.lower():
            return self._navigate_to_url(url)
        else:
            # Default action: navigate and extract basic content
            return self._navigate_and_extract(url)
    
    def _extract_url(self, query: str) -> Optional[str]:
        """Extract URL from query text"""
        import re
        
        # Look for URLs in the query
        url_pattern = r'https?://[^\s<>"{}|\\^`\[\]]+'
        urls = re.findall(url_pattern, query)
        
        if urls:
            return urls[0]
        
        # Look for domain names that might be URLs
        domain_pattern = r'\b(?:[a-zA-Z0-9-]+\.)+[a-zA-Z]{2,}\b'
        domains = re.findall(domain_pattern, query)
        
        if domains:
            # Add https:// if not present
            domain = domains[0]
            if not domain.startswith(('http://', 'https://')):
                return f"https://{domain}"
            return domain
        
        return None
    
    def _navigate_to_url(self, url: str) -> Dict[str, Any]:
        """Navigate to a URL and return basic information"""
        try:
            response = self.session.get(url, timeout=10)
            response.raise_for_status()
            
            self.current_url = url
            self.current_content = response.text
            
            # Parse with BeautifulSoup
            soup = BeautifulSoup(response.text, 'html.parser')
            
            # Extract basic information
            title = soup.find('title')
            title_text = title.get_text().strip() if title else "No title"
            
            # Get meta description
            meta_desc = soup.find('meta', attrs={'name': 'description'})
            description = meta_desc.get('content', '') if meta_desc else ''
            
            return {
                "success": True,
                "url": url,
                "title": title_text,
                "description": description,
                "status_code": response.status_code,
                "content_length": len(response.text),
                "content_type": response.headers.get('content-type', ''),
                "message": f"Successfully navigated to {url}"
            }
            
        except requests.exceptions.RequestException as e:
            return {
                "success": False,
                "url": url,
                "error": str(e),
                "message": f"Failed to navigate to {url}: {str(e)}"
            }
    
    def _extract_content(self, url: str, query: str) -> Dict[str, Any]:
        """Extract specific content from a URL based on query"""
        try:
            response = self.session.get(url, timeout=10)
            response.raise_for_status()
            
            soup = BeautifulSoup(response.text, 'html.parser')
            
            # Determine what to extract based on query
            extracted_content = {}
            
            if 'title' in query.lower():
                title = soup.find('title')
                extracted_content['title'] = title.get_text().strip() if title else None
            
            if 'text' in query.lower() or 'content' in query.lower():
                # Extract main text content
                for script in soup(["script", "style"]):
                    script.decompose()
                text = soup.get_text()
                lines = (line.strip() for line in text.splitlines())
                chunks = (phrase.strip() for line in lines for phrase in line.split("  "))
                extracted_content['text'] = '\n'.join(chunk for chunk in chunks if chunk)
            
            if 'links' in query.lower():
                links = []
                for link in soup.find_all('a', href=True):
                    href = link['href']
                    text = link.get_text().strip()
                    full_url = urljoin(url, href)
                    links.append({"text": text, "url": full_url})
                extracted_content['links'] = links
            
            if 'images' in query.lower():
                images = []
                for img in soup.find_all('img', src=True):
                    src = img['src']
                    alt = img.get('alt', '')
                    full_url = urljoin(url, src)
                    images.append({"alt": alt, "url": full_url})
                extracted_content['images'] = images
            
            if 'headings' in query.lower():
                headings = []
                for i in range(1, 7):
                    for heading in soup.find_all(f'h{i}'):
                        headings.append({
                            "level": i,
                            "text": heading.get_text().strip()
                        })
                extracted_content['headings'] = headings
            
            # If no specific extraction requested, get general content
            if not extracted_content:
                title = soup.find('title')
                extracted_content = {
                    'title': title.get_text().strip() if title else None,
                    'text': soup.get_text()[:2000] + "..." if len(soup.get_text()) > 2000 else soup.get_text()
                }
            
            return {
                "success": True,
                "url": url,
                "extracted_content": extracted_content,
                "message": f"Successfully extracted content from {url}"
            }
            
        except requests.exceptions.RequestException as e:
            return {
                "success": False,
                "url": url,
                "error": str(e),
                "message": f"Failed to extract content from {url}: {str(e)}"
            }
    
    def _navigate_and_extract(self, url: str) -> Dict[str, Any]:
        """Navigate to URL and extract basic content"""
        nav_result = self._navigate_to_url(url)
        
        if not nav_result["success"]:
            return nav_result
        
        # Extract basic content
        try:
            soup = BeautifulSoup(self.current_content, 'html.parser')
            
            # Remove script and style elements
            for script in soup(["script", "style"]):
                script.decompose()
            
            # Get text content
            text = soup.get_text()
            lines = (line.strip() for line in text.splitlines())
            chunks = (phrase.strip() for line in lines for phrase in line.split("  "))
            clean_text = '\n'.join(chunk for chunk in chunks if chunk)
            
            # Limit text length for readability
            if len(clean_text) > 3000:
                clean_text = clean_text[:3000] + "\n\n[Content truncated...]"
            
            nav_result["extracted_text"] = clean_text
            nav_result["message"] += " and extracted content"
            
            return nav_result
            
        except Exception as e:
            nav_result["extraction_error"] = str(e)
            return nav_result
    
    def get_capabilities(self) -> List[str]:
        """Return capabilities of this tool"""
        return [
            "web_navigation",
            "content_extraction",
            "web_scraping",
            "link_discovery",
            "image_discovery",
            "text_extraction"
        ]
    
    def validate_input(self, query: str, context: Dict[str, Any]) -> bool:
        """Validate input for web browsing"""
        # Check if query contains a valid URL or domain
        url = self._extract_url(query)
        if not url:
            return False
        
        # Basic URL validation
        parsed = urlparse(url)
        if not parsed.scheme or not parsed.netloc:
            return False
        
        return True
    
    def get_current_url(self) -> Optional[str]:
        """Get the currently loaded URL"""
        return self.current_url
    
    def search_in_content(self, search_term: str) -> List[str]:
        """Search for a term in the current page content"""
        if not self.current_content:
            return []
        
        soup = BeautifulSoup(self.current_content, 'html.parser')
        text = soup.get_text().lower()
        
        results = []
        lines = text.split('\n')
        for i, line in enumerate(lines):
            if search_term.lower() in line:
                # Get some context around the match
                start = max(0, i-2)
                end = min(len(lines), i+3)
                context = '\n'.join(lines[start:end])
                results.append(context)
        
        return results

