# window.py
#
# Copyright 2024 Etersoft
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program.  If not, see <https://www.gnu.org/licenses/>.
#
# SPDX-License-Identifier: AGPL-3.0-or-later

from gi.repository import Gtk, Adw

import gettext
gettext.textdomain('eepm-play-gui')
_ = gettext.gettext

from .appsmanager import ApplicationManager
from .command_runner import CommandRunner


class ApplicationRow(Adw.ActionRow):
    def __init__(self, app, is_installed, on_toggle):
        super().__init__(title=app['name'], subtitle=app['dscr'])
        self.app = app
        self.is_installed = is_installed
        self.checkbox = Gtk.CheckButton()
        self.checkbox.add_css_class("selection-mode")
        self.checkbox.set_active(is_installed)
        self.checkbox.connect("toggled", self.on_checkbox_toggled)
        self.add_suffix(self.checkbox)
        self.on_toggle = on_toggle  # Callback for when the checkbox is toggled

    def on_checkbox_toggled(self, checkbox):
        self.on_toggle(self.app['name'], checkbox.get_active())
        self.update_row_css()  # Update the row's style when toggled

    def is_changed(self):
        """Determine if the checkbox state has changed compared to the installed state."""
        return self.checkbox.get_active() != self.is_installed

    def update_row_css(self):
        """Update the CSS classes for the row based on the checkbox state."""
        for css in ["marked-for-install", "marked-for-removal", "unchanged"]:
            self.remove_css_class(css)

        # Add the appropriate class based on the current checkbox state
        if self.is_changed():
            if self.checkbox.get_active():
                self.add_css_class("marked-for-install")
                print("marked-for-install")
            else:
                self.add_css_class("marked-for-removal")
                print("marked-for-removal")
        else:
            # self.add_css_class("unchanged")
            print("marked-unchanged")


@Gtk.Template(resource_path='/ru/eepm/PlayGUI/logdialog.ui')
class LogDialog(Adw.Dialog):
    __gtype_name__ = 'LogDialog'
    logdialog_textview = Gtk.Template.Child()

    def __init__(self, win, **kwargs):
        super().__init__(**kwargs)
        self.win = win

    def run(self, command, on_done):
        self.present(self.win)
        # Создание и передача функции обратного вызова для обновления UI
        runner = CommandRunner(on_done=on_done)
        runner.run_command(command, self)


@Gtk.Template(resource_path='/ru/eepm/PlayGUI/window.ui')
class EepmPlayGuiWindow(Adw.ApplicationWindow):
    __gtype_name__ = 'EepmPlayGuiWindow'
    search_entry = Gtk.Template.Child()
    search_dropdown = Gtk.Template.Child()
    loading_spinner = Gtk.Template.Child()
    choice_listbox = Gtk.Template.Child()
    apply_button = Gtk.Template.Child()

    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self.set_title("EPM PLAY")
        self.checkboxes = None

        self.apply_button.connect("activated", self.on_apply_clicked)

        self.search_entry.connect("search-changed", self.on_search_changed)
        self.search_dropdown.connect("notify::selected", self.on_filter_changed)

        self.choice_listbox.set_filter_func(self.listbox_filter_func)

        self.dialog = LogDialog(win=self)

        self.update_ui()

    def show_loading_spinner(self):
        self.loading_spinner.set_visible(True)
        self.choice_listbox.set_visible(False)  # Скрыть группу настроек во время загрузки

    def hide_loading_spinner(self):
        self.loading_spinner.set_visible(False)
        self.choice_listbox.set_visible(True)

    def on_applications_loaded(self, applications, error=None):
        if error:
            print(f"Error: {error}")
        else:
            self.applications = applications

    def update_ui(self):
        self.installed_apps = None
        self.applications = None

        self.show_loading_spinner()  # Show loading again for installed apps

        self.update_button_status()

        ApplicationManager.get_available_applications(self.on_applications_loaded)
        ApplicationManager.get_installed_applications(self.on_installed_apps_loaded)

    def on_installed_apps_loaded(self, installed_apps, error=None):
        if error:
            print(f"Error: {error}")
        else:
            self.installed_apps = installed_apps
            self.clear_choice_listbox()

            self.checkboxes = {}
            for app in self.applications:
                self.add_application_row(app)

            self.choice_listbox.invalidate_filter()
            self.hide_loading_spinner()

    def clear_choice_listbox(self):
        self.choice_listbox.remove_all()

    def add_application_row(self, app):
        """Adds an application row to the listbox."""
        row = ApplicationRow(
            app=app,
            is_installed=app['name'] in self.installed_apps,
            on_toggle=self.on_checkbox_toggled
        )
        self.choice_listbox.append(row)
        self.checkboxes[app['name']] = row

    def on_checkbox_toggled(self, app_name, active):
        print(f"{app_name} {'установлен' if active else 'снят'}")
        self.update_button_status()

    def update_button_status(self):
        """Update the button status based on the current selection."""
        to_install, to_remove = self.get_install_remove_lists()

        button_states = {
            (True, True): (_("Remove and install applications"), "suggested-action"),
            (True, False): (_("Install applications"), "suggested-action"),
            (False, True): (_("Remove applications"), "destructive-action"),
            (False, False): (_("Update applications"), "suggested-action"),
        }

        title, css_class = button_states[(bool(to_install), bool(to_remove))]
        self.apply_button.set_title(title)

        # Remove all previous CSS classes in a loop
        for css in ["suggested-action", "destructive-action"]:
            self.apply_button.remove_css_class(css)

        # Add the new CSS class
        self.apply_button.add_css_class(css_class)

    def on_search_changed(self, search_entry):
        self.choice_listbox.invalidate_filter()  # Обновление фильтра при изменении поиска

    def on_filter_changed(self, dropdown, _pspec):
        self.choice_listbox.invalidate_filter()  # Обновление фильтра при изменении фильтра

    def listbox_filter_func(self, row):
        """Функция фильтрации для GtkListBox, которая проверяет текст и состояние фильтра."""
        search_text = self.search_entry.get_text().lower()
        filter_option = self.search_dropdown.get_selected()

        # Получение заголовка и подзаголовка строки
        title = row.get_title().lower() if row.get_title() else ''
        subtitle = row.get_subtitle().lower() if row.get_subtitle() else ''

        # Проверка текста поиска (по имени или описанию)
        matches_search = search_text in title or search_text in subtitle

        # Проверка по фильтру: установлено/не установлено в системе
        app_name = row.get_title() if row.get_title() else ''
        is_installed = app_name in self.installed_apps
        is_changed = row.is_changed()

        if filter_option == 1:  # Installed
            return matches_search and is_installed
        elif filter_option == 2:  # Uninstalled
            return matches_search and not is_installed
        elif filter_option == 3:  # Changed
            return matches_search and is_changed
        else:
            return matches_search  # All

    def on_apply_clicked(self, button):
        commands = None

        self.show_loading_spinner()  # Показать сообщение о загрузке перед выполнением команды
        to_install, to_remove = self.get_install_remove_lists()
        commands = self.build_commands(to_install, to_remove)

        if commands:
            full_command = " && ".join(commands)
            pkexec_command = f'pkexec sh -c "{full_command}"'
            self.dialog.run(pkexec_command, on_done=self.update_ui)
        else:
            self.dialog.run("pkexec epm play --update all", on_done=self.update_ui)

    def get_install_remove_lists(self):
        if self.installed_apps and self.checkboxes:
            to_install = [app_name for app_name, row in self.checkboxes.items() if row.checkbox.get_active() and app_name not in self.installed_apps]
            to_remove = [app_name for app_name, row in self.checkboxes.items() if not row.checkbox.get_active() and app_name in self.installed_apps]
            return to_install, to_remove
        else:
            return False, False

    def build_commands(self, to_install, to_remove):
        commands = []
        if to_install:
            commands.append(f"epm play --auto {' '.join(to_install)}")
        if to_remove:
            commands.append(f"epm play --auto --remove {' '.join(to_remove)}")
        return commands