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

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 Account
from tikka.domains.entities.constants import AMOUNT_UNIT_KEY
from tikka.domains.entities.identity import Identity, IdentityStatus
from tikka.domains.entities.transfer import Transfer
from tikka.domains.entities.universal_dividend import UniversalDividend
from tikka.interfaces.adapters.repository.transfers import TransfersRepositoryInterface
from tikka.interfaces.adapters.repository.universal_dividends import (
    UniversalDividendsRepositoryInterface,
)
from tikka.slots.pyqt.entities.constants import SELECTED_UNIT_PREFERENCES_KEY
from tikka.slots.pyqt.widgets.account_transfer_row import AccountTransferRowWidget
from tikka.slots.pyqt.widgets.account_universal_dividend_row import (
    AccountUniversalDividendRowWidget,
)


class TransferItemDelegate(QStyledItemDelegate):
    """
    TransferItemDelegate class
    """

    def __init__(self, application: Application, locale: QLocale):
        """
        Init TransferItemDelegate instance
        """
        super().__init__()

        self.application = application
        self.locale = locale

        # set item height >= AccountTransferRowWidget.height
        self.item_height = 40

    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 transfer 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)

        # calculate balance display by unit preference
        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)

        transfer_account = index.data(TransfersListModel.TransferAccountRole)
        transfer_identity = index.data(TransfersListModel.TransferIdentityRole)
        transfer: Transfer = index.data(TransfersListModel.TransferRole)
        if transfer is not None:
            account_address_label = (
                f"{transfer_account.address[:4]}…{transfer_account.address[-5:]}"
            )
            account_name_label = transfer_account.name
            account_has_identity = transfer_identity is not None
            identity_name_label = (
                f"{transfer_identity.name}#{transfer_identity.index}"
                if transfer_identity is not None
                else ""
            )
            datetime_label = self.locale.toString(
                transfer.timestamp.astimezone(),
                QLocale.dateTimeFormat(self.locale, QLocale.ShortFormat),
            )
            comment_type = transfer.comment_type
            comment_label = ""
            if transfer.comment is not None:
                if transfer.comment_type is not None:
                    if (
                        transfer.comment_type.upper() == "ASCII"
                        or transfer.comment_type.upper() == "UNICODE"
                    ):
                        comment_label = transfer.comment
                    elif transfer.comment_type.upper() == "RAW":
                        comment_label = (
                            f"RAW:{transfer.comment[:min(len(transfer.comment), 10)]}..."
                            f"{transfer.comment[max(-len(transfer.comment), -10):]}"
                        )
                    elif transfer.comment_type.upper() == "CID":
                        comment_label = f"CID:{transfer.comment}"
                else:
                    comment_label = transfer.comment

            if transfer.issuer_address == transfer_account.address:
                amount_sign = ""
            else:
                amount_sign = "-"
            amount_str = self.locale.toCurrencyString(
                amount.value(transfer.amount), amount.symbol()
            )
            amount_label = f"{amount_sign}{amount_str}"

            """ Replace item row by the widget """
            widget: Union[
                AccountTransferRowWidget, AccountUniversalDividendRowWidget
            ] = AccountTransferRowWidget(
                account_address_label,
                account_name_label,
                identity_name_label,
                account_has_identity,
                datetime_label,
                amount_label,
                comment_label,
                comment_type,
            )  # Crée le widget

        universal_dividend: UniversalDividend = index.data(
            TransfersListModel.UniversalDividendRole
        )
        if universal_dividend is not None:
            identity_name_label = f"{universal_dividend.identity_name}#{universal_dividend.identity_index}"
            datetime_label = self.locale.toString(
                universal_dividend.timestamp.astimezone(),
                QLocale.dateTimeFormat(self.locale, QLocale.ShortFormat),
            )
            amount_label = self.locale.toCurrencyString(
                amount.value(universal_dividend.amount), amount.symbol()
            )

            """ Replace item row by the widget """
            widget = AccountUniversalDividendRowWidget(
                identity_name_label, datetime_label, amount_label
            )  # Crée le widget

        # set widget background
        # widget.setAutoFillBackground(True)

        # 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()


def normalize_to_utc(dt: datetime) -> datetime:
    """Convertit un datetime en UTC naïf (sans fuseau)."""
    if dt.tzinfo is not None:
        dt = dt.astimezone(timezone.utc).replace(tzinfo=None)
    return dt


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

    (
        TransferRole,
        TransferAccountRole,
        TransferIdentityRole,
        UniversalDividendRole,
    ) = range(Qt.UserRole, Qt.UserRole + 4)

    def __init__(self, application: Application, address: str):
        """
        Init TransfersListModel with account address to get account transfers list

        :param application: Application instance
        :param address: Account address
        """
        super().__init__()

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

        self.address = address
        self.transfers: List[Transfer] = []
        self.universal_dividends: List[UniversalDividend] = []
        self.transfer_and_dividend_combined: List[
            Union[Transfer, UniversalDividend]
        ] = []

    def init_data(self, offset: int = 0, limit: int = 100):
        """
        Fill data from repository

        :param offset: Offset in DB list to paginate
        :param limit: Limit page size
        :return:
        """
        # 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)
        else:
            self.amount = self.application.amounts.get_amount(AMOUNT_UNIT_KEY)

        self.beginResetModel()
        self.transfers = self.application.transfers.repository.list(
            self.address,
            offset=offset,
            limit=limit,
            sort_column=TransfersRepositoryInterface.COLUMN_TIMESTAMP,
            sort_order=TransfersRepositoryInterface.SORT_ORDER_DESCENDING,
        )
        self.universal_dividends = self.application.universal_dividends.list(
            self.address,
            offset=offset,
            limit=limit,
            sort_column=UniversalDividendsRepositoryInterface.COLUMN_TIMESTAMP,
            sort_order=UniversalDividendsRepositoryInterface.SORT_ORDER_DESCENDING,
        )

        self.transfer_and_dividend_combined = self.transfers + self.universal_dividends
        self.transfer_and_dividend_combined = sorted(
            self.transfer_and_dividend_combined,
            key=lambda x: normalize_to_utc(x.timestamp),
            reverse=True,
        )
        self.endResetModel()

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

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

    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.transfers):
            return QAbstractItemModel.createIndex(
                self, row, column, self.transfer_and_dividend_combined[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()
        row_data = self.transfer_and_dividend_combined[row]
        if isinstance(row_data, Transfer):
            transfer = row_data
            if role == self.TransferRole:
                return QVariant(transfer)

            if role == self.TransferAccountRole:
                if transfer.issuer_address != self.address:
                    account = self.application.accounts.get_by_address(
                        transfer.issuer_address
                    )
                    if account is None:
                        account = Account(address=transfer.issuer_address)
                else:
                    account = self.application.accounts.get_by_address(
                        transfer.receiver_address
                    )
                    if account is None:
                        account = Account(address=transfer.receiver_address)
                return QVariant(account)

            if role == self.TransferIdentityRole:
                if transfer.issuer_address != self.address:
                    if transfer.issuer_identity_index is not None:
                        return QVariant(
                            Identity(
                                address=transfer.issuer_address,
                                name=transfer.issuer_identity_name,
                                index=transfer.issuer_identity_index,
                                removable_on=0,
                                next_creatable_on=0,
                                status=IdentityStatus.NOT_MEMBER,
                                old_address=None,
                                first_eligible_ud=0,
                            )
                        )
                    else:
                        return QVariant(None)
                else:
                    if transfer.receiver_identity_index is not None:
                        return QVariant(
                            Identity(
                                address=transfer.receiver_address,
                                name=transfer.receiver_identity_name,
                                index=transfer.receiver_identity_index,
                                removable_on=0,
                                next_creatable_on=0,
                                status=IdentityStatus.NOT_MEMBER,
                                old_address=None,
                                first_eligible_ud=0,
                            )
                        )
                    else:
                        return QVariant(None)

        else:
            universal_dividend = row_data
            if role == self.UniversalDividendRole:
                return QVariant(universal_dividend)

        return QVariant()
