"""
Provide locale management for the Graphical User Interface.
"""

from __future__ import annotations

from typing import Any, TYPE_CHECKING

from PyQt6.QtWidgets import QComboBox, QLabel, QWidget
from typing_extensions import override

from betty.asyncio import wait_to_thread
from betty.gui.text import Caption
from betty.locale import negotiate_locale, get_display_name

if TYPE_CHECKING:
    from betty.app import App
    from PyQt6 import QtGui


class LocalizedObject:
    """
    A `PyQt6.QtWidgets.QWidget` mix-in to localize widgets, and re-localize them when the locale changes.
    """

    def __init__(self, app: App, *args: Any, **kwargs: Any):
        super().__init__(*args, **kwargs)
        self._app = app

    @override
    def showEvent(  # type: ignore[misc]
        self: LocalizedObject & QWidget,
        a0: QtGui.QShowEvent | None,
    ) -> None:
        super().showEvent(a0)  # type: ignore[misc]
        self._set_translatables()
        self._app.configuration.on_change(self._set_translatables)

    def _set_translatables(self) -> None:
        pass


class TranslationsLocaleCollector(LocalizedObject):
    """
    Helps users select a locale for which translations are available.
    """

    def __init__(self, app: App, allowed_locales: set[str]):
        super().__init__(app)
        self._allowed_locales = allowed_locales

        allowed_locale_names: list[tuple[str, str]] = []
        for allowed_locale in allowed_locales:
            locale_name = get_display_name(allowed_locale)
            if locale_name is not None:
                allowed_locale_names.append(
                    (
                        allowed_locale,
                        locale_name,
                    )
                )
        allowed_locale_names = sorted(allowed_locale_names, key=lambda x: x[1])

        def _update_configuration_locale() -> None:
            self._app.configuration.locale = self._configuration_locale.currentData()

        self._configuration_locale = QComboBox()
        for i, (locale, locale_name) in enumerate(allowed_locale_names):
            self._configuration_locale.addItem(locale_name, locale)
            if locale == self._app.configuration.locale:
                self._configuration_locale.setCurrentIndex(i)
        self._configuration_locale.currentIndexChanged.connect(
            _update_configuration_locale
        )
        self._configuration_locale_label = QLabel()
        self._configuration_locale_caption = Caption()

        self._set_translatables()
        self._app.configuration.on_change(self._set_translatables)

    @property
    def locale(self) -> QComboBox:
        """
        The selected locale.
        """
        return self._configuration_locale

    @property
    def rows(self) -> list[list[QWidget]]:
        """
        The :py:class:`PyQt6.QtWidgets.QFormLayout` rows provided by the collector.
        """
        return [
            [self._configuration_locale_label, self._configuration_locale],
            [self._configuration_locale_caption],
        ]

    @override
    def _set_translatables(self) -> None:
        super()._set_translatables()
        localizer = self._app.localizer
        localizers = self._app.localizers
        self._configuration_locale_label.setText(localizer._("Locale"))
        locale = self.locale.currentData()
        if locale:
            translations_locale = negotiate_locale(
                locale,
                list(localizers.locales),
            )
            if translations_locale is None:
                self._configuration_locale_caption.setText(
                    localizer._("There are no translations for {locale_name}.").format(
                        locale_name=get_display_name(locale, localizer.locale),
                    )
                )
            else:
                negotiated_locale_translations_coverage = wait_to_thread(
                    localizers.coverage(translations_locale)
                )
                if negotiated_locale_translations_coverage[1] == 0:
                    negotiated_locale_translations_coverage_percentage = 0
                else:
                    negotiated_locale_translations_coverage_percentage = round(
                        100
                        / (
                            negotiated_locale_translations_coverage[1]
                            / negotiated_locale_translations_coverage[0]
                        )
                    )
                self._configuration_locale_caption.setText(
                    localizer._(
                        "The translations for {locale_name} are {coverage_percentage}%% complete."
                    ).format(
                        locale_name=get_display_name(
                            translations_locale, localizer.locale
                        ),
                        coverage_percentage=round(
                            negotiated_locale_translations_coverage_percentage
                        ),
                    )
                )
