# Copyright 2021 Vincent Texier <vit@free.fr>
#
# This software 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 software 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 <http://www.gnu.org/licenses/>.
import logging
import sys
from typing import Optional

from PyQt5.QtGui import QFont, QMovie
from PyQt5.QtWidgets import QApplication, QDialog, QWidget

from tikka.domains.application import Application
from tikka.domains.entities.account import Account
from tikka.domains.entities.constants import DATA_PATH
from tikka.domains.entities.identity import Identity, IdentityStatus
from tikka.slots.pyqt.entities.constants import ADDRESS_MONOSPACE_FONT_NAME, ICON_LOADER
from tikka.slots.pyqt.entities.event_bus import EventBus
from tikka.slots.pyqt.entities.worker import (
    ThreadManager,
)
from tikka.slots.pyqt.resources.gui.windows.identity_change_account_rc import (
    Ui_IdentityChangeAccountDialog,
)
from tikka.slots.pyqt.windows.account_unlock import AccountUnlockWindow


class IdentityChangeAccountWindow(QDialog, Ui_IdentityChangeAccountDialog):
    """
    IdentityChangeAccountWindow class
    """

    def __init__(
        self,
        application: Application,
        account: Account,
        parent: Optional[QWidget] = None,
    ):
        """
        Init identity change account window

        :param application: Application instance
        :param account: Account owner instance
        :param parent: QWidget instance
        """
        super().__init__(parent=parent)
        self.setupUi(self)

        self.application = application
        self._ = self.application.translator.gettext

        self.event_bus = EventBus.instance()

        self.account = account
        self.destination_account: Optional[Account] = None
        self.destination_identity: Optional[Identity] = None

        # animated loading icon
        self.loader_movie = QMovie(ICON_LOADER)
        self.loader_movie.start()
        self.loaderIconLabel.setMovie(self.loader_movie)
        loader_icon_size_policy = self.loaderIconLabel.sizePolicy()
        loader_icon_size_policy.setRetainSizeWhenHidden(True)
        self.loaderIconLabel.setSizePolicy(loader_icon_size_policy)
        self.loaderIconLabel.hide()

        self.monospace_font = QFont(ADDRESS_MONOSPACE_FONT_NAME)
        self.monospace_font.setStyleHint(QFont.Monospace)
        self.addressValueLabel.setFont(self.monospace_font)

        self.changeAccountButton.setEnabled(False)

        # events
        self.accountComboBox.activated.connect(self.on_account_combobox_index_changed)
        self.changeAccountButton.clicked.connect(self._on_change_account_button_clicked)

        ##############################
        # ASYNC METHODS
        ##############################
        self.thread_manager: ThreadManager = ThreadManager(self)

        self.init_account_combo_box()

    def init_account_combo_box(self) -> None:
        """
        Init combobox with no identity accounts
        Avec séparateur entre noms et adresses
        """
        self.accountComboBox.clear()
        self.accountComboBox.addItem("-", userData=None)

        # Séparer les comptes
        named_accounts = []
        unnamed_accounts = []

        for account in self.application.accounts.get_list():
            identity = self.application.identities.get_index_by_address(account.address)
            if identity is None:
                if account.name and account.name.strip():
                    named_accounts.append(account)
                else:
                    unnamed_accounts.append(account)

        # Trier
        named_accounts.sort(key=lambda acc: acc.name.lower())  # type: ignore
        unnamed_accounts.sort(key=lambda acc: acc.address.lower())

        # Ajouter les comptes avec nom
        if named_accounts:
            for account in named_accounts:
                self.accountComboBox.addItem(
                    f"{account.address} - {account.name}",
                    userData=account.address,
                )

            # Ajouter un séparateur si on a les deux catégories
            if unnamed_accounts:
                self.accountComboBox.insertSeparator(len(named_accounts) + 1)

        # Ajouter les comptes sans nom
        for account in unnamed_accounts:
            self.accountComboBox.addItem(
                account.address,
                userData=account.address,
            )

    def on_account_combobox_index_changed(self):
        """
        Triggered when account selection is changed

        :return:
        """
        destination_address = self.accountComboBox.currentData()
        self.destination_account = self.application.accounts.get_by_address(
            destination_address
        )
        self.update_ui()

    def update_ui(self):
        """
        Update UI fields

        :return:
        """
        self.addressValueLabel.setText(self.destination_account.address or "")
        self.changeAccountButton.setEnabled(
            self.accountComboBox.currentData() is not None
        )

        if self.destination_identity is not None:
            display_identity_status = {
                IdentityStatus.UNCONFIRMED.value: self._("Unconfirmed"),
                IdentityStatus.UNVALIDATED.value: self._("Unvalidated"),
                IdentityStatus.MEMBER.value: self._("Member"),
                IdentityStatus.NOT_MEMBER.value: self._("Not member"),
                IdentityStatus.REVOKED.value: self._("Revoked"),
            }
            identity_status_color = {
                IdentityStatus.UNCONFIRMED.value: "orange",
                IdentityStatus.UNVALIDATED.value: "orange",
                IdentityStatus.MEMBER.value: "green",
                IdentityStatus.NOT_MEMBER.value: "red",
                IdentityStatus.REVOKED.value: "red",
            }
            identity_status_style = f"""
                                                    color: white;
                                                    border: 3px solid "{identity_status_color[self.destination_identity.status.value]}";
                                                    border-radius: 10%;
                                                    background: "{identity_status_color[self.destination_identity.status.value]}";
                                                    """

            self.identityIndexValueLabel.setText(f"#{self.destination_identity.index}")
            self.identityStatusValueLabel.setText(
                display_identity_status[self.destination_identity.status.value]
            )
            self.identityStatusValueLabel.setStyleSheet(identity_status_style)
        else:
            self.identityIndexValueLabel.setText("")
            self.identityStatusValueLabel.setText("")
            self.identityStatusValueLabel.setStyleSheet("")

    def _on_change_account_button_clicked(self):
        """
        Run when use click change account button

        :return:
        """
        if self.destination_account is None:
            self.errorLabel.setStyleSheet("color: red;")
            self.errorLabel.setText(self._("No account selected"))
            return

        if not self.application.wallets.is_unlocked(self.account.address):
            # ask password...
            dialog_code = AccountUnlockWindow(
                self.application, self.account, self
            ).exec_()
            if dialog_code == QDialog.Rejected:
                return

        if not self.application.wallets.is_unlocked(self.destination_account.address):
            # ask password...
            dialog_code = AccountUnlockWindow(
                self.application, self.destination_account, self
            ).exec_()
            if dialog_code == QDialog.Rejected:
                return

        self.loaderIconLabel.show()
        self.changeAccountButton.setDisabled(True)
        self.thread_manager.start(
            self.network_change_account, self._on_finished_network_change_account
        )

    def network_change_account(self):
        """
        Change owner account for identity on the network

        :return:
        """
        if self.destination_account is not None:
            try:
                self.application.identities.network_change_owner_key(
                    self.application.wallets.get_keypair(self.account.address),
                    self.application.wallets.get_keypair(
                        self.destination_account.address
                    ),
                )
            except Exception as exception:
                self.errorLabel.setStyleSheet("color: red;")
                self.errorLabel.setText(str(exception))
                logging.exception(exception)

            try:
                self.application.identities.network_update_identity(
                    self.account.address
                )
            except Exception as exception:
                logging.exception(exception)

            try:
                self.application.identities.network_update_identity(
                    self.destination_account.address
                )
            except Exception as exception:
                logging.exception(exception)

    def _on_finished_network_change_account(self):
        """
        When async network_change_account is finished

        :return:
        """
        self.destination_identity = self.application.identities.get_by_address(
            self.destination_account.address
        )
        self.loaderIconLabel.hide()
        self.update_ui()
        if self.destination_identity is not None:
            self.errorLabel.setStyleSheet("color: green;")
            self.errorLabel.setText(
                self._("Identity #{index} moved to {account_address}").format(
                    index=self.destination_identity.index,
                    account_address=self.destination_account.address,
                )
            )
            self.init_account_combo_box()
            self.changeAccountButton.setEnabled(False)
            self.event_bus.identity_account_changed.emit(
                self.account, self.destination_account
            )
            logging.debug(
                "identity_account_changed_signal emitted from identity_change_account window %s -> #%s",
                self.account.address,
                self.destination_account.address,
            )
        else:
            self.changeAccountButton.setEnabled(True)


if __name__ == "__main__":
    qapp = QApplication(sys.argv)
    application_ = Application(DATA_PATH)
    account_ = Account("xxxx")
    IdentityChangeAccountWindow(application_, account_).exec_()
