import logging

from PyQt6.QtCore import (
    QAbstractListModel,
    Qt,
    QModelIndex,
    QPoint,
    QSize,
    QTimer,
    QVariant,
)
from PyQt6.QtGui import QPainter, QFontMetrics
from PyQt6.QtWidgets import QListView, QStyledItemDelegate


logger = logging.getLogger(__name__)


class TextlistModel(QAbstractListModel):
    """

    Public methods:

    - add: add item to list
    - remove: remove item from list
    - clear: clear list
    - __len__: for truth judgement
    """

    def __init__(self, parent):
        super().__init__(parent)
        self._items = []

    @property
    def items(self):
        return self._items

    def add(self, item):
        start = len(self._items)
        end = start + 1
        self.beginInsertRows(QModelIndex(), start, end)
        self._items.append(item)
        self.endInsertRows()

    def remove(self, item):
        row = self._items.index(item)
        self.beginRemoveRows(QModelIndex(), row, row + 1)
        self._items.remove(item)
        self.endRemoveRows()

    def clear(self):
        total_length = len(self.items)
        self.beginRemoveRows(QModelIndex(), 0, total_length - 1)
        self._items = []
        self.endRemoveRows()

    def __len__(self):
        return len(self._items)

    def rowCount(self, _=QModelIndex()):
        return len(self.items)

    def flags(self, index):  # pylint: disable=no-self-use
        if not index.isValid():
            return 0
        flags = Qt.ItemFlag.ItemIsSelectable | Qt.ItemFlag.ItemIsEnabled
        return flags

    def data(self, index, role=Qt.ItemDataRole.DisplayRole):
        if not index.isValid():
            return QVariant()
        row = index.row()
        item = self.items[row]
        if role == Qt.ItemDataRole.UserRole:
            return item
        return QVariant()


class TextlistDelegate(QStyledItemDelegate):
    def __init__(self, parent=None):
        super().__init__(parent=parent)

    def paint(self, painter, option, index):
        return super().paint(painter, option, index)

    def sizeHint(self, option, index):
        size = super().sizeHint(option, index)
        if index.isValid():
            return QSize(size.width(), 25)
        return size


class TextlistView(QListView):
    """
    .. versiondeprecated:: 3.9
        A TextlistView should have scrollbar by default.
    """

    def __init__(self, parent=None, **kwargs):
        super().__init__(parent)

        self.delegate = TextlistDelegate(self)
        self.setItemDelegate(self.delegate)

        self._result_timer = QTimer(self)
        self._result_timer.timeout.connect(self.__on_timeout)
        self._results = {}  # {row: [index, True]}

        self.setAttribute(Qt.WidgetAttribute.WA_MacShowFocusRect, False)
        self.setHorizontalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff)
        self.setVerticalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff)

    def __on_timeout(self):
        self._result_timer.stop()
        self._results.clear()
        self.viewport().update()

    def wheelEvent(self, e):  # pylint: disable=no-self-use
        e.ignore()

    def sizeHint(self):
        height = 10
        for i in range(0, self.model().rowCount()):
            height += self.sizeHintForRow(i)
        return QSize(self.width(), height)

    def rowsInserted(self, parent, start, end):
        super().rowsInserted(parent, start, end)
        self.setFixedHeight(self.sizeHint().height())

    def rowsAboutToBeRemoved(self, parent, start, end):
        super().rowsAboutToBeRemoved(parent, start, end)
        self.setFixedHeight(self.sizeHint().height())

    def paintEvent(self, e):
        super().paintEvent(e)
        if not self._results:
            return
        painter = QPainter(self.viewport())
        painter.setRenderHint(QPainter.RenderHint.Antialiasing)
        fm = QFontMetrics(self.font())
        for _, result in self._results.items():
            index, state = result
            rect = self.rectForIndex(index)
            if state is None:
                text = "😶"
            elif state is True:
                text = "👋"
            else:
                text = "🙁"
            x = rect.width() - 20 + rect.x()
            # Vertically center the text
            y = (rect.height() + fm.ascent() - fm.descent()) // 2 + rect.y()
            topleft = QPoint(x, y)
            painter.drawText(topleft, text)
