# This file is part of Gajim.
#
# SPDX-License-Identifier: GPL-3.0-only

from typing import Any
from typing import cast

import logging

from gi.repository import Adw
from gi.repository import Gtk
from nbxmpp.errors import StanzaError
from nbxmpp.protocol import JID
from nbxmpp.task import Task

from gajim.common import app

from gajim.gtk.util.classes import SignalManager
from gajim.gtk.util.misc import get_ui_string

log = logging.getLogger("gajim.gtk.preferences.blocked_contacts")


@Gtk.Template.from_string(string=get_ui_string("preference/blocked_contacts.ui"))
class BlockedContacts(Gtk.Grid, SignalManager):
    __gtype_name__ = "BlockedContacts"

    _spinner: Adw.Spinner = Gtk.Template.Child()
    _block_view: Gtk.TreeView = Gtk.Template.Child()
    _jid_cell_renderer: Gtk.CellRendererText = Gtk.Template.Child()
    _add_button: Gtk.Button = Gtk.Template.Child()
    _remove_button: Gtk.Button = Gtk.Template.Child()
    _save_button: Gtk.Button = Gtk.Template.Child()

    def __init__(self, account: str) -> None:
        Gtk.Grid.__init__(self)
        SignalManager.__init__(self)

        self._account = account
        self._client = app.get_client(account)
        self._prev_blocked_jids: set[JID] = set()

        self._blocking_store = Gtk.ListStore(str)
        self._block_view.set_model(self._blocking_store)

        self._set_grid_state(False)

        self._activate_spinner()

        self._connect(self._jid_cell_renderer, "edited", self._jid_edited)
        self._connect(self._add_button, "clicked", self._on_add)
        self._connect(self._remove_button, "clicked", self._on_remove)
        self._connect(self._save_button, "clicked", self._on_save)

        self._client.get_module("Blocking").request_blocking_list(
            callback=self._on_blocking_list_received
        )

    def _on_blocking_list_received(self, task: Task) -> None:
        self._disable_spinner()
        try:
            blocking_list = cast(set[JID], task.finish())
        except StanzaError as error:
            self._set_grid_state(False)
            log.error("Failed to retrieve blocked contacts: %s", error)
            return

        self._prev_blocked_jids = blocking_list
        self._blocking_store.clear()

        for jid in blocking_list:
            self._blocking_store.append([str(jid)])

        self._set_grid_state(True)

    def _on_save_result(self, task: Task):
        self._disable_spinner()
        try:
            task.finish()
        except StanzaError as error:
            log.error("Failed to store blocked contacts: %s", error)
        finally:
            self._set_grid_state(True)

    def _set_grid_state(self, state: bool) -> None:
        self.set_sensitive(state)

    def _jid_edited(
        self, _renderer: Gtk.CellRendererText, path: str, new_text: str
    ) -> None:
        iter_ = self._blocking_store.get_iter(path)
        self._blocking_store.set_value(iter_, 0, new_text)

    def _on_add(self, _button: Any) -> None:
        self._blocking_store.append([""])

    def _on_remove(self, _button: Any) -> None:
        selected_rows = self._block_view.get_selection().get_selected_rows()
        if selected_rows is None:
            return

        mod, paths = selected_rows
        for path in paths:
            iter_ = mod.get_iter(path)
            self._blocking_store.remove(iter_)

    def _on_save(self, _button: Gtk.Button) -> None:
        self._activate_spinner()
        self._set_grid_state(False)

        blocked_jids: set[JID] = set()
        for item in self._blocking_store:
            if not item[0]:
                # No address/placeholder
                continue
            blocked_jids.add(JID.from_string(item[0]))

        unblock_jids = self._prev_blocked_jids - blocked_jids
        block_jids = blocked_jids - self._prev_blocked_jids

        self._client.get_module("Blocking").update_blocking_list(
            block_jids,
            unblock_jids,
            callback=self._on_save_result,  # type: ignore
        )

    def _activate_spinner(self) -> None:
        self._spinner.set_visible(True)

    def _disable_spinner(self) -> None:
        self._spinner.set_visible(False)

    def do_unroot(self) -> None:
        self._disconnect_all()
        del self._client
        del self._blocking_store
        Gtk.Grid.do_unroot(self)
        app.check_finalize(self)
