"""
网络工具
实现 WebSearch 和 WebFetch 功能
"""

from typing import Optional, Dict, Any, List
from pydantic import BaseModel, Field
import httpx
import re
from urllib.parse import urlparse

from .base import BaseTool
from core.logger import log_event


class WebSearchInput(BaseModel):
    """WebSearch 输入参数"""
    query: str = Field(..., description="搜索查询")
    max_results: int = Field(default=5, description="最大结果数量")


class DuckDuckGoEngine:
    """DuckDuckGo 搜索引擎实现"""

    async def search(self, query: str, max_results: int) -> List[Dict[str, str]]:
        """使用 DuckDuckGo 搜索"""
        try:
            url = "https://html.duckduckgo.com/html/"
            headers = {
                "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
            }

            async with httpx.AsyncClient(timeout=30.0) as client:
                response = await client.post(
                    url,
                    data={"q": query},
                    headers=headers,
                    follow_redirects=True,
                )

                if response.status_code != 200:
                    return []

                html = response.text

            results = []
            result_pattern = r'<a rel="nofollow" class="result__a" href="([^"]+)"[^>]*>([^<]+)</a>'
            snippet_pattern = r'<a class="result__snippet"[^>]*>([^<]+)</a>'

            matches = re.findall(result_pattern, html)
            snippets = re.findall(snippet_pattern, html)

            for i, (href, title) in enumerate(matches[:max_results]):
                result = {
                    "url": href,
                    "title": title.strip(),
                    "snippet": snippets[i].strip() if i < len(snippets) else "",
                }
                results.append(result)

            return results

        except Exception as e:
            log_event("duckduckgo_search_error", error=str(e))
            return []


class WebFetchInput(BaseModel):
    """WebFetch 输入参数"""
    url: str = Field(..., description="要获取的 URL")
    prompt: Optional[str] = Field(default=None, description="处理内容的提示词")
    max_length: int = Field(default=10000, description="最大内容长度")


class WebSearchTool(BaseTool):
    """网页搜索工具"""

    name = "websearch"
    description = """搜索网页获取信息。

使用场景：
- 查找最新的技术文档
- 搜索错误信息的解决方案
- 获取 API 文档和示例

注意：
- 搜索结果可能不是最新的
- 需要验证搜索结果的准确性
"""
    args_schema = WebSearchInput

    def __init__(self):
        self._engine = DuckDuckGoEngine()

    async def run(self, query: str, max_results: int = 5) -> str:
        """
        执行搜索

        Args:
            query: 搜索查询
            max_results: 最大结果数量

        Returns:
            搜索结果
        """
        results = await self._engine.search(query, max_results)

        if not results:
            return f"未找到关于 '{query}' 的搜索结果"

        output_lines = [f"搜索结果 (duckduckgo): {query}\n"]

        for i, result in enumerate(results, 1):
            output_lines.append(f"{i}. {result['title']}")
            output_lines.append(f"   URL: {result['url']}")
            if result.get('snippet'):
                output_lines.append(f"   摘要: {result['snippet']}")
            output_lines.append("")

        return "\n".join(output_lines)


class WebFetchTool(BaseTool):
    """网页获取工具"""

    name = "webfetch"
    description = """获取网页内容。

使用场景：
- 获取文档页面内容
- 读取 API 响应
- 获取代码示例

注意：
- 某些网站可能阻止爬取
- 内容会被截断以适应上下文限制
"""
    args_schema = WebFetchInput

    def __init__(self):
        self._cache: Dict[str, str] = {}
        self._cache_ttl = 900  # 15 分钟缓存

    async def run(
        self,
        url: str,
        prompt: Optional[str] = None,
        max_length: int = 10000,
    ) -> str:
        """
        获取网页内容

        Args:
            url: 要获取的 URL
            prompt: 处理内容的提示词（可选）
            max_length: 最大内容长度

        Returns:
            网页内容
        """
        # 验证 URL
        parsed = urlparse(url)
        if not parsed.scheme:
            url = "https://" + url
            parsed = urlparse(url)

        if parsed.scheme not in ["http", "https"]:
            return f"错误：不支持的 URL 协议: {parsed.scheme}"

        # 检查缓存
        if url in self._cache:
            content = self._cache[url]
        else:
            # 获取内容
            content = await self._fetch_url(url)
            if content:
                self._cache[url] = content

        if not content:
            return f"错误：无法获取 URL: {url}"

        # 转换为文本
        text = self._html_to_text(content)

        # 截断
        if len(text) > max_length:
            text = text[:max_length] + "\n\n[内容已截断...]"

        # 如果有提示词，添加到输出
        if prompt:
            return f"URL: {url}\n提示: {prompt}\n\n内容:\n{text}"

        return f"URL: {url}\n\n{text}"

    async def _fetch_url(self, url: str) -> Optional[str]:
        """获取 URL 内容"""
        headers = {
            "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",
            "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
            "Accept-Language": "en-US,en;q=0.5",
        }

        try:
            async with httpx.AsyncClient(timeout=30.0, follow_redirects=True) as client:
                response = await client.get(url, headers=headers)

                if response.status_code != 200:
                    return None

                # 检查内容类型
                content_type = response.headers.get("content-type", "")

                if "text/html" in content_type:
                    return response.text
                elif "application/json" in content_type:
                    return response.text
                elif "text/" in content_type:
                    return response.text
                else:
                    return f"[二进制内容: {content_type}]"

        except httpx.TimeoutException:
            return None
        except Exception as e:
            log_event("web_fetch_error", error=str(e))
            return None

    def _html_to_text(self, html: str) -> str:
        """将 HTML 转换为纯文本"""
        # 移除 script 和 style
        html = re.sub(r'<script[^>]*>.*?</script>', '', html, flags=re.DOTALL | re.IGNORECASE)
        html = re.sub(r'<style[^>]*>.*?</style>', '', html, flags=re.DOTALL | re.IGNORECASE)

        # 移除 HTML 注释
        html = re.sub(r'<!--.*?-->', '', html, flags=re.DOTALL)

        # 处理常见标签
        html = re.sub(r'<br\s*/?>', '\n', html, flags=re.IGNORECASE)
        html = re.sub(r'<p[^>]*>', '\n\n', html, flags=re.IGNORECASE)
        html = re.sub(r'</p>', '', html, flags=re.IGNORECASE)
        html = re.sub(r'<div[^>]*>', '\n', html, flags=re.IGNORECASE)
        html = re.sub(r'<h[1-6][^>]*>', '\n\n## ', html, flags=re.IGNORECASE)
        html = re.sub(r'</h[1-6]>', '\n', html, flags=re.IGNORECASE)
        html = re.sub(r'<li[^>]*>', '\n- ', html, flags=re.IGNORECASE)

        # 处理链接
        html = re.sub(r'<a[^>]*href="([^"]*)"[^>]*>([^<]*)</a>', r'\2 (\1)', html, flags=re.IGNORECASE)

        # 处理代码块
        html = re.sub(r'<pre[^>]*>', '\n```\n', html, flags=re.IGNORECASE)
        html = re.sub(r'</pre>', '\n```\n', html, flags=re.IGNORECASE)
        html = re.sub(r'<code[^>]*>', '`', html, flags=re.IGNORECASE)
        html = re.sub(r'</code>', '`', html, flags=re.IGNORECASE)

        # 移除所有其他标签
        html = re.sub(r'<[^>]+>', '', html)

        # 解码 HTML 实体
        html = html.replace('&nbsp;', ' ')
        html = html.replace('&lt;', '<')
        html = html.replace('&gt;', '>')
        html = html.replace('&amp;', '&')
        html = html.replace('&quot;', '"')
        html = html.replace('&#39;', "'")

        # 清理空白
        lines = html.split('\n')
        cleaned_lines = []
        for line in lines:
            line = line.strip()
            if line:
                cleaned_lines.append(line)

        text = '\n'.join(cleaned_lines)

        # 移除多余空行
        text = re.sub(r'\n{3,}', '\n\n', text)

        return text.strip()
