# 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/>.
from typing import List

from PyQt5.QtCore import (
    QAbstractItemModel,
    QAbstractListModel,
    QLocale,
    QModelIndex,
    QSize,
    Qt,
    QVariant,
)
from PyQt5.QtGui import QPainter, QPalette
from PyQt5.QtWidgets import QStyle, QStyledItemDelegate, QStyleOptionViewItem

from tikka.domains.application import Application
from tikka.domains.entities.account import SearchAccount
from tikka.domains.entities.constants import AMOUNT_UNIT_KEY
from tikka.domains.entities.identity import IdentityStatus
from tikka.domains.entities.smith import SmithStatus
from tikka.slots.pyqt.entities.constants import (
    SELECTED_UNIT_PREFERENCES_KEY,
)
from tikka.slots.pyqt.widgets.account_search_row import AccountSearchRowWidget


class AccountSearchItemDelegate(QStyledItemDelegate):
    """
    AccountSearchItemDelegate class
    """

    def __init__(self):
        """
        Init AccountSearchItemDelegate instance
        """
        super().__init__()

        # set item height >= AccountSearchItemDelegate.height
        self.item_height = 54

    def sizeHint(self, option: QStyleOptionViewItem, index: QModelIndex):
        """
        Return item size

        Force height

        :param option: QStyleOptionViewItem instance
        :param index: QModelIndex instance
        :return:
        """
        return QSize(option.rect.width(), self.item_height)

    def paint(
        self, painter: QPainter, option: QStyleOptionViewItem, index: QModelIndex
    ) -> None:
        """
        Draw account search row item in listview

        :param painter: QPainter instance
        :param option: QStyleOptionViewItem instance
        :param index: QModelIndex instance
        :return:
        """
        # Init style option
        style_option = QStyleOptionViewItem(option)
        self.initStyleOption(style_option, index)

        search_account: SearchAccount = index.data(
            AccountSearchListModel.SearchAccountRole
        )
        balance_label = index.data(AccountSearchListModel.AccountBalanceRole)
        account_v1_address = index.data(AccountSearchListModel.AccountV1AddressRole)

        if search_account.identity_index is not None:
            if search_account.identity_name is None:
                identity_name_label = f"#{search_account.identity_index}"
            else:
                identity_name_label = (
                    f"{search_account.identity_name}#{search_account.identity_index}"
                )
            identity_status_label = index.data(
                AccountSearchListModel.IdentityStatusRole
            )
            identity_status_color = index.data(
                AccountSearchListModel.IdentityStatusColorRole
            )
        else:
            identity_name_label = ""
            identity_status_label = ""
            identity_status_color = ""

        is_smith = (
            search_account.smith_status is not None
            and search_account.smith_status.value == SmithStatus.SMITH.value
        )

        """ Replace item row by the widget """
        widget = AccountSearchRowWidget(
            search_account.address,
            balance_label,
            account_v1_address,
            identity_name_label,
            identity_status_label,
            identity_status_color,
            is_smith,
            search_account.linked_identity,
        )

        # Modify widget palette depending on behavior (hover, selection)
        palette = widget.palette()

        # 🔹 Check if the line number is even (alternate color effect)
        if index.row() % 2 == 1:  # Even lines use AlternateBase color
            base_color = option.palette.color(QPalette.AlternateBase)
        else:
            base_color = option.palette.color(QPalette.Base)

        if option.state & QStyle.State_Selected:
            palette.setColor(
                QPalette.Window, option.palette.highlight().color()
            )  # selection color
        elif option.state & QStyle.State_MouseOver:
            palette.setColor(
                QPalette.Window, option.palette.color(QPalette.Highlight).lighter(200)
            )  # Hover
        else:
            palette.setColor(
                QPalette.Window, base_color
            )  # normal or alternate background

        widget.setPalette(palette)  # Apply palette on widget
        widget.update()  # 🔥 Force display of new colors

        widget.resize(option.rect.size())  # Set size to the row size
        painter.save()
        painter.translate(option.rect.topLeft())  # Place the widget correctly
        widget.render(painter)
        painter.restore()


class AccountSearchListModel(QAbstractListModel):
    """
    AccountSearchListModel class that drives the population of list display
    """

    (
        SearchAccountRole,
        AccountBalanceRole,
        AccountV1AddressRole,
        IdentityStatusRole,
        IdentityStatusColorRole,
        AccountNameRole,
    ) = range(Qt.UserRole, Qt.UserRole + 6)

    def __init__(
        self,
        application: Application,
        locale: QLocale,
    ):
        """
        Init AccountSearchListModel

        :param application: Application instance
        :param locale: QLocale instance
        """
        super().__init__()

        self.application = application
        self._ = self.application.translator.gettext
        self.amount = self.application.amounts.get_amount(AMOUNT_UNIT_KEY)
        self.ss58_format = self.application.currencies.get_current().ss58_format

        # get parent widget locale to localize balance display
        self.locale = locale
        self.accounts: List[SearchAccount] = []

    def init_data(self, accounts: List[SearchAccount], limit: int):
        """
        Fill data from repository

        :param accounts: Search results
        :param limit: Max number of results
        :return:
        """
        self.beginResetModel()

        # calculate balance display by unit preference
        unit_preference = self.application.repository.preferences.get(
            SELECTED_UNIT_PREFERENCES_KEY
        )
        if unit_preference is not None:
            self.amount = self.application.amounts.get_amount(unit_preference)

        self.accounts = accounts
        self.endResetModel()

    def rowCount(self, _: QModelIndex = QModelIndex()) -> int:
        """
        Return row count

        :param _: QModelIndex instance
        :return:
        """
        return len(self.accounts)

    def index(  # type: ignore
        self, row: int, column: int, parent: QModelIndex = QModelIndex()
    ) -> QModelIndex:
        """
        Return QModelIndex for row, column and parent

        :param row: Row index
        :param column: Column index
        :param parent: Parent QModelIndex instance
        :return:
        """
        if not QAbstractItemModel.hasIndex(self, row, column, parent):
            return QModelIndex()

        if row < len(self.accounts):
            return QAbstractItemModel.createIndex(self, row, column, self.accounts[row])

        return QModelIndex()

    def data(self, index: QModelIndex, role: int = Qt.DisplayRole) -> QVariant:
        """
        Return data of cell for column index.column

        :param index: QModelIndex instance
        :param role: Item data role
        :return:
        """
        row = index.row()
        search_account = self.accounts[row]
        data = QVariant()

        if role == self.SearchAccountRole:
            return QVariant(search_account)

        if role == self.AccountBalanceRole:
            return QVariant(
                self.locale.toCurrencyString(
                    self.amount.value(search_account.balance), self.amount.symbol()
                )
            )

        if role == self.AccountV1AddressRole and search_account.legacy_v1:
            return QVariant(search_account.get_v1_address(self.ss58_format, True))

        if (
            role == self.IdentityStatusRole
            and search_account.identity_status 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"),
            }
            return QVariant(
                display_identity_status[search_account.identity_status.value]
            )

        if (
            role == self.IdentityStatusColorRole
            and search_account.identity_status is not None
        ):
            display_identity_status_color = {
                IdentityStatus.UNCONFIRMED.value: "orange",
                IdentityStatus.UNVALIDATED.value: "orange",
                IdentityStatus.MEMBER.value: "green",
                IdentityStatus.NOT_MEMBER.value: "red",
                IdentityStatus.REVOKED.value: "red",
            }
            return QVariant(
                display_identity_status_color[search_account.identity_status.value]
            )

        return data
