"""Integrate Betty with `Wikipedia <https://wikipedia.org>`_."""

from __future__ import annotations

import logging
from asyncio import gather
from pathlib import Path
from typing import Iterable, TYPE_CHECKING, final, Self

from jinja2 import pass_context
from typing_extensions import override

from betty.concurrent import RateLimiter
from betty.service import service
from betty.fetch import FetchError
from betty.jinja2 import Jinja2Provider, context_localizer, Filters, Globals
from betty.locale import negotiate_locale
from betty.locale.localizable import _
from betty.plugin import ShorthandPluginBase
from betty.project.extension import ConfigurableExtension
from betty.project.extension.wikipedia.config import WikipediaConfiguration
from betty.project.load import PostLoadAncestryEvent
from betty.wikipedia import (
    Summary,
    _parse_url,
    NotAPageError,
    _Retriever,
    _Populator,
    RATE_LIMIT,
)
from betty.wikipedia.copyright_notice import WikipediaContributors

if TYPE_CHECKING:
    from betty.copyright_notice import CopyrightNotice
    from betty.project import Project
    from betty.event_dispatcher import EventHandlerRegistry
    from jinja2.runtime import Context
    from betty.ancestry.link import Link


async def _populate_ancestry(event: PostLoadAncestryEvent) -> None:
    project = event.project
    extensions = await project.extensions
    wikipedia = extensions[Wikipedia]
    populator = _Populator(
        project.ancestry,
        list(project.configuration.locales.keys()),
        await project.localizers,
        await wikipedia.retriever,
        await project.copyright_notice_repository.new_target(WikipediaContributors),
    )
    await populator.populate()


@final
class Wikipedia(
    ShorthandPluginBase, ConfigurableExtension[WikipediaConfiguration], Jinja2Provider
):
    """
    Integrates Betty with `Wikipedia <https://wikipedia.org>`_.
    """

    def __init__(
        self,
        project: Project,
        wikipedia_contributors_copyright_notice: CopyrightNotice,
        *,
        configuration: WikipediaConfiguration,
    ):
        super().__init__(project, configuration=configuration)
        self._wikipedia_contributors_copyright_notice = (
            wikipedia_contributors_copyright_notice
        )

    @override
    @classmethod
    async def new_for_project(cls, project: Project) -> Self:
        return cls(
            project,
            await project.copyright_notice_repository.new_target(
                "wikipedia-contributors"
            ),
            configuration=cls.new_default_configuration(),
        )

    _plugin_id = "wikipedia"
    _plugin_label = _("Wikipedia")
    _plugin_description = _("Enrich your ancestry with information from Wikipedia")

    @override
    def register_event_handlers(self, registry: EventHandlerRegistry) -> None:
        registry.add_handler(PostLoadAncestryEvent, _populate_ancestry)

    @service
    async def rate_limiter(self) -> RateLimiter:
        """
        The Wikipedia API rate limiter.
        """
        return RateLimiter(
            RATE_LIMIT, manager=self._project.app.multiprocessing_manager
        )

    @service
    async def retriever(self) -> _Retriever:
        """
        The Wikipedia content retriever.
        """
        return _Retriever(await self.project.app.fetcher, await self.rate_limiter)

    @override
    @property
    def globals(self) -> Globals:
        return {
            "wikipedia_contributors_copyright_notice": self._wikipedia_contributors_copyright_notice
        }

    @override
    @property
    def filters(self) -> Filters:
        return {
            "wikipedia": self.filter_wikipedia_links,
        }

    @pass_context
    async def filter_wikipedia_links(
        self, context: Context, links: Iterable[Link]
    ) -> Iterable[Summary]:
        """
        Given a sequence of links, return any Wikipedia summaries for them.
        """
        return filter(
            None,
            await gather(
                *(
                    self._filter_wikipedia_link(
                        context_localizer(context).locale,
                        link,
                    )
                    for link in links
                )
            ),
        )

    async def _filter_wikipedia_link(self, locale: str, link: Link) -> Summary | None:
        try:
            page_language, page_name = _parse_url(link.url)
        except NotAPageError:
            return None
        if negotiate_locale(locale, [page_language]) is None:
            return None
        try:
            retriever = await self.retriever
            return await retriever.get_summary(page_language, page_name)
        except FetchError as error:
            logger = logging.getLogger(__name__)
            logger.warning(str(error))
            return None

    @override
    @classmethod
    def assets_directory_path(cls) -> Path | None:
        return Path(__file__).parent / "assets"

    @override
    @classmethod
    def new_default_configuration(cls) -> WikipediaConfiguration:
        return WikipediaConfiguration()
