# 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 datetime import datetime
from typing import List, Optional

import qrcode
from PyQt5.QtCore import QEvent, QLocale, QPoint, QTimer, pyqtSignal
from PyQt5.QtGui import QDesktopServices, QFont, QPixmap, QWheelEvent
from PyQt5.QtWebEngineWidgets import QWebEngineView
from PyQt5.QtWidgets import QApplication, QListView, QMainWindow, QSizePolicy, QWidget

from tikka.domains.application import Application
from tikka.domains.entities.account import Account, AccountCryptoType
from tikka.domains.entities.address import DisplayAddress
from tikka.domains.entities.constants import AMOUNT_UNIT_KEY, DATA_PATH
from tikka.domains.entities.events import (
    UnitEvent,
)
from tikka.domains.entities.identity import Identity, IdentityStatus
from tikka.domains.entities.profile import Profile
from tikka.domains.entities.smith import SmithStatus
from tikka.slots.pyqt.entities.constants import (
    ACCOUNT_TRANSFER_HISTORY_LIMIT,
    ADDRESS_MONOSPACE_FONT_NAME,
    ICON_ACCOUNT_NO_WALLET,
    ICON_ACCOUNT_WALLET_LOCKED,
    ICON_ACCOUNT_WALLET_UNLOCKED,
    ICON_IDENTITY_MEMBER,
    ICON_IDENTITY_MEMBER_NOT_VALIDATED,
    ICON_IDENTITY_NOT_MEMBER,
    SELECTED_UNIT_PREFERENCES_KEY,
)
from tikka.slots.pyqt.entities.event_bus import EventBus
from tikka.slots.pyqt.entities.qrcode_image import QRCodeImage
from tikka.slots.pyqt.entities.worker import ThreadManager
from tikka.slots.pyqt.models.certifications import (
    CertificationItemDelegate,
    CertificationsListModel,
)
from tikka.slots.pyqt.models.transfers import TransferItemDelegate, TransfersListModel
from tikka.slots.pyqt.resources.gui.widgets.account_rc import Ui_AccountWidget
from tikka.slots.pyqt.widgets.account_certifications_menu import (
    AccountCertificationsPopupMenu,
)
from tikka.slots.pyqt.widgets.account_menu import AccountPopupMenu
from tikka.slots.pyqt.widgets.account_profile_view import create_profile_html
from tikka.slots.pyqt.widgets.account_transfers_menu import AccountTransfersPopupMenu
from tikka.slots.pyqt.windows.identity_certify import IdentityCertifyWindow
from tikka.slots.pyqt.windows.identity_change_account import IdentityChangeAccountWindow
from tikka.slots.pyqt.windows.identity_confirm import IdentityConfirmWindow
from tikka.slots.pyqt.windows.identity_create import IdentityCreateWindow
from tikka.slots.pyqt.windows.profile_change_account import ProfileChangeAccountWindow
from tikka.slots.pyqt.windows.transfer import TransferWindow


class TransfersListView(QListView):
    """
    TransfersListView class
    """

    scroll_offset_changed = pyqtSignal(int)

    def __init__(
        self,
        application: Application,
        address: str,
        page_size: int,
        parent: Optional[QWidget] = None,
    ):
        """
        Init TransfersListView instance

        :param application: Application instance
        :param address: Account address
        :param page_size: Nb of transfers per page
        :param parent: QWidget instance, default None
        """
        super().__init__(parent)

        self.application = application
        self.address = address
        self.scroll_offset = 0
        self.page_size = page_size

    def wheelEvent(self, event: QWheelEvent):
        """
        Override Wheel event

        :param event: QEvent instance
        :return:
        """
        delta = event.angleDelta().y()
        if not self.verticalScrollBar().isVisible():
            if delta < 0:  # 🔽 Scroll vers le bas (charger plus)
                if (
                    self.scroll_offset + self.page_size
                    < self.application.transfers.count(self.address)
                ):
                    self.scroll_offset += self.page_size
                    self.model().init_data(self.scroll_offset, self.page_size)  # type: ignore
                    self.scroll_offset_changed.emit(self.scroll_offset)

            elif (
                delta > 0
            ):  # 🔼 Scroll vers le haut (charger page précédente si disponible)
                self.scroll_offset -= self.page_size
                if self.scroll_offset < 0:
                    self.scroll_offset = 0
                self.model().init_data(self.scroll_offset, self.page_size)  # type: ignore
                self.scroll_offset_changed.emit(self.scroll_offset)
        else:
            # wheel backward and scroll bar maximum down...
            if (
                delta < 0
                and self.verticalScrollBar().value()
                == self.verticalScrollBar().maximum()
            ):

                if (
                    self.scroll_offset + self.page_size
                    < self.application.transfers.count(self.address)
                ):
                    self.scroll_offset += self.page_size
                    self.model().init_data(self.scroll_offset, self.page_size)  # type: ignore
                    self.scroll_offset_changed.emit(self.scroll_offset)

            # wheel forward and scroll bar maximum top...
            elif (
                delta > 0
                and self.verticalScrollBar().value()
                == self.verticalScrollBar().minimum()
            ):
                self.scroll_offset -= self.page_size
                if self.scroll_offset < 0:
                    self.scroll_offset = 0
                self.model().init_data(self.scroll_offset, self.page_size)  # type: ignore
                self.scroll_offset_changed.emit(self.scroll_offset)

        super().wheelEvent(event)


class AccountWidget(QWidget, Ui_AccountWidget):
    """
    AccountWidget class
    """

    def __init__(
        self,
        application: Application,
        account: Account,
        parent: Optional[QWidget] = None,
    ) -> None:
        """
        Init AccountWidget instance

        :param application: Application instance
        :param account: 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.event_bus.repository_data_updated.connect(
            self.repository_data_updated_event_bus
        )
        self.event_bus.account_renamed.connect(self.on_account_renamed_event_bus)
        self.event_bus.account_wallet_updated.connect(
            self.on_account_wallet_updated_event_bus
        )
        self.event_bus.account_balances_updated.connect(
            self.on_account_balances_updated
        )
        self.event_bus.identities_updated.connect(self.on_identities_updated_event_bus)
        self.event_bus.account_identities_updated.connect(
            self.on_account_identities_updated_event_bus
        )
        self.event_bus.account_profiles_updated.connect(
            self.on_account_profiles_updated_event_bus
        )

        self.account: Account = account
        self.identity: Optional[Identity] = self.application.identities.get_by_address(
            self.account.address
        )

        self.history_limit = ACCOUNT_TRANSFER_HISTORY_LIMIT
        self.history_page_size = 100
        self.history_scroll_offset = 0
        self.profile: Optional[Profile] = self.application.profiles.get(
            self.account.address
        )

        self.monospace_font = QFont(ADDRESS_MONOSPACE_FONT_NAME)
        self.monospace_font.setStyleHint(QFont.Monospace)
        self.addressValueLabel.setFont(self.monospace_font)
        self.addressValueLabel.setText(self.account.address)
        if self.account.legacy_v1:
            self.v1AddressValueLabel.setText(
                self.account.get_v1_address(
                    self.application.currencies.get_current().ss58_format, True
                )
            )

        # creating a pix map of qr code
        qr_code_pixmap = qrcode.make(
            self.account.address, image_factory=QRCodeImage
        ).pixmap()

        # set qrcode to the label
        self.QRCodeAddressLabel.setPixmap(qr_code_pixmap)

        self.transfers_model = TransfersListModel(
            application, self.account.address, self.locale()
        )

        # self.transfersListView.hide()
        # self.transfersListView = TransfersListView(
        #     application, self.account.address, self.history_page_size, self.groupBox
        # )
        #
        # self.transfersListView.setMouseTracking(False)
        # self.transfersListView.setEditTriggers(QAbstractItemView.NoEditTriggers)
        # self.transfersListView.setAlternatingRowColors(True)
        # self.transfersListView.setSelectionMode(QAbstractItemView.NoSelection)
        # self.transfersListView.setObjectName("transfersListView")
        # self.verticalLayout_4.addWidget(self.transfersListView)

        # transfers
        self.transfersListView.setModel(self.transfers_model)
        self.transfersListView.setItemDelegate(TransferItemDelegate())
        self.transfersListView.setResizeMode(QListView.Adjust)

        # certifications received
        self.certifications_received_model = CertificationsListModel(
            application,
            self.locale(),
            self.application.repository.certifications.COLUMN_RECEIVER_IDENTITY_INDEX,
        )
        self.certificationsReceivedListView.setModel(self.certifications_received_model)
        self.certificationsReceivedListView.setItemDelegate(CertificationItemDelegate())
        self.certificationsReceivedListView.setResizeMode(QListView.Adjust)

        # certifications issued
        self.certifications_issued_model = CertificationsListModel(
            application,
            self.locale(),
            self.application.repository.certifications.COLUMN_ISSUER_IDENTITY_INDEX,
        )
        self.certificationsIssuedListView.setModel(self.certifications_issued_model)
        self.certificationsIssuedListView.setItemDelegate(CertificationItemDelegate())
        self.certificationsIssuedListView.setResizeMode(QListView.Adjust)

        self.certifications_received_model.init_data(
            self.identity.index if self.identity else None
        )
        self.certifications_issued_model.init_data(
            self.identity.index if self.identity else None
        )

        # profile webview
        self.profile_web_view = QWebEngineView(self.tabWidgetPage3)
        self.profileWebViewFrame.layout().addWidget(self.profile_web_view)
        self.profile_web_view.setSizePolicy(
            QSizePolicy.Expanding, QSizePolicy.Expanding
        )
        # Intercepter les changements d'URL pour ouvrir les liens externes
        self.profile_web_view.urlChanged.connect(self.on_profile_web_view_url_changed)

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

        # events
        self.refreshButton.clicked.connect(self._on_refresh_button_clicked)
        self.transferToButton.clicked.connect(self.transfer_to)
        self.transferFromButton.clicked.connect(self.transfer_from)
        self.customContextMenuRequested.connect(self.on_account_context_menu)
        # self.transfersListView.scroll_offset_changed.connect(self._update_ui)
        self.transfersListView.customContextMenuRequested.connect(
            self.on_transfers_context_menu
        )
        self.confirmIdentityButton.clicked.connect(
            self.on_confirm_identity_button_clicked
        )
        self.createIdentityButton.clicked.connect(
            self.on_create_identity_button_clicked
        )
        self.certifyIdentityButton.clicked.connect(
            self.on_certify_identity_button_clicked
        )
        self.identityChangeAccountButton.clicked.connect(
            self.on_identity_change_account_button_clicked
        )
        self.certificationsReceivedListView.customContextMenuRequested.connect(
            self.on_received_certifications_context_menu
        )
        self.certificationsIssuedListView.customContextMenuRequested.connect(
            self.on_issued_certifications_context_menu
        )
        self.profileChangeAccountButton.clicked.connect(
            self.on_profile_change_account_button_clicked
        )
        # application events
        self.application.event_dispatcher.add_event_listener(
            UnitEvent.EVENT_TYPE_CHANGED, lambda e: self._on_unit_changed_event()
        )

        self.transfers_model.init_data(
            self.history_scroll_offset, self.history_page_size
        )

        self._update_ui()

    def cleanup(self):
        """
        Clean up WebEngine resources

        Fix message: Release of profile requested but WebEnginePage still not deleted. Expect troubles !

        :return:
        """
        if hasattr(self, "profile_web_view"):
            # Clear the profile web view
            self.profile_web_view.deleteLater()
            del self.profile_web_view

    def closeEvent(self, event):
        """
        Triggered when user close main window or tab

        :param event:
        :return:
        """
        self.cleanup()
        super().closeEvent(event)

    def transfer_to(self):
        """
        When user click on transfer to button

        :return:
        """
        TransferWindow(
            self.application, None, self.account, parent=self.parent()
        ).exec_()

    def transfer_from(self):
        """
        When user click on transfer from button

        :return:
        """
        TransferWindow(
            self.application, self.account, None, parent=self.parent()
        ).exec_()

    def _on_refresh_button_clicked(self, _: QEvent):
        """
        Triggered when user click on Refresh button

        :param _: QEvent instance
        :return:
        """
        self.errorLabel.setText("")
        self.thread_manager.start(
            self.fetch_account_from_network,
            self._on_finished_fetch_account_from_network,
        )
        self.thread_manager.start(
            self.fetch_transfers_from_network,
            self.on_finished_fetch_transfers_from_network,
        )
        self.thread_manager.start(
            self.fetch_certifications_from_network,
            self.on_finished_fetch_certifications_from_network,
        )
        self.thread_manager.start(
            self.fetch_profile_from_network,
            self._on_finished_fetch_profile_from_network,
        )

    def _on_unit_changed_event(self):
        """
        Triggered when user change money units

        :return:
        """
        self._update_ui()
        self.transfers_model.init_data(
            self.history_scroll_offset, self.history_page_size
        )

    def fetch_account_from_network(self):
        """
        Get last account data from the network

        :return:
        """
        if not self.application.connections.node.is_connected():
            return

        self.refreshButton.setEnabled(False)

        try:
            self.application.accounts.network_update_account(self.account)
        except Exception as exception:
            self.errorLabel.setText(self._(str(exception)))
            logging.exception(exception)
        else:
            try:
                identity = self.application.identities.network_update_identity(
                    self.account.address
                )
            except Exception as exception:
                self.errorLabel.setText(self._(str(exception)))
                logging.exception(exception)
            else:
                if identity is not None:
                    try:
                        self.application.smiths.network_update_smith(identity.index)
                    except Exception as exception:
                        self.errorLabel.setText(self._(str(exception)))
                        logging.exception(exception)

    def _on_finished_fetch_account_from_network(self):
        """
        Triggered when async request fetch_account_from_network is finished

        :return:
        """
        self._update_ui()
        self.refreshButton.setEnabled(True)
        logging.debug("Account widget update %s", self.account.address)

    def fetch_transfers_from_network(self):
        """
        Get transfers data from the network

        :return:
        """
        if not self.application.connections.indexer.is_connected():
            return

        self.refreshButton.setEnabled(False)

        try:
            self.application.transfers.network_fetch_history_for_account(
                self.account, self.history_limit
            )
        except Exception as exception:
            self.errorLabel.setText(self._(str(exception)))
            logging.exception(exception)

        try:
            self.application.accounts.network_update_total_transfers_count(self.account)
        except Exception as exception:
            self.errorLabel.setText(self._(str(exception)))
            logging.exception(exception)

    def on_finished_fetch_transfers_from_network(self):
        """
        Triggered when async request fetch_transfers_from_network is finished

        :return:
        """
        self.account = self.application.accounts.get_by_address(self.account.address)
        self.transfers_model.init_data(
            self.history_scroll_offset, self.history_page_size
        )
        self._update_ui()
        self.refreshButton.setEnabled(True)
        logging.debug(
            "Finished fetch account transfers %s %s",
            self.account.name,
            self.account.address,
        )

    def fetch_certifications_from_network(self):
        """
        Get certifications data from the network

        :return:
        """
        if (
            self.identity is None
            or not self.application.connections.indexer.is_connected()
        ):
            return

        self.refreshButton.setEnabled(False)

        try:
            self.application.certifications.network_fetch_certifications_for_identity(
                self.identity.index
            )
        except Exception as exception:
            self.errorLabel.setText(self._(str(exception)))
            logging.exception(exception)

    def on_finished_fetch_certifications_from_network(self):
        """
        Triggered when async request fetch_certifications_from_network is finished

        :return:
        """
        self.certifications_received_model.init_data(
            self.identity.index if self.identity else None
        )
        self.certifications_issued_model.init_data(
            self.identity.index if self.identity else None
        )

        self.refreshButton.setEnabled(True)
        logging.debug(
            "Finished fetch identity certifications %s %s",
            self.account.name,
            self.account.address,
        )

    def fetch_profile_from_network(self):
        """
        Get last account data from the network

        :return:
        """
        if not self.application.connections.datapod.is_connected():
            return

        self.refreshButton.setEnabled(False)
        try:
            self.profile = self.application.profiles.network_update(self.account)
        except Exception as exception:
            self.errorLabel.setText(str(exception))
            logging.exception(exception)

    def _on_finished_fetch_profile_from_network(self):
        """
        Triggered when async request fetch_account_from_network is finished

        :return:
        """
        self._update_ui()
        self.refreshButton.setEnabled(True)
        logging.debug("Account profile widget update")

    def _update_ui(self):
        """
        Update UI from self.account

        :return:
        """
        # update local data from DB
        self.account = self.application.accounts.get_by_address(self.account.address)
        self.identity = self.application.identities.get_by_address(self.account.address)
        self.profile = self.application.profiles.get(self.account.address)

        unit_preference = self.application.repository.preferences.get(
            SELECTED_UNIT_PREFERENCES_KEY
        )
        if unit_preference is not None:
            amount = self.application.amounts.get_amount(unit_preference)
        else:
            amount = self.application.amounts.get_amount(AMOUNT_UNIT_KEY)

        if self.account.balance is None:
            self.balanceValueLabel.setText("?")
            self.balanceReservedValueLabel.setText("")
            self.transferFromButton.setEnabled(False)
        else:
            self.balanceValueLabel.setText(
                self.locale().toCurrencyString(
                    amount.value(self.account.balance), amount.symbol()
                )
            )
            display_reserved_balance = self.locale().toCurrencyString(
                amount.value(self.account.balance_reserved), amount.symbol()
            )
            self.balanceReservedValueLabel.setText(f"[-{display_reserved_balance}]")
            self.transferFromButton.setEnabled(True)

        if self.application.wallets.exists(self.account.address):
            if self.account.balance is None or self.account.balance == 0:
                self.transferFromButton.setEnabled(False)
            else:
                self.transferFromButton.setEnabled(True)
            if self.application.wallets.is_unlocked(self.account.address):
                self.lockStatusIcon.setPixmap(QPixmap(ICON_ACCOUNT_WALLET_UNLOCKED))
            else:
                self.lockStatusIcon.setPixmap(QPixmap(ICON_ACCOUNT_WALLET_LOCKED))
        else:
            self.lockStatusIcon.setPixmap(QPixmap(ICON_ACCOUNT_NO_WALLET))
            self.transferFromButton.setEnabled(False)

        if self.identity is not None:
            self.accountTabWidget.setTabVisible(1, True)
            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.identity.status.value]}";
                                    border-radius: 10%;
                                    background: "{identity_status_color[self.identity.status.value]}";
                                    """

            display_identity_icon = {
                IdentityStatus.UNCONFIRMED.value: QPixmap(
                    ICON_IDENTITY_MEMBER_NOT_VALIDATED
                ),
                IdentityStatus.UNVALIDATED.value: QPixmap(
                    ICON_IDENTITY_MEMBER_NOT_VALIDATED
                ),
                IdentityStatus.MEMBER.value: QPixmap(ICON_IDENTITY_MEMBER),
                IdentityStatus.NOT_MEMBER.value: QPixmap(ICON_IDENTITY_NOT_MEMBER),
                IdentityStatus.REVOKED.value: QPixmap(ICON_IDENTITY_NOT_MEMBER),
            }

            self.identityStatusIconLabel.setPixmap(
                display_identity_icon[self.identity.status.value]
            )
            identity_name = self.identity.name or ""
            self.identityNameValueLabel.setText(
                f"{identity_name}#{self.identity.index}"
            )
            self.identityValueLabel.setText(
                display_identity_status[self.identity.status.value]
            )
            self.identityValueLabel.setStyleSheet(identity_status_style)

            smith = self.application.smiths.get(self.identity.index)
            if smith is not None:
                display_smith_status = {
                    SmithStatus.INVITED.value: self._("Invited"),
                    SmithStatus.PENDING.value: self._("Pending"),
                    SmithStatus.SMITH.value: self._("Smith"),
                    SmithStatus.EXCLUDED.value: self._("Excluded"),
                }
                smith_status_color = {
                    SmithStatus.INVITED.value: "orange",
                    SmithStatus.PENDING.value: "orange",
                    SmithStatus.SMITH.value: "green",
                    SmithStatus.EXCLUDED.value: "red",
                }
                smith_status_style = f"""
                                     color: white;
                                     border: 3px solid "{smith_status_color[smith.status.value]}";
                                     border-radius: 10%;
                                     background: "{smith_status_color[smith.status.value]}";
                                     """
                self.smithValuelabel.setText(display_smith_status[smith.status.value])
                self.smithValuelabel.setStyleSheet(smith_status_style)
            else:
                self.smithValuelabel.setText(self._("No"))
                self.smithValuelabel.setStyleSheet("")

            if self.account.name is None:
                self.accountNameValueLabel.setText("")
            else:
                self.accountNameValueLabel.setText(f" - {self.account.name}")

            self.createIdentityButton.setVisible(
                self.identity.status == IdentityStatus.MEMBER
            )
            self.confirmIdentityButton.setVisible(
                self.identity.status == IdentityStatus.UNCONFIRMED
            )
            self.certifyIdentityButton.setVisible(
                self.identity.status == IdentityStatus.MEMBER
            )
        else:
            self.accountTabWidget.setTabVisible(1, False)
            self.confirmIdentityButton.hide()
            self.createIdentityButton.hide()
            self.certifyIdentityButton.hide()
            self.identityStatusIconLabel.setPixmap(QPixmap(ICON_IDENTITY_NOT_MEMBER))
            self.identityNameValueLabel.setText("")
            self.identityValueLabel.setText(self._("No"))
            self.smithValuelabel.setText(self._("No"))
            self.identityValueLabel.setStyleSheet("")
            self.smithValuelabel.setStyleSheet("")
            if self.account.name is None:
                self.accountNameValueLabel.setText("")
            else:
                self.accountNameValueLabel.setText(self.account.name)

        if self.account.root is None and self.account.path is None:
            self.derivationValueLabel.setText(self._("Root"))
        else:
            root_account = self.application.accounts.get_by_address(self.account.root)
            if root_account is not None and root_account.name is not None:
                self.derivationValueLabel.setFont(QFont())
                self.derivationValueLabel.setText(root_account.name + self.account.path)
            else:
                self.derivationValueLabel.setFont(self.monospace_font)
                self.derivationValueLabel.setText(
                    DisplayAddress(self.account.root).shorten + self.account.path
                )

        # transfers
        self.transfersCountValueLabel.setText(
            f"1...{self.history_limit}/{self.account.total_transfers_count}"
        )
        # current_page = int(
        #     (self.transfersListView.scroll_offset / self.history_page_size) + 1
        # )
        # self.transfersCountValueLabel.setText(
        #     str(
        #         f"Page {current_page}/{self.history_page_size} |"
        #         f"1...{self.history_limit}/{self.account.total_transfers_count}"
        #     )
        # )

        # profile
        if self.profile is not None:
            self.accountTabWidget.setTabVisible(2, True)
            self.profile_web_view.setHtml(self.get_profile_as_html())
        else:
            self.accountTabWidget.setTabVisible(2, False)

        # fix bug on tab titles not showing after setTabVisible()
        QTimer.singleShot(500, self._fix_tab_bar_geometry)

    def _fix_tab_bar_geometry(self):
        """
        Force Qt to calculate again the geometry of tab bar after setTabVisible()
        Fix bug when tab titles do not appear correctly
        """
        try:
            tab_bar = self.accountTabWidget.tabBar()
            if tab_bar:
                current_size = tab_bar.size()

                # Forcer un changement de taille
                tab_bar.resize(current_size.width() + 1, current_size.height())
                QApplication.processEvents()  # Important!
                tab_bar.resize(current_size.width(), current_size.height())
                QApplication.processEvents()
        except RuntimeError:
            # C++ object is destroy, can happen in tests
            pass

    def get_profile_as_html(self) -> str:
        """
        Display profile data as HTML in web view

        :return:
        """
        if self.profile is not None:
            updated_on_time_stamp = self.profile.data.get("time", 0)
            updated_on_localized_datetime_string = self.locale().toString(
                datetime.fromtimestamp(updated_on_time_stamp),
                QLocale.dateTimeFormat(self.locale(), QLocale.ShortFormat),
            )
            updated_on_localized_string = self._(
                "Updated at {localized_string}"
            ).format(localized_string=updated_on_localized_datetime_string)

            return create_profile_html(self.profile.data, updated_on_localized_string)

        return self._("No profile.")

    def on_profile_web_view_url_changed(self, url):
        """
        Triggered when user click on web link in profile web view

        :return:
        """
        if url.isValid() and url.scheme() in ["http", "https"]:
            QDesktopServices.openUrl(url)
            # Reload page to stay on original content
            self.profile_web_view.sender().setHtml(self.get_profile_as_html())

    def on_account_context_menu(self, position: QPoint):
        """
        When right button on account tab

        :return:
        """
        # display popup menu at click position
        menu = AccountPopupMenu(self.application, self.account, self)
        menu.exec_(self.mapToGlobal(position))

    def on_transfers_context_menu(self, position: QPoint):
        """
        When right button on transfers listview

        :return:
        """
        # get selected transfer
        transfer = self.transfersListView.currentIndex().internalPointer()
        if transfer is not None:
            # display popup menu at click position
            menu = AccountTransfersPopupMenu(
                self.application, self.account, transfer, self
            )
            menu.exec_(self.transfersListView.mapToGlobal(position))

    def on_received_certifications_context_menu(self, position: QPoint):
        """
        When right button on received certifications listview

        :return:
        """
        # get selected certification
        certification = (
            self.certificationsReceivedListView.currentIndex().internalPointer()
        )
        if certification is not None:
            # display popup menu at click position
            menu = AccountCertificationsPopupMenu(
                self.application, self.account, certification, self
            )
            menu.exec_(self.certificationsReceivedListView.mapToGlobal(position))

    def on_issued_certifications_context_menu(self, position: QPoint):
        """
        When right button on issued certifications listview

        :return:
        """
        # get selected certification
        certification = (
            self.certificationsIssuedListView.currentIndex().internalPointer()
        )
        if certification is not None:
            # display popup menu at click position
            menu = AccountCertificationsPopupMenu(
                self.application, self.account, certification, self
            )
            menu.exec_(self.certificationsIssuedListView.mapToGlobal(position))

    def on_confirm_identity_button_clicked(self):
        """
        Triggered when user click on confirm_identity_button

        :return:
        """
        window = IdentityConfirmWindow(self.application, self.account)
        window.exec_()

    def on_create_identity_button_clicked(self):
        """
        Triggered when user click on create_identity_button

        :return:
        """
        window = IdentityCreateWindow(self.application, self.account)
        window.exec_()

    def on_certify_identity_button_clicked(self):
        """
        Triggered when user click on certify_identity_button

        :return:
        """
        if self.identity is not None:
            window = IdentityCertifyWindow(self.application, self.account)
            window.exec_()

    def on_identity_change_account_button_clicked(self):
        """
        Triggered when user click on identity change account button

        :return:
        """
        if self.identity is not None:
            window = IdentityChangeAccountWindow(self.application, self.account)
            window.exec_()

    def on_profile_change_account_button_clicked(self):
        """
        Triggered when user click on identity change account button

        :return:
        """
        if self.profile is not None:
            window = ProfileChangeAccountWindow(self.application, self.account)
            window.exec_()

    def on_identities_updated_event_bus(self, identities: List[Identity]):
        """
        Triggered when identities are updated

        :param identities: List of Identity instance
        :return:
        """
        if self.account.address in [identity.address for identity in identities]:
            self._update_ui()
            self.certifications_issued_model.init_data(
                self.identity.index if self.identity else None
            )
            self.certifications_received_model.init_data(
                self.identity.index if self.identity else None
            )

    def on_account_identities_updated_event_bus(self, accounts: List[Account]):
        """
        Triggered when account identities are updated (some identities are None, so we are using accounts!)

        :param accounts: List of Account instance
        :return:
        """
        if self.account.address in [account.address for account in accounts]:
            self._update_ui()
            self.certifications_issued_model.init_data(
                self.identity.index if self.identity else None
            )
            self.certifications_received_model.init_data(
                self.identity.index if self.identity else None
            )

    def on_account_profiles_updated_event_bus(self, accounts: List[Account]):
        """
        Triggered when account profiles are updated (some profiles are None, so we are using accounts!)

        :param accounts: List of Account instance
        :return:
        """
        if self.account.address in [account.address for account in accounts]:
            self._update_ui()

    def on_account_balances_updated(self, accounts: List[Account]):
        """
        Triggered when a on_account_balances_updated event is dispatched

        :param accounts: List of Account instances
        :return:
        """
        if self.account.address in [account.address for account in accounts]:
            self._update_ui()
            self.transfers_model.init_data(
                self.history_scroll_offset, self.history_page_size
            )

    def on_account_wallet_updated_event_bus(self, account: Account):
        """
        Triggered when the account wallet is updated

        :param account: Account instance
        :return:
        """
        self._update_ui()

    def on_account_renamed_event_bus(self, account: Account):
        """
        Triggered when the account is renamed

        :param account: Account instance
        :return:
        """
        self._update_ui()
        self.certifications_issued_model.init_data(
            self.identity.index if self.identity else None
        )
        self.certifications_received_model.init_data(
            self.identity.index if self.identity else None
        )
        self.transfers_model.init_data()

    def repository_data_updated_event_bus(self):
        """
        Triggered when the main window has updated repository data

        :return:
        """
        self._update_ui()
        self.certifications_issued_model.init_data(
            self.identity.index if self.identity else None
        )
        self.certifications_received_model.init_data(
            self.identity.index if self.identity else None
        )
        self.transfers_model.init_data()


if __name__ == "__main__":
    qapp = QApplication(sys.argv)

    application_ = Application(DATA_PATH)
    account_ = application_.accounts.get_by_address(
        "g1LAN1C5rktuWS2giuZ7z1CydYKV2HQeH2rj8A6qY3CemwTVz"
    )
    if account_ is None:
        account_ = application_.accounts.create_new_root_account(
            mnemonic="bottom drive obey lake curtain smoke basket hold race lonely fit walk",
            language_code="en",
            crypto_type=AccountCryptoType.ED25519,
            name="Root ED",
            password="aaaaaa",
        )

    main_window = QMainWindow()
    main_window.show()

    main_window.setCentralWidget(AccountWidget(application_, account_, main_window))

    sys.exit(qapp.exec_())
