#
#  Copyright 2024 The InfiniFlow Authors. All Rights Reserved.
#
#  Licensed under the Apache License, Version 2.0 (the "License");
#  you may not use this file except in compliance with the License.
#  You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
#  Unless required by applicable law or agreed to in writing, software
#  distributed under the License is distributed on an "AS IS" BASIS,
#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#  See the License for the specific language governing permissions and
#  limitations under the License.
#
import logging
import os
import time
from abc import ABC
import wikipedia
from agent.tools.base import ToolMeta, ToolParamBase, ToolBase
from common.connection_utils import timeout


class WikipediaParam(ToolParamBase):
    """
    Define the Wikipedia component parameters.
    """

    def __init__(self):
        self.meta: ToolMeta = {
            "name": "wikipedia_search",
            "description": """A wide range of how-to and information pages are made available in wikipedia. Since 2001, it has grown rapidly to become the world's largest reference website. From Wikipedia, the free encyclopedia.""",
            "parameters": {
                "query": {
                    "type": "string",
                    "description": "The search keyword to execute with wikipedia. The keyword MUST be a specific subject that can match the title.",
                    "default": "{sys.query}",
                    "required": True,
                }
            },
        }
        super().__init__()
        self.top_n = 10
        self.language = "en"

    def check(self):
        self.check_positive_integer(self.top_n, "Top N")
        self.check_valid_value(
            self.language,
            "Wikipedia languages",
            [
                "af",
                "pl",
                "ar",
                "ast",
                "az",
                "bg",
                "nan",
                "bn",
                "be",
                "ca",
                "cs",
                "cy",
                "da",
                "de",
                "et",
                "el",
                "en",
                "es",
                "eo",
                "eu",
                "fa",
                "fr",
                "gl",
                "ko",
                "hy",
                "hi",
                "hr",
                "id",
                "it",
                "he",
                "ka",
                "lld",
                "la",
                "lv",
                "lt",
                "hu",
                "mk",
                "arz",
                "ms",
                "min",
                "my",
                "nl",
                "ja",
                "nb",
                "nn",
                "ce",
                "uz",
                "pt",
                "kk",
                "ro",
                "ru",
                "ceb",
                "sk",
                "sl",
                "sr",
                "sh",
                "fi",
                "sv",
                "ta",
                "tt",
                "th",
                "tg",
                "azb",
                "tr",
                "uk",
                "ur",
                "vi",
                "war",
                "zh",
                "yue",
            ],
        )

    def get_input_form(self) -> dict[str, dict]:
        return {"query": {"name": "Query", "type": "line"}}


class Wikipedia(ToolBase, ABC):
    """Wikipedia search tool that retrieves and processes Wikipedia articles."""

    component_name = "Wikipedia"

    @timeout(int(os.environ.get("COMPONENT_EXEC_TIMEOUT", 60)))
    def _invoke(self, **kwargs):
        """Search Wikipedia for articles matching the query and return formalized content.

        Args:
            **kwargs: Must include 'query' key with the search keyword.

        Returns:
            Formatted Wikipedia content or error message.
        """
        if self.check_if_canceled("Wikipedia processing"):
            return

        if not kwargs.get("query"):
            self.set_output("formalized_content", "")
            return ""

        last_e = ""
        for _ in range(self._param.max_retries + 1):
            if self.check_if_canceled("Wikipedia processing"):
                return

            try:
                wikipedia.set_lang(self._param.language)
                wiki_engine = wikipedia
                pages = []
                for p in wiki_engine.search(kwargs["query"], results=self._param.top_n):
                    if self.check_if_canceled("Wikipedia processing"):
                        return

                    try:
                        pages.append(wikipedia.page(p))
                    except wikipedia.exceptions.DisambiguationError as e:
                        logging.info(f"Wikipedia disambiguation for '{p}', options: {e.options[:5]}")
                    except wikipedia.exceptions.PageError:
                        logging.info(f"Wikipedia page not found: '{p}'")
                    except Exception as e:
                        logging.exception(f"Unexpected error fetching Wikipedia page '{p}': {e}")
                self._retrieve_chunks(pages, get_title=lambda r: r.title, get_url=lambda r: r.url, get_content=lambda r: r.summary)
                return self.output("formalized_content")
            except Exception as e:
                if self.check_if_canceled("Wikipedia processing"):
                    return

                last_e = e
                logging.exception(f"Wikipedia error: {e}")
                time.sleep(self._param.delay_after_error)

        if last_e:
            self.set_output("_ERROR", str(last_e))
            return f"Wikipedia error: {last_e}"

        assert False, self.output()

    def thoughts(self) -> str:
        return """
Keywords: {}
Looking for the most relevant articles.
        """.format(self.get_input().get("query", "-_-!"))
