# -*- coding: utf-8 -*-
#  _____  ____   _               _
# |_   _|| __ ) | |  ___    ___ | | __
#   | |  |  _ \ | | / _ \  / __|| |/ /
#   | |  | |_) || || (_) || (__ |   <
#   |_|  |____/ |_| \___/  \___||_|\_\
#
# Copyright (C) 2021-2025 Camelia Lavender <camelia@tblock.me>

# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU 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 General Public License for more details.

# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <https://www.gnu.org/licenses/>.

# Standard libraries
import gettext
import os
from typing import AnyStr

# External libraries
import tblock
import gi

# Local libraries
from ._polkit import pk_dummy_authenticate
from .job import JobController
from .const import PATH_GUI, ExitStatusCode

gi.require_version("Gtk", "3.0")
# pylint: disable=wrong-import-position,wrong-import-order
from gi.repository import Gtk

gettext.textdomain("tblock-gui")
_ = gettext.gettext


class FilterListsActivity:
    """
    Class that interacts with the activity of the same name from the glade UI
    """

    def __init__(self, application) -> None:
        """
        Initiate the activity

        :param application: An object of type Gtk.Application
        """
        self._application = application
        self.window = self._application.builder.get_object("FilterListsActivity")
        self.window.set_application(self._application)

        self._treeview = None
        self._store = Gtk.ListStore(bool, str, str, str)
        self._changes = []
        self._filter_lists = []

        agr = Gtk.AccelGroup()
        self.window.add_accel_group(agr)
        key, modifier = Gtk.accelerator_parse("<Control>A")
        agr.connect(key, modifier, Gtk.AccelFlags.VISIBLE, self.__select_all_lists)

        self.threaded_activity = JobController(
            application=self._application,
            status_label=self._application.builder.get_object(
                "FilterListsActivityStatusLabel"
            ),
            status_bar=self._application.builder.get_object(
                "FilterListsActivityStatusBar"
            ),
            finish_button=self._application.builder.get_object(
                "FilterListsActivityFinishButton"
            ),
            return_button=self._application.builder.get_object(
                "FilterListsActivityGobackButton"
            ),
        )

    def start(self) -> None:
        """
        Start the activity
        """
        self.refresh()
        self._application.builder.get_object("FilterListsActivitySearchBar").set_text(
            ""
        )
        self.go_to_page("chooser")
        self.window.show_all()
        self._application.builder.get_object("FilterListsActivityDetailsButton").hide()
        self._application.builder.get_object("FilterListsApplyButton").hide()
        self._application.builder.get_object(
            "FilterListsActivityDetailsHomepage"
        ).hide()
        self.window.set_focus(self._treeview)

    def search(self) -> None:
        """
        Run the search operation
        """
        query = self._application.builder.get_object(
            "FilterListsActivitySearchBar"
        ).get_text()
        if query is None:
            self.refresh()
        else:
            self.refresh(query)

    def refresh(self, query: AnyStr = None) -> None:
        """
        Refresh the activity and optionally filter the results based on the search query

        :param query: The query which should be used to filter the results (default is None)
        """
        container = self._application.builder.get_object("FilterListsActivityContainer")
        self._filter_lists = []
        self._changes.clear()
        if self._treeview is not None:
            container.remove(self._treeview)
            self._treeview = None
        self._store = Gtk.ListStore(bool, str, str)
        if query is None:
            self._filter_lists = tblock.get_all_filter_lists()
        else:
            self._filter_lists = tblock.get_search_results_filter_lists(query)
        for i in self._filter_lists:
            _f = tblock.Filter(i, quiet=True)
            if _f.metadata:
                self._store.append([_f.subscribing, _f.id, _f.metadata["title"]])
            else:
                self._store.append([_f.subscribing, _f.id, "-"])
        self._treeview = Gtk.TreeView(model=self._store)
        renderer_toggle = Gtk.CellRendererToggle()
        renderer_toggle.connect("toggled", self.__select_list)
        column_toggle = Gtk.TreeViewColumn(_("Active"), renderer_toggle, active=0)
        self._treeview.append_column(column_toggle)
        renderer_text = Gtk.CellRendererText()
        column_text = Gtk.TreeViewColumn(_("ID"), renderer_text, text=1)
        self._treeview.append_column(column_text)
        renderer_text = Gtk.CellRendererText()
        column_text = Gtk.TreeViewColumn(_("Title"), renderer_text, text=2)
        self._treeview.append_column(column_text)
        container.add(self._treeview)
        self._treeview.connect("cursor_changed", self.__cursor_changed)
        self._treeview.show()
        # Do not focus any filter list by default
        self._treeview.set_cursor(0)
        self._treeview.get_selection().unselect_all()
        self.__cursor_changed(self._treeview)

    def __cursor_changed(self, __widget) -> None:
        """
        Handler for when another filter list is selected

        :param __widget:
        """
        details_title = self._application.builder.get_object(
            "FilterListsActivityDetailsTitle"
        )
        details_description = self._application.builder.get_object(
            "FilterListsActivityDetailsDescription"
        )
        details_homepage = self._application.builder.get_object(
            "FilterListsActivityDetailsHomepage"
        )
        details_icon = self._application.builder.get_object(
            "FilterListsActivityDetailsIcon"
        )
        model, row = __widget.get_selection().get_selected()
        if row is not None:
            _f = tblock.Filter(model[row][1])
            if _f.on_repo:
                details_title.set_text(_f.metadata["title"])
                details_description.set_text(_f.metadata["description"])
                details_homepage.set_uri(_f.metadata["homepage"])
                details_homepage.show()
                icon = os.path.join(PATH_GUI.REPO_ICONS, _f.id + ".png")
                if os.path.isfile(icon):
                    details_icon.set_from_file(icon)
                else:
                    details_icon.set_from_file(PATH_GUI.UNKNOWN_IMAGE)
            else:
                details_title.set_text(_f.id)
                details_description.set_text(
                    _(
                        "This is a custom filter list that was manually added by the user."
                    )
                )
                details_homepage.set_uri("")
                details_homepage.hide()
                details_icon.set_from_file(PATH_GUI.UNKNOWN_IMAGE)
            # This will be uncommented when the option is supported (maybe in a future release)
            # self.builder.get_object("FilterListsActivityDetailsButton").show()
        else:
            details_title.set_text(_("No filter list selected"))
            details_description.set_text(
                _("Click on a filter list from the table to view its details.")
            )
            details_homepage.set_uri("")
            details_homepage.hide()
            details_icon.set_from_file(PATH_GUI.UNKNOWN_IMAGE)
            self._application.builder.get_object(
                "FilterListsActivityDetailsButton"
            ).hide()

    def __select_all_lists(self, *_args) -> None:
        """
        Handler for when all filter lists are selected

        :param _args:
        """
        not_subscribed = tblock.get_all_filter_lists(not_subscribing_only=True)
        i = 0
        if self.window.get_focus() == self._treeview:
            if sorted(not_subscribed) == sorted(self._changes):
                for _x in self._store:
                    self.__select_list(None, i)
                    i += 1
            else:
                for _x in self._store:
                    filter_list = tblock.Filter(self._filter_lists[i])
                    if filter_list.id in self._changes and filter_list.subscribing:
                        self._changes.remove(filter_list.id)
                    elif not self._store[i][0]:
                        self._changes.append(filter_list.id)
                    self._store[i][0] = True
                    i += 1

    def __select_list(self, __widget, path) -> None:
        """
        Handler for when a filter list is selected

        :param __widget:
        :param path:
        """
        self._store[path][0] = not self._store[path][0]
        filter_list = self._filter_lists[int(path)]
        if filter_list in self._changes:
            self._changes.remove(filter_list)
        else:
            self._changes.append(filter_list)
        if self._changes:
            self._application.builder.get_object("FilterListsApplyButton").show()
        else:
            self._application.builder.get_object("FilterListsApplyButton").hide()

    def run_thread(self) -> None:
        """
        Run the subscribing/unsubscribing operation
        """
        _auth_err_code = pk_dummy_authenticate()
        if _auth_err_code == ExitStatusCode.NO_ERROR:
            self.go_to_page("operation")
            self.threaded_activity.manage_filter_lists(
                self._changes, sync=False, build_hosts=True, unsubscribe=True
            )
        else:
            self._application.error_window.handle_error(_auth_err_code)

    def run_thread_update(self) -> None:
        """
        Run the update operation
        """
        _auth_err_code = pk_dummy_authenticate()
        if _auth_err_code == ExitStatusCode.NO_ERROR:
            self.window.show_all()
            self.go_to_page("operation")
            self.threaded_activity.update_filter_lists()
        else:
            self._application.error_window.handle_error(_auth_err_code)

    def run_thread_sync(self) -> None:
        """
        Run the sync operation
        """
        _auth_err_code = pk_dummy_authenticate()
        if _auth_err_code == ExitStatusCode.NO_ERROR:
            self.window.show_all()
            self.go_to_page("operation")
            self.threaded_activity.manage_filter_lists(
                [], sync=True, build_hosts=False, unsubscribe=False
            )
        else:
            self._application.error_window.handle_error(_auth_err_code)

    def go_to_page(self, name: str) -> None:
        """
        Go to a selected page of the activity

        :param name: The index of the page to go to
        """
        self._application.builder.get_object(
            "FilterListsActivityStack"
        ).set_visible_child_name(name)

    def quit(self) -> None:
        """
        Quit the activity
        """
        if self.threaded_activity.running:
            self.threaded_activity.kill()
        self.window.hide()
