import locale
from typing import List, Union

from babel import Locale
import wx

from clarabot_nano.control.label import AutoHeightWrappedStaticText
from clarabot_nano.control.wizard import SimpleWizardPage
from clarabot_nano.indexer.utility import get_effective_languages_by_config


class IndexLanguagePage(SimpleWizardPage):
    @classmethod
    def _lang_config_to_ui(cls, lang: str) -> str:
        try:
            name = Locale(lang).get_display_name(locale.getlocale()[0])
        except Exception:
            name = "???"
        return f"{name} ({lang})"

    @classmethod
    def _lang_ui_to_config(cls, ui_lang: str) -> str:
        return ui_lang.rpartition("(")[-1][:-1]

    def __init__(self, parent, available: List[str], config: Union[str, None, List[str]]):
        SimpleWizardPage.__init__(
            self,
            parent,
            gettext(
                "Select the languages to have improved indexing in the search database. "
                "The more languages have custom indexing the more costly it becomes to search.\n"
                "Languages that are not specified here will be indexed in a generic field with reduced accuracy."
            ),
        )
        self._supported_languages = set(available)
        self._config = config or []  # coerce none

        self.radio_all = wx.RadioButton(self, wx.ID_ANY, gettext("All supported languages"), style=wx.RB_GROUP)
        self.layout.Add(self.radio_all, 0, wx.EXPAND | wx.BOTTOM, 8)
        self.radio_list = wx.RadioButton(self, wx.ID_ANY, gettext("Select from the supported languages"))
        self.layout.Add(self.radio_list, 0, wx.EXPAND | wx.BOTTOM, 8)

        self.languages_layout = wx.BoxSizer(wx.HORIZONTAL)
        self.layout.Add(self.languages_layout, 0, wx.EXPAND | wx.LEFT | wx.BOTTOM, 16)
        self.languages = wx.CheckListBox(
            self, wx.ID_ANY, choices=[self._lang_config_to_ui(x) for x in available], style=wx.LB_ALWAYS_SB | wx.LB_SORT
        )
        self.languages_layout.Add(self.languages, 1, wx.EXPAND | wx.RIGHT, 8)
        self.language_buttons_layout = wx.BoxSizer(wx.VERTICAL)
        self.languages_layout.Add(self.language_buttons_layout, 0, wx.EXPAND)
        self.select_none = wx.Button(self, wx.ID_ANY, gettext("Select none"))
        self.language_buttons_layout.Add(self.select_none, 0, wx.EXPAND | wx.BOTTOM, 8)
        self.select_all = wx.Button(self, wx.ID_ANY, gettext("Select all"))
        self.language_buttons_layout.Add(self.select_all, 0, wx.EXPAND | wx.BOTTOM, 8)
        self.language_buttons_layout.AddStretchSpacer(1)
        self.effective_label = AutoHeightWrappedStaticText(self, wx.ID_ANY)
        self.layout.Add(self.effective_label, 0, wx.EXPAND)

        self.Bind(wx.EVT_BUTTON, self._on_select_none, self.select_none)
        self.Bind(wx.EVT_BUTTON, self._on_select_all, self.select_all)
        self.Bind(wx.EVT_CHECKLISTBOX, self._on_checklist_box, self.languages)
        self.Bind(wx.EVT_RADIOBUTTON, self._on_radio_button, self.radio_all)
        self.Bind(wx.EVT_RADIOBUTTON, self._on_radio_button, self.radio_list)

        if self._config == "all":
            self.radio_all.SetValue(True)
        else:
            self.radio_list.SetValue(True)
            self.languages.SetCheckedStrings(
                [x2 for x in self._config if (x2 := self._lang_config_to_ui(x.lower())) in self.languages.GetStrings()]
            )
        self._on_radio_button()
        self._on_checklist_box()

        self.Layout()

    def _on_radio_button(self, *args):
        list_selected = self.radio_list.GetValue()
        self.languages.Enable(list_selected)
        self.select_none.Enable(list_selected)
        self.select_all.Enable(list_selected)
        self._on_checklist_box()

    def _on_checklist_box(self, *args):
        if self.radio_all.GetValue():
            value = gettext("All")
            value += f" ({', '.join(sorted(self._supported_languages))})"
        else:
            value = ", ".join(
                get_effective_languages_by_config(
                    self._supported_languages, [self._lang_ui_to_config(x) for x in self.languages.GetCheckedStrings()]
                )
            )
        self.effective_label.SetLabelText(gettext("Languages that will have improved indexing:\n") + value)

    def _on_select_none(self, event):
        self.languages.SetCheckedStrings([])
        self._on_checklist_box()

    def _on_select_all(self, event):
        self.languages.SetCheckedStrings(self.languages.GetStrings())
        self._on_checklist_box()

    def get_new_config(self):
        """
        Merge the config in a way to avoid unnecessary changes that have no meaning.
        """
        if self.radio_all.GetValue():
            return self._config, "all"
        deselected = set(self._lang_ui_to_config(x) for x in self.languages.GetStrings())
        selected = set(self._lang_ui_to_config(x) for x in self.languages.GetCheckedStrings())
        deselected.difference_update(selected)
        # create new config state
        config = list(self._config)
        # remove all would be matches of the deselected items
        for lang in deselected:
            while True:
                for x in config:
                    if lang == x.lower():
                        config.remove(x)
                else:
                    break
        # get the selected items that are missing from the config
        selected.difference_update(x.lower() for x in self._config)
        config.extend(sorted(selected))
        return self._config, config
