import asyncio
from urllib.parse import urlparse
from meta_researcher.tool.tools.search_engine.log import logger
import httpx
from asyncache import cached as acached
from cachetools import TTLCache

from meta_researcher.tool.tools.search_engine.base_search import BaseSearch, SearchConfig


class BingSearch(BaseSearch):
    def __init__(self, args: SearchConfig):
        super().__init__(args)
        self.topk = 10
        self.name = "BingSearch"

    async def _search(self, query: str) -> dict:
        bing_search_url = "https://api.bing.microsoft.com/v7.0/search"
        params = {"q": query, "mkt": "zh-CN"}
        headers = {"Ocp-Apim-Subscription-Key": self.args.bing_search_key}
        async with httpx.AsyncClient(timeout=None) as client:
            response = await client.get(bing_search_url, headers=headers, params=params)
            response.raise_for_status()
            return response.json()

    def _parse_response(self, response) -> list[dict]:
        webpages = {w["id"]: w for w in response.get("webPages", {}).get("value", [])}
        results = []
        for item in response.get("rankingResponse", {}).get("mainline", {}).get("items", {}):
            if item["answerType"] == "WebPages":
                webpage = webpages.get(item["value"]["id"])
                if webpage:
                    url_info = urlparse(webpage["url"])
                    icon = f"{url_info.scheme}://{url_info.netloc}/favicon.ico"
                    site_name = webpage.get("siteName", "")
                    if not site_name:
                        site_name = url_info.netloc
                    results.append(
                        {
                            "url": webpage["url"],
                            "summ": webpage.get("snippet", ""),
                            "title": webpage.get("name", ""),
                            "site_name": site_name,
                            "icon": icon,
                            "published_date": webpage.get("datePublishedDisplayText", ""),
                            "meta": {
                                "from": self.name,
                            },
                        }
                    )
        return self._filter_results(results)

    @acached(cache=TTLCache(maxsize=100, ttl=600))
    async def search(self, query: str) -> list[dict]:
        for attempt in range(self.args.max_retry):
            try:
                print(f"Begin search web for {query}")
                response = await self._search(query)
                if len(response.get("rankingResponse", {}).get("mainline", {}).get("items", {})) == 0:
                    raise ValueError(f"call {self.name} failed.")
                return self._parse_response(response)
            except Exception as e:
                logger.warning(str(e))
                await asyncio.sleep(1)
        raise Exception("Failed to get search results from Bing Search after retries.")


async def test_main():
    searcher = BingSearch(SearchConfig(bing_search_key="54923f91b20542b08b8b1c5e6ec8bd48"))
    results = await searcher.search("驻北马其顿使馆临时代办徐磊比托拉圣克莱门特大学嘉奖仪式 日期")
    print(results)


if __name__ == "__main__":
    asyncio.run(test_main())
