"""MMessage"""

from typing import ClassVar

from qtpy import QtCore, QtWidgets

from .avatar import MAvatar
from .label import MLabel
from .loading import MLoading
from .qt import MPixmap
from .theme import MTheme
from .tool_button import MToolButton


class MMessage(QtWidgets.QWidget):
    """
    Display global messages as feedback in response to user operations.
    """

    InfoType = "info"
    SuccessType = "success"
    WarningType = "warning"
    ErrorType = "error"
    LoadingType = "loading"

    default_config: ClassVar[dict[str, int]] = {"duration": 2, "top": 24}

    sig_closed = QtCore.Signal()

    def __init__(
        self,
        text: str,
        duration: int | None = None,
        dayu_type: str | None = None,
        closable: bool = False,
        parent: QtWidgets.QWidget | None = None,
    ):
        super().__init__(parent)
        self.setObjectName("message")
        self.setWindowFlags(
            QtCore.Qt.WindowType.FramelessWindowHint | QtCore.Qt.WindowType.Dialog
        )
        self.setAttribute(QtCore.Qt.WidgetAttribute.WA_DeleteOnClose)

        if dayu_type == MMessage.LoadingType:
            _icon_label = MLoading.tiny()
        else:
            _icon_label = MAvatar.tiny()
            current_type = dayu_type or MMessage.InfoType
            _icon_label.set_dayu_image(
                MPixmap(
                    f"{current_type}_fill.svg",
                    vars(MTheme()).get(current_type + "_color"),
                )
            )

        self._content_label = MLabel(parent=self)
        self._content_label.setText(text)

        self._close_button = (
            MToolButton(parent=self).icon_only().svg("close_line.svg").tiny()
        )
        self._close_button.clicked.connect(self.close)
        self._close_button.setVisible(closable or False)

        self._main_lay = QtWidgets.QHBoxLayout()
        self._main_lay.addWidget(_icon_label)
        self._main_lay.addWidget(self._content_label)
        self._main_lay.addStretch()
        self._main_lay.addWidget(self._close_button)
        self.setLayout(self._main_lay)

        _close_timer = QtCore.QTimer(self)
        _close_timer.setSingleShot(True)
        _close_timer.timeout.connect(self.close)
        _close_timer.timeout.connect(self.sig_closed)
        _close_timer.setInterval((duration or self.default_config["duration"]) * 1000)

        _ani_timer = QtCore.QTimer(self)
        _ani_timer.timeout.connect(self._fade_out)
        _ani_timer.setInterval(
            (duration or self.default_config["duration"]) * 1000 - 300
        )

        _close_timer.start()
        _ani_timer.start()

        self._pos_ani = QtCore.QPropertyAnimation(self)
        self._pos_ani.setTargetObject(self)
        self._pos_ani.setEasingCurve(QtCore.QEasingCurve.Type.OutCubic)
        self._pos_ani.setDuration(300)
        self._pos_ani.setPropertyName(b"pos")

        self._opacity_ani = QtCore.QPropertyAnimation()
        self._opacity_ani.setTargetObject(self)
        self._opacity_ani.setDuration(300)
        self._opacity_ani.setEasingCurve(QtCore.QEasingCurve.Type.OutCubic)
        self._opacity_ani.setPropertyName(b"windowOpacity")
        self._opacity_ani.setStartValue(0.0)
        self._opacity_ani.setEndValue(1.0)

        self._set_proper_position(parent)
        self._fade_int()

    def _fade_out(self):
        self._pos_ani.setDirection(QtCore.QAbstractAnimation.Direction.Backward)
        self._pos_ani.start()
        self._opacity_ani.setDirection(QtCore.QAbstractAnimation.Direction.Backward)
        self._opacity_ani.start()

    def _fade_int(self):
        self._pos_ani.start()
        self._opacity_ani.start()

    def _set_proper_position(self, parent: QtWidgets.QWidget | None):
        if parent is None:
            return
        parent_geo = parent.geometry()
        if parent.isWindowType() or parent in QtWidgets.QApplication.topLevelWidgets():
            pos = parent_geo.topLeft()
        else:
            pos = parent.mapToGlobal(parent_geo.topLeft())
        offset = 0
        for child in parent.children():
            if isinstance(child, MMessage) and child.isVisible():
                offset = max(offset, child.y())
        base = pos.y() + MMessage.default_config["top"]
        target_x = pos.x() + int(parent_geo.width() / 2) - 100
        target_y = (offset + 50) if offset else base
        self._pos_ani.setStartValue(QtCore.QPoint(target_x, target_y - 40))
        self._pos_ani.setEndValue(QtCore.QPoint(target_x, target_y))

    @classmethod
    def info(
        cls,
        text: str,
        parent: QtWidgets.QWidget,
        duration: int | None = None,
        closable: bool = False,
    ):
        """Show a normal message"""
        inst = cls(
            text,
            dayu_type=MMessage.InfoType,
            duration=duration,
            closable=closable,
            parent=parent,
        )
        inst.show()
        return inst

    @classmethod
    def success(
        cls,
        text: str,
        parent: QtWidgets.QWidget,
        duration: int | None = None,
        closable: bool = False,
    ):
        """Show a success message"""
        inst = cls(
            text,
            dayu_type=MMessage.SuccessType,
            duration=duration,
            closable=closable,
            parent=parent,
        )

        inst.show()
        return inst

    @classmethod
    def warning(
        cls,
        text: str,
        parent: QtWidgets.QWidget,
        duration: int | None = None,
        closable: bool = False,
    ):
        """Show a warning message"""
        inst = cls(
            text,
            dayu_type=MMessage.WarningType,
            duration=duration,
            closable=closable,
            parent=parent,
        )
        inst.show()
        return inst

    @classmethod
    def error(
        cls,
        text: str,
        parent: QtWidgets.QWidget,
        duration: int | None = None,
        closable: bool = False,
    ):
        """Show an error message"""
        inst = cls(
            text,
            dayu_type=MMessage.ErrorType,
            duration=duration,
            closable=closable,
            parent=parent,
        )
        inst.show()
        return inst

    @classmethod
    def loading(cls, text: str, parent: QtWidgets.QWidget):
        """Show a message with loading animation"""
        inst = cls(text, dayu_type=MMessage.LoadingType, parent=parent)
        inst.show()
        return inst

    @classmethod
    def config(cls, duration: int | None = None, top: int | None = None):
        """
        Config the global MMessage duration and top setting.
        :param duration: int (unit is second)
        :param top: int (unit is px)
        :return: None
        """
        if duration is not None:
            cls.default_config["duration"] = duration
        if top is not None:
            cls.default_config["top"] = top
