# 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.QtCore import pyqtSignal
from PyQt5.QtGui import QMovie
from PyQt5.QtWidgets import QApplication, QDialog, QLabel, QWidget

from tikka.domains.application import Application
from tikka.domains.entities.account import Account
from tikka.domains.entities.address import DisplayAddress
from tikka.domains.entities.constants import DATA_PATH
from tikka.domains.entities.identity import Identity, IdentityStatus
from tikka.slots.pyqt.entities.constants import 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_certify_rc import (
    Ui_IdentityCertifyDialog,
)
from tikka.slots.pyqt.windows.account_unlock import AccountUnlockWindow


class IdentityCertifyWindow(QDialog, Ui_IdentityCertifyDialog):
    """
    IdentityCertifyWindow class
    """

    identity_certified_signal = pyqtSignal(Identity, Identity)

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

        :param application: Application instance
        :param issuer_account: Creator Account instance
        :param receiver_account: Creator Account 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.issuer_account: Account = issuer_account
        self.issuer_identity: Optional[
            Identity
        ] = self.application.identities.get_by_address(self.issuer_account.address)

        self.receiver_account: Optional[Account] = receiver_account
        self.receiver_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()

        # events
        if self.receiver_account is None:
            self.identityComboBox.activated.connect(
                self.on_identity_combobox_index_changed
            )
            self.certifyIdentityButton.setEnabled(False)
        else:
            self.helpLabel.hide()
            self.identityComboBox.hide()
            self.receiver_identity = self.application.identities.get_by_address(
                self.receiver_account.address
            )
            if self.receiver_identity is not None:
                self.gridLayout.addWidget(QLabel(self.receiver_account.name or ""))
                self.update_ui()

        self.certifyIdentityButton.clicked.connect(
            self._on_certify_identity_button_clicked
        )

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

        self.init_identity_combo_box()

    def init_identity_combo_box(self) -> None:
        """
        Init combobox with identities not unconfirmed nor revoked
        Avec séparateur entre noms et adresses
        """
        self.identityComboBox.clear()
        self.identityComboBox.addItem("-", userData=None)

        for identity in self.application.identities.list():
            if (
                identity.status
                not in (
                    IdentityStatus.UNCONFIRMED,
                    IdentityStatus.REVOKED,
                )
                and identity.address != self.issuer_account.address
            ):
                account = self.application.accounts.get_by_address(identity.address)
                account_name = f" - {account.name}" if account.name is not None else ""  # type: ignore

                self.identityComboBox.addItem(
                    f"{identity.name}#{identity.index} - {DisplayAddress(identity.address).shorten}{account_name}",
                    userData=identity.index,
                )

    def on_identity_combobox_index_changed(self):
        """
        Triggered when identity selection is changed

        :return:
        """
        self.receiver_identity = self.application.identities.get(
            self.identityComboBox.currentData()
        )
        self.certifyIdentityButton.setEnabled(self.receiver_identity is not None)
        self.update_ui()

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

        :return:
        """
        if self.receiver_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.receiver_identity.status.value]}";
                                    border-radius: 10%;
                                    background: "{identity_status_color[self.receiver_identity.status.value]}";
                                    """
            self.addressValueLabel.setText(self.receiver_identity.address)
            self.identityIndexValueLabel.setText(f"#{self.receiver_identity.index}")
            self.identityNameValueLabel.setText(self.receiver_identity.name)
            self.identityStatusValueLabel.setText(
                display_identity_status[self.receiver_identity.status.value]
            )
            self.identityStatusValueLabel.setStyleSheet(identity_status_style)

            self.certifyIdentityButton.setEnabled(True)
        else:
            self.addressValueLabel.setText("")
            self.identityIndexValueLabel.setText("")
            self.identityNameValueLabel.setText("")
            self.identityStatusValueLabel.setText("")
            self.certifyIdentityButton.setEnabled(False)

    def _on_certify_identity_button_clicked(self):
        """
        Run when use click certify identity button

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

        self.loaderIconLabel.show()
        self.certifyIdentityButton.setDisabled(True)
        self.thread_manager.start(
            self.network_certify_identity, self._on_finished_network_certify_identity
        )

    def network_certify_identity(self):
        """
        Send certify identity request to network

        :return:
        """
        if self.receiver_identity is not None:
            currency = self.application.currencies.get_current()
            try:
                assert self.issuer_identity is not None
                self.application.identities.network_certify(
                    self.application.wallets.get_keypair(self.issuer_account.address),
                    self.issuer_identity.index,
                    self.receiver_identity.index,
                )
            except Exception as exception:
                self.errorLabel.setStyleSheet("color: red;")
                self.errorLabel.setText(str(exception))
                logging.exception(exception)
            else:
                self.errorLabel.setStyleSheet("color: green;")
                if (
                    self.receiver_identity.status == IdentityStatus.UNVALIDATED
                    and self.application.certifications.count_received(
                        self.receiver_identity.index
                    )
                    + 1
                    >= currency.certification_number_to_be_member
                ):
                    self.errorLabel.setText(
                        self._("Certification sent!\nRequest membership validation.")
                    )
                else:
                    self.errorLabel.setText(self._("Certification sent!"))

            if (
                self.receiver_identity.status == IdentityStatus.UNVALIDATED
                and self.application.certifications.count_received(
                    self.receiver_identity.index
                )
                + 1
                >= currency.certification_number_to_be_member
            ):
                try:
                    self.application.identities.network_request_distance_evaluation_for(
                        self.application.wallets.get_keypair(
                            self.issuer_account.address
                        ),
                        self.receiver_identity.index,
                    )
                except Exception as exception:
                    self.errorLabel.setStyleSheet("color: red;")
                    self.errorLabel.setText(str(exception))
                    logging.exception(exception)

    def _on_finished_network_certify_identity(self):
        """
        When async network_certify_identity is finished

        :return:
        """
        self.receiver_identity = self.application.identities.get(
            self.receiver_identity.index
        )

        self.certifyIdentityButton.setDisabled(False)
        self.loaderIconLabel.hide()
        self.update_ui()
        if self.receiver_identity is not None:
            self.certifyIdentityButton.setEnabled(False)
            if self.issuer_identity is not None:
                logging.debug(
                    "Identity certify window emit signal with issuer %s -> receiver %s",
                    self.issuer_identity.name,
                    self.receiver_identity.name,
                )
                self.event_bus.identity_certified.emit(
                    self.issuer_identity, self.receiver_identity
                )


if __name__ == "__main__":
    qapp = QApplication(sys.argv)
    application_ = Application(DATA_PATH)
    account_ = Account("xxxx")
    account2_ = Account("yyyy")

    IdentityCertifyWindow(application_, account_, account2_).exec_()
