# 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 QPoint, pyqtSignal
from PyQt5.QtGui import QFont, QWheelEvent
from PyQt5.QtWidgets import QApplication, QListView, QMainWindow, QWidget

from tikka.domains.application import Application
from tikka.domains.entities.account import Account, AccountCryptoType
from tikka.domains.entities.constants import TEST_DATA_PATH
from tikka.domains.entities.events import (
    UnitEvent,
)
from tikka.domains.entities.transfer import Transfer
from tikka.slots.pyqt.entities.constants import (
    ACCOUNT_TRANSFER_HISTORY_LIMIT,
    ADDRESS_MONOSPACE_FONT_NAME,
)
from tikka.slots.pyqt.entities.worker import ThreadManager
from tikka.slots.pyqt.models.transfers import TransferItemDelegate, TransfersListModel
from tikka.slots.pyqt.resources.gui.widgets.transfers_rc import Ui_TransfersWidget
from tikka.slots.pyqt.widgets.account_transfers_menu import AccountTransfersPopupMenu
from tikka.slots.pyqt.windows.transfers_export import TransfersExportWindow


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 TransfersWidget(QWidget, Ui_TransfersWidget):
    """
    TransfersWidget class
    """

    def __init__(
        self,
        application: Application,
        account: Account,
        parent: Optional[QWidget] = None,
    ) -> None:
        """
        Init TransfersWidget 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.account: Account = account

        self.history_limit = ACCOUNT_TRANSFER_HISTORY_LIMIT
        self.history_page_size = 100
        self.history_scroll_offset = 0

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

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

        # 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.application, self.locale())
        )
        self.transfersListView.setResizeMode(QListView.Adjust)

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

        # events
        # self.transfersListView.scroll_offset_changed.connect(self._update_ui)
        self.transfersExportOFXButton.clicked.connect(
            self.on_transfers_export_ofx_button_clicked
        )
        self.transfersListView.customContextMenuRequested.connect(
            self.on_transfers_context_menu
        )

        # application events
        self.application.event_dispatcher.add_event_listener(
            UnitEvent.EVENT_TYPE_CHANGED, lambda e: self._on_unit_changed_event()
        )

        self.update_ui()

    def on_transfers_export_ofx_button_clicked(self):
        """
        When user click on transfers Export OFX button

        :return:
        """
        TransfersExportWindow(
            self.application, self.account.address, self.parentWidget()
        ).exec_()

    def async_refresh_data_from_network(self):
        """
        Triggered when user click on Refresh button

        :return:
        """
        self.thread_manager.start(
            self.fetch_transfers_and_uds_from_network,
            self.on_finished_fetch_transfers_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_transfers_and_uds_from_network(self):
        """
        Get transfers and UDs data from the network

        :return:
        """
        if not self.application.connections.indexer.is_connected():
            return
        try:
            self.application.transfers.network_fetch_history_for_account(
                self.account, self.history_limit
            )
        except Exception as exception:
            logging.exception(exception)

        try:
            self.application.universal_dividends.network_fetch_history_for_account(
                self.account, self.history_limit
            )
        except Exception as exception:
            logging.exception(exception)

        # todo: fetch UDs total count ?

        try:
            self.application.accounts.network_update_total_transfers_count(self.account)
        except Exception as exception:
            logging.exception(exception)

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

        :return:
        """
        self.update_ui()
        logging.debug(
            "Finished fetch account transfers and UDs %s %s",
            self.account.name,
            self.account.address,
        )

    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)

        # transfers tab
        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}"
        #     )
        # )

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

    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 and isinstance(transfer, Transfer):
            # display popup menu at click position
            menu = AccountTransfersPopupMenu(
                self.application, self.account, transfer, self
            )
            menu.exec_(self.transfersListView.mapToGlobal(position))


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

    application_ = Application(TEST_DATA_PATH)

    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",
    )
    account2_ = application_.accounts.create_new_account(
        mnemonic="bottom drive obey lake curtain smoke basket hold race lonely fit walk",
        language_code="en",
        derivation="//0",
        crypto_type=AccountCryptoType.ED25519,
        name="//0 ED",
        password="aaaaaa",
    )
    from datetime import datetime

    from tikka.domains.entities.universal_dividend import UniversalDividend

    application_.transfers.add(
        Transfer(
            "xxxxx",
            account_.address,
            None,
            None,
            account2_.address,
            None,
            None,
            1000,
            datetime(2026, 1, 2, 18, 27),
            "comment test",
            "ASCII",
        )
    )
    application_.universal_dividends.add(
        UniversalDividend(
            "yyyy", account_.address, 1, "root", 1000, datetime(2026, 1, 5, 12, 0)
        )
    )

    main_window = QMainWindow()
    main_window.show()

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

    sys.exit(qapp.exec_())
