"""Internal implementation details for CuteElide.

Do not import from this module directly. Use `from cuteelide import cuteelide`.
"""

from __future__ import annotations

from dataclasses import dataclass

from PySide6.QtCore import QRect, QSize, Qt
from PySide6.QtGui import QFont, QFontMetrics, QPainter, QPalette
from PySide6.QtWidgets import QLabel, QWidget

ElideMode = Qt.TextElideMode


@dataclass(frozen=True)
class ElideOptions:
    mode: Qt.TextElideMode = Qt.TextElideMode.ElideRight
    alignment: Qt.AlignmentFlag = Qt.AlignmentFlag.AlignLeft | Qt.AlignmentFlag.AlignVCenter
    padding: int = 0


def elide_text(text: str, font: QFont, max_width: int, mode: Qt.TextElideMode) -> str:
    if not text:
        return ""
    if max_width <= 0:
        return ""
    metrics = QFontMetrics(font)
    return metrics.elidedText(text, mode, max_width)


def _effective_rect(rect: QRect, padding: int) -> QRect:
    if padding <= 0:
        return rect
    return QRect(
        rect.left() + padding,
        rect.top(),
        max(0, rect.width() - (padding * 2)),
        rect.height(),
    )


def draw_elided_text(
    painter: QPainter,
    rect: QRect,
    text: str,
    *,
    font: QFont | None = None,
    palette: QPalette | None = None,
    enabled: bool = True,
    options: ElideOptions | None = None,
) -> str:
    if options is None:
        options = ElideOptions()

    use_font = font or painter.font()
    use_palette = palette or painter.palette()
    draw_rect = _effective_rect(rect, options.padding)

    if draw_rect.width() <= 0 or draw_rect.height() <= 0:
        return ""

    elided = elide_text(text, use_font, draw_rect.width(), options.mode)
    painter.save()
    painter.setFont(use_font)
    color_group = QPalette.ColorGroup.Normal if enabled else QPalette.ColorGroup.Disabled
    painter.setPen(use_palette.color(color_group, QPalette.ColorRole.WindowText))
    flags = options.alignment | Qt.TextFlag.TextSingleLine
    painter.drawText(draw_rect, flags, elided)
    painter.restore()
    return elided


class ElidingLabel(QLabel):
    def __init__(self, parent: QWidget | None = None) -> None:
        super().__init__(parent)
        self._full_text = ""
        self._options = ElideOptions()
        self.setWordWrap(False)
        self.setMinimumWidth(0)

    def setFullText(self, text: str) -> None:
        self._full_text = text or ""
        super().setText("")
        self.update()

    def fullText(self) -> str:
        return self._full_text

    def setText(self, text: str) -> None:
        self.setFullText(text)

    def text(self) -> str:
        return self._full_text

    def setElideMode(self, mode: Qt.TextElideMode) -> None:
        self._options = ElideOptions(
            mode=mode, alignment=self._options.alignment, padding=self._options.padding
        )
        self.update()

    def elideMode(self) -> Qt.TextElideMode:
        return self._options.mode

    def setElideAlignment(self, alignment: Qt.AlignmentFlag) -> None:
        self._options = ElideOptions(
            mode=self._options.mode, alignment=alignment, padding=self._options.padding
        )
        self.update()

    def elideAlignment(self) -> Qt.AlignmentFlag:
        return self._options.alignment

    def setElidePadding(self, padding: int) -> None:
        self._options = ElideOptions(
            mode=self._options.mode, alignment=self._options.alignment, padding=padding
        )
        self.update()

    def elidePadding(self) -> int:
        return self._options.padding

    def sizeHint(self) -> QSize:
        base = super().sizeHint()
        height = max(base.height(), self.fontMetrics().height() + 2)
        return QSize(base.width(), height)

    def minimumSizeHint(self) -> QSize:
        height = max(super().minimumSizeHint().height(), self.fontMetrics().height() + 2)
        return QSize(0, height)

    def paintEvent(self, event) -> None:
        super().paintEvent(event)
        if not self._full_text:
            return
        painter = QPainter(self)
        painter.setRenderHint(QPainter.RenderHint.TextAntialiasing, True)
        draw_elided_text(
            painter,
            self.contentsRect(),
            self._full_text,
            font=self.font(),
            palette=self.palette(),
            enabled=self.isEnabled(),
            options=self._options,
        )
        painter.end()
