"""
Roura Agent Web Fetch Tool - Fetch URLs and extract text content.

© Roura.io
"""
from __future__ import annotations

import re
from dataclasses import dataclass, field
from typing import Optional
from urllib.parse import urlparse, urljoin

import httpx

from .base import Tool, ToolParam, ToolResult, RiskLevel, registry


# Default timeout for requests
DEFAULT_TIMEOUT = 30.0

# Maximum content size (10MB)
MAX_CONTENT_SIZE = 10 * 1024 * 1024

# User agent for requests
USER_AGENT = "Roura-Agent/1.0 (https://roura.io)"


def strip_html_tags(html: str) -> str:
    """
    Strip HTML tags and extract text content.

    Args:
        html: HTML content

    Returns:
        Plain text content
    """
    # Remove script and style elements
    html = re.sub(r'<script[^>]*>.*?</script>', '', html, flags=re.DOTALL | re.IGNORECASE)
    html = re.sub(r'<style[^>]*>.*?</style>', '', html, flags=re.DOTALL | re.IGNORECASE)

    # Remove HTML comments
    html = re.sub(r'<!--.*?-->', '', html, flags=re.DOTALL)

    # Replace common block elements with newlines
    html = re.sub(r'<(?:p|div|br|h[1-6]|li|tr)[^>]*>', '\n', html, flags=re.IGNORECASE)

    # Remove remaining HTML tags
    html = re.sub(r'<[^>]+>', '', html)

    # Decode common HTML entities
    html = html.replace('&nbsp;', ' ')
    html = html.replace('&lt;', '<')
    html = html.replace('&gt;', '>')
    html = html.replace('&amp;', '&')
    html = html.replace('&quot;', '"')
    html = html.replace('&#39;', "'")

    # Clean up whitespace
    lines = []
    for line in html.split('\n'):
        line = ' '.join(line.split())  # Normalize whitespace
        if line:
            lines.append(line)

    return '\n'.join(lines)


def extract_links_from_html(html: str, base_url: str) -> list[dict]:
    """
    Extract links from HTML content.

    Args:
        html: HTML content
        base_url: Base URL for resolving relative links

    Returns:
        List of link dictionaries with 'url' and 'text' keys
    """
    links = []
    pattern = r'<a[^>]+href=["\']([^"\']+)["\'][^>]*>(.*?)</a>'

    for match in re.finditer(pattern, html, re.IGNORECASE | re.DOTALL):
        href = match.group(1)
        text = strip_html_tags(match.group(2)).strip()

        # Skip empty or anchor-only links
        if not href or href.startswith('#') or href.startswith('javascript:'):
            continue

        # Resolve relative URLs
        try:
            full_url = urljoin(base_url, href)
            links.append({
                'url': full_url,
                'text': text[:100] if text else None,
            })
        except Exception:
            continue

    return links[:50]  # Limit to 50 links


def fetch_url(
    url: str,
    timeout: float = DEFAULT_TIMEOUT,
    extract_links: bool = False,
) -> dict:
    """
    Fetch a URL and return its content.

    Args:
        url: The URL to fetch
        timeout: Request timeout in seconds
        extract_links: Whether to extract links from HTML

    Returns:
        Dictionary with 'content', 'content_type', 'status_code', etc.
    """
    # Validate URL
    parsed = urlparse(url)
    if parsed.scheme not in ('http', 'https'):
        raise ValueError(f"Invalid URL scheme: {parsed.scheme}")

    headers = {
        'User-Agent': USER_AGENT,
        'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
    }

    with httpx.Client(timeout=timeout, follow_redirects=True) as client:
        response = client.get(url, headers=headers)

        content_type = response.headers.get('content-type', '').lower()
        content_length = int(response.headers.get('content-length', 0))

        # Check content size
        if content_length > MAX_CONTENT_SIZE:
            raise ValueError(f"Content too large: {content_length} bytes")

        # Get content
        raw_content = response.text

        # Process based on content type
        if 'application/json' in content_type:
            # Return JSON as-is
            return {
                'url': str(response.url),
                'status_code': response.status_code,
                'content_type': 'json',
                'content': raw_content[:50000],  # Limit size
                'links': [],
            }

        elif 'text/html' in content_type or 'application/xhtml' in content_type:
            # Extract text from HTML
            text_content = strip_html_tags(raw_content)

            result = {
                'url': str(response.url),
                'status_code': response.status_code,
                'content_type': 'html',
                'content': text_content[:50000],  # Limit size
                'links': [],
            }

            if extract_links:
                result['links'] = extract_links_from_html(raw_content, str(response.url))

            return result

        else:
            # Plain text or other
            return {
                'url': str(response.url),
                'status_code': response.status_code,
                'content_type': 'text',
                'content': raw_content[:50000],  # Limit size
                'links': [],
            }


@dataclass
class WebFetchTool(Tool):
    """Fetch a URL and extract text content."""

    name: str = "web.fetch"
    description: str = "Fetch a URL and extract text content (useful for documentation, APIs, etc.)"
    risk_level: RiskLevel = RiskLevel.SAFE
    parameters: list[ToolParam] = field(default_factory=lambda: [
        ToolParam("url", str, "The URL to fetch", required=True),
        ToolParam("extract_links", bool, "Whether to extract links from the page (default: False)", required=False, default=False),
    ])

    def execute(
        self,
        url: str,
        extract_links: bool = False,
    ) -> ToolResult:
        """Fetch a URL and extract content."""
        try:
            result = fetch_url(
                url=url,
                extract_links=extract_links,
            )

            return ToolResult(
                success=True,
                output={
                    'url': result['url'],
                    'status_code': result['status_code'],
                    'content_type': result['content_type'],
                    'content_length': len(result['content']),
                    'content': result['content'],
                    'links': result['links'] if extract_links else [],
                },
            )

        except httpx.TimeoutException:
            return ToolResult(
                success=False,
                output=None,
                error="Request timed out",
            )
        except httpx.HTTPStatusError as e:
            return ToolResult(
                success=False,
                output=None,
                error=f"HTTP error: {e.response.status_code}",
            )
        except ValueError as e:
            return ToolResult(
                success=False,
                output=None,
                error=str(e),
            )
        except Exception as e:
            return ToolResult(
                success=False,
                output=None,
                error=str(e),
            )

    def dry_run(self, url: str, extract_links: bool = False) -> str:
        """Describe what would be fetched."""
        return f"Would fetch: {url}"


@dataclass
class WebSearchTool(Tool):
    """
    Search the web (placeholder - requires API key).

    Note: This is a placeholder. Real implementation would use
    a search API like DuckDuckGo, Brave Search, or similar.
    """

    name: str = "web.search"
    description: str = "Search the web (requires configuration)"
    risk_level: RiskLevel = RiskLevel.SAFE
    parameters: list[ToolParam] = field(default_factory=lambda: [
        ToolParam("query", str, "Search query", required=True),
        ToolParam("max_results", int, "Maximum results (default: 5)", required=False, default=5),
    ])

    def execute(
        self,
        query: str,
        max_results: int = 5,
    ) -> ToolResult:
        """Search the web."""
        # Placeholder - would need a search API
        return ToolResult(
            success=False,
            output=None,
            error="Web search not configured. Use web.fetch with a specific URL instead.",
        )

    def dry_run(self, query: str, max_results: int = 5) -> str:
        return f"Would search for: {query}"


# Create and register tool instances
web_fetch = WebFetchTool()
web_search = WebSearchTool()

registry.register(web_fetch)
registry.register(web_search)


# Convenience function
def fetch_webpage(url: str, extract_links: bool = False) -> ToolResult:
    """Fetch a URL and extract text content."""
    return web_fetch.execute(url=url, extract_links=extract_links)
