# SPDX-FileCopyrightText: 2019-2025 Valéry Febvre
# SPDX-License-Identifier: GPL-3.0-or-later
# Author: Valéry Febvre <vfebvre@easter-eggs.com>

from gettext import gettext as _
import os

from gi.repository import Adw
from gi.repository import Gio
from gi.repository import GLib
from gi.repository import GObject
from gi.repository import Gtk

from komikku.explorer.common import ExplorerServerRow
from komikku.explorer.common import set_missing_server_logos
from komikku.models import Settings
from komikku.servers import LANGUAGES
from komikku.servers.utils import get_allowed_servers_list
from komikku.utils import get_data_dir


@Gtk.Template.from_resource('/info/febvre/Komikku/ui/explorer_servers.ui')
class ExplorerServersPage(Adw.NavigationPage):
    __gtype_name__ = 'ExplorerServersPage'

    global_search_button = Gtk.Template.Child('global_search_button')
    search_button = Gtk.Template.Child('search_button')

    searchbar = Gtk.Template.Child('searchbar')
    searchbar_separator = Gtk.Template.Child('searchbar_separator')
    searchentry = Gtk.Template.Child('searchentry')
    listbox = Gtk.Template.Child('listbox')
    pinned_listbox = Gtk.Template.Child('pinned_listbox')

    preselection = False

    def __init__(self, parent):
        Adw.NavigationPage.__init__(self)

        self.parent = parent
        self.window = parent.window

        self.servers = None

        self.connect('shown', self.on_shown)

        self.global_search_button.connect('clicked', self.on_global_search_button_clicked)

        self.searchbar.bind_property(
            'search-mode-enabled', self.search_button, 'active',
            GObject.BindingFlags.BIDIRECTIONAL | GObject.BindingFlags.SYNC_CREATE
        )
        self.searchbar.bind_property(
            'search-mode-enabled', self.searchbar_separator, 'visible',
            GObject.BindingFlags.BIDIRECTIONAL | GObject.BindingFlags.SYNC_CREATE
        )
        self.searchbar.connect_entry(self.searchentry)
        self.searchbar.connect('notify::search-mode-enabled', self.on_search_mode_toggled)
        self.searchbar.set_key_capture_widget(self.window)

        self.searchentry.connect('activate', self.on_searchentry_activated)
        self.searchentry.connect('search-changed', self.search)

        self.pinned_listbox.connect('row-activated', self.on_server_clicked)

        self.listbox.connect('row-activated', self.on_server_clicked)
        self.listbox.set_filter_func(self.filter)

    def clear(self):
        # Clear servers list
        row = self.listbox.get_first_child()
        while row:
            next_row = row.get_next_sibling()
            if isinstance(row, ExplorerServerRow):
                row.dispose()
            row = next_row

        self.listbox.remove_all()

    def clear_pinned(self):
        # Clear pinned servers list
        row = self.pinned_listbox.get_first_child()
        while row:
            next_row = row.get_next_sibling()
            if isinstance(row, ExplorerServerRow):
                row.dispose()
            row = next_row

        self.pinned_listbox.remove_all()

    def filter(self, row):
        """
        This function gets one row and has to return:
        - True if the row should be displayed
        - False if the row should not be displayed
        """
        term = self.searchentry.get_text().strip().lower()

        if not hasattr(row, 'server_data'):
            # Languages headers should always be displayed
            return True

        server_name = row.server_data['name']
        server_lang = row.server_data['lang']

        # Search in name and language
        return (
            term in server_name.lower() or
            term in LANGUAGES.get(server_lang, _('Other')).lower() or
            term in server_lang.lower()
        )

    def on_global_search_button_clicked(self, _button):
        self.parent.search_page.show()

    def on_server_clicked(self, _listbox, row):
        server = getattr(row.server_data['module'], row.server_data['class_name'])()
        if self.preselection and hasattr(row, 'manga_data'):
            self.parent.search_page.show_manga_card(row.manga_data, server)
        else:
            self.parent.search_page.show(server)

    def on_search_mode_toggled(self, _searchbar, _gparam):
        if self.searchbar.get_search_mode():
            self.pinned_listbox.set_visible(False)
        elif len(Settings.get_default().pinned_servers):
            self.pinned_listbox.set_visible(True)

    def on_searchentry_activated(self, _entry):
        if not self.searchbar.get_search_mode():
            return

        # Select first search result
        for child_row in self.listbox:
            if not hasattr(child_row, 'server_data') or not self.filter(child_row):
                continue
            self.on_server_clicked(self.listbox, child_row)
            break

    def on_shown(self, _page):
        if self.searchbar.get_search_mode():
            self.searchentry.grab_focus()

        def do_show_banner():
            if self.window.last_navigation_action != 'push':
                return

            if not Settings.get_default().servers_languages:
                self.window.show_banner(
                    _('It appears that you have not yet defined any server languages in Preferences.'), at_bottom=True
                )

        if not Gtk.Settings.get_default().get_property('gtk-enable-animations'):
            # When animations are disabled, popped/pushed events are sent after `shown` event (bug?)
            # Use idle_add to be sure that last `popped` or `pushed` event has been received
            GLib.idle_add(do_show_banner)
        else:
            do_show_banner()

    def open_local_folder(self, _button):
        path = os.path.join(get_data_dir(), 'local')
        Gio.app_info_launch_default_for_uri(f'file://{path}')  # noqa: E231

    def populate(self, servers=None):
        self.clear()

        if not servers:
            self.servers = get_allowed_servers_list(Settings.get_default())
            self.populate_pinned()
            self.preselection = False
        else:
            self.servers = servers
            self.pinned_listbox.set_visible(False)
            self.preselection = True

        last_lang = None
        for server_data in self.servers:
            if server_data['lang'] != last_lang:
                # Add language header
                last_lang = server_data['lang']

                row = Gtk.ListBoxRow(activatable=False)
                row.add_css_class('explorer-server-section-listboxrow')
                label = Gtk.Label(xalign=0)
                label.add_css_class('subtitle')
                label.set_text(LANGUAGES.get(server_data['lang'], _('Other')).upper())
                row.set_child(label)
                self.listbox.append(row)

            row = ExplorerServerRow(server_data, self)
            self.listbox.append(row)

        set_missing_server_logos(self.listbox)

        if self.preselection and len(self.servers) == 1:
            row = self.listbox.get_first_child().get_next_sibling()
            self.parent.server = getattr(row.server_data['module'], row.server_data['class_name'])()
            self.parent.search_page.show_manga_card(row.manga_data)
        elif self not in self.window.navigationview.get_navigation_stack():
            self.window.navigationview.push(self)

    def populate_pinned(self):
        self.clear_pinned()

        count = 0
        pinned_servers = Settings.get_default().pinned_servers
        for server_data in self.servers:
            if server_data['id'] not in pinned_servers:
                continue

            row = ExplorerServerRow(server_data, self)
            self.pinned_listbox.append(row)
            count += 1

        set_missing_server_logos(self.pinned_listbox)

        if count:
            # Add header
            row = Gtk.ListBoxRow(activatable=False)
            row.add_css_class('explorer-server-section-listboxrow')
            label = Gtk.Label(xalign=0)
            label.add_css_class('subtitle')
            label.set_text(_('Pinned').upper())
            row.set_child(label)
            self.pinned_listbox.prepend(row)

        self.pinned_listbox.set_visible(count > 0 and not self.searchbar.get_search_mode())

    def search(self, _entry):
        self.listbox.invalidate_filter()

    def show(self, servers):
        self.searchbar.set_search_mode(False)
        self.populate(servers)

    def toggle_server_pinned_state(self, button, row):
        if button.get_active():
            Settings.get_default().add_pinned_server(row.server_data['id'])
        else:
            Settings.get_default().remove_pinned_server(row.server_data['id'])

        if row.get_parent().get_name() == 'pinned_servers':
            for child_row in self.listbox:
                if not hasattr(child_row, 'server_data'):
                    continue

                if child_row.server_data['id'] == row.server_data['id']:
                    child_row.get_first_child().get_last_child().set_active(button.get_active())
                    break

        self.populate_pinned()
