from __future__ import annotations

from typing import Literal

from prettyqt import constants, core, widgets
from prettyqt.qt import QtCore, QtGui, QtWidgets
from prettyqt.utils import InvalidParamError, bidict, datatypes


REMOVE_BEHAVIOUR = bidict(
    left_tab=QtWidgets.QTabBar.SelectionBehavior.SelectLeftTab,
    right_tab=QtWidgets.QTabBar.SelectionBehavior.SelectRightTab,
    previous_tab=QtWidgets.QTabBar.SelectionBehavior.SelectPreviousTab,
)

RemoveBehaviourStr = Literal["left_tab", "right_tab", "previous_tab"]

SHAPE = bidict(
    rounded_north=QtWidgets.QTabBar.Shape.RoundedNorth,
    rounded_south=QtWidgets.QTabBar.Shape.RoundedSouth,
    rounded_west=QtWidgets.QTabBar.Shape.RoundedWest,
    rounded_east=QtWidgets.QTabBar.Shape.RoundedEast,
    triangular_north=QtWidgets.QTabBar.Shape.TriangularNorth,
    triangular_south=QtWidgets.QTabBar.Shape.TriangularSouth,
    triangular_west=QtWidgets.QTabBar.Shape.TriangularWest,
    triangular_east=QtWidgets.QTabBar.Shape.TriangularEast,
)

ShapeStr = Literal[
    "rounded_north",
    "rounded_south",
    "rounded_west",
    "rounded_east",
    "triangular_north",
    "triangular_south",
    "triangular_west",
    "triangular_east",
]

POSITIONS = bidict(
    left=QtWidgets.QTabBar.ButtonPosition.LeftSide,
    right=QtWidgets.QTabBar.ButtonPosition.RightSide,
)

PositionStr = Literal["left", "right"]


class TabBarMixin(widgets.WidgetMixin):
    on_detach = QtCore.Signal(int, QtCore.QPoint)

    def __init__(self, *args, **kwargs) -> None:
        super().__init__(*args, **kwargs)

        self.setAcceptDrops(True)
        self.set_elide_mode("right")
        self.set_selection_behavior_on_remove("left_tab")

    def __getitem__(self, index: tuple[int, str]):
        return self.tabButton(index[0], POSITIONS[index[1]])

    def __setitem__(
        self, index: tuple[int, PositionStr], value: QtWidgets.QWidget | None
    ):
        self.set_tab(index[0], index[1], value)

    #  Send the on_detach when a tab is double clicked
    def mouseDoubleClickEvent(self, event):
        event.accept()
        tab = self.tabAt(event.position().toPoint())
        pos = QtGui.QCursor.pos()
        self.on_detach.emit(tab, pos)

    def set_icon_size(self, size: int | datatypes.SizeType):
        """Set size of the icons."""
        if isinstance(size, int):
            size = core.Size(size, size)
        elif isinstance(size, tuple):
            size = core.Size(*size)
        self.setIconSize(size)

    def get_icon_size(self) -> core.Size:
        return core.Size(self.iconSize())

    def set_tab(
        self, index: int, position: PositionStr, widget: QtWidgets.QWidget | None
    ) -> None:
        self.setTabButton(index, POSITIONS[position], widget)  # type: ignore

    def set_selection_behavior_on_remove(self, mode: RemoveBehaviourStr) -> None:
        """Set the remove hehaviour.

        What tab should be set as current when removeTab is called
        if the removed tab is also the current tab.

        Args:
            mode: new remove behaviour
        """
        if mode not in REMOVE_BEHAVIOUR:
            raise InvalidParamError(mode, REMOVE_BEHAVIOUR)
        self.setSelectionBehaviorOnRemove(REMOVE_BEHAVIOUR[mode])

    def get_remove_behaviour(self) -> RemoveBehaviourStr:
        """Return remove behaviour.

        Returns:
            remove behaviour
        """
        return REMOVE_BEHAVIOUR.inverse[self.selectionBehaviorOnRemove()]

    def set_elide_mode(self, mode: constants.ElideModeStr) -> None:
        """Set elide mode.

        Args:
            mode: elide mode to use

        Raises:
            InvalidParamError: invalid elide mode
        """
        if mode not in constants.ELIDE_MODE:
            raise InvalidParamError(mode, constants.ELIDE_MODE)
        self.setElideMode(constants.ELIDE_MODE[mode])

    def get_elide_mode(self) -> constants.ElideModeStr:
        """Return elide mode.

        Returns:
            elide mode
        """
        return constants.ELIDE_MODE.inverse[self.elideMode()]


class TabBar(TabBarMixin, QtWidgets.QTabBar):
    pass
