import importlib
import os
from typing import Tuple
from collections.abc import Callable

aliases = {
    'qt4': 'qt',
    'gtk2': 'gtk',
}

backends = [
    "qt",
    "qt5",
    "qt6",
    "gtk",
    "gtk2",
    "gtk3",
    "gtk4",
    "tk",
    "wx",
    "pyglet",
    "glut",
    "osx",
    "asyncio",
]

registered = {}

def register(name, inputhook):
    """Register the function *inputhook* as an event loop integration."""
    registered[name] = inputhook


class UnknownBackend(KeyError):
    def __init__(self, name):
        self.name = name

    def __str__(self):
        return ("No event loop integration for {!r}. "
                "Supported event loops are: {}").format(self.name,
                                    ', '.join(backends + sorted(registered)))


def set_qt_api(gui):
    """Sets the `QT_API` environment variable if it isn't already set."""

    qt_api = os.environ.get("QT_API", None)

    from IPython.external.qt_loaders import (
        QT_API_PYQT,
        QT_API_PYQT5,
        QT_API_PYQT6,
        QT_API_PYSIDE,
        QT_API_PYSIDE2,
        QT_API_PYSIDE6,
        QT_API_PYQTv1,
        loaded_api,
    )

    loaded = loaded_api()

    qt_env2gui = {
        QT_API_PYSIDE: "qt4",
        QT_API_PYQTv1: "qt4",
        QT_API_PYQT: "qt4",
        QT_API_PYSIDE2: "qt5",
        QT_API_PYQT5: "qt5",
        QT_API_PYSIDE6: "qt6",
        QT_API_PYQT6: "qt6",
    }
    if loaded is not None and gui != "qt":
        if qt_env2gui[loaded] != gui:
            print(
                f"Cannot switch Qt versions for this session; will use {qt_env2gui[loaded]}."
            )
            return qt_env2gui[loaded]

    if qt_api is not None and gui != "qt":
        if qt_env2gui[qt_api] != gui:
            print(
                f'Request for "{gui}" will be ignored because `QT_API` '
                f'environment variable is set to "{qt_api}"'
            )
            return qt_env2gui[qt_api]
    else:
        if gui == "qt5":
            try:
                import PyQt5  # noqa

                os.environ["QT_API"] = "pyqt5"
            except ImportError:
                try:
                    import PySide2  # noqa

                    os.environ["QT_API"] = "pyside2"
                except ImportError:
                    os.environ["QT_API"] = "pyqt5"
        elif gui == "qt6":
            try:
                import PyQt6  # noqa

                os.environ["QT_API"] = "pyqt6"
            except ImportError:
                try:
                    import PySide6  # noqa

                    os.environ["QT_API"] = "pyside6"
                except ImportError:
                    os.environ["QT_API"] = "pyqt6"
        elif gui == "qt":
            # Don't set QT_API; let IPython logic choose the version.
            if "QT_API" in os.environ.keys():
                del os.environ["QT_API"]
        else:
            print(f'Unrecognized Qt version: {gui}. Should be "qt5", "qt6", or "qt".')
            return

        # Import it now so we can figure out which version it is.
        from IPython.external.qt_for_kernel import QT_API

        return qt_env2gui[QT_API]


def get_inputhook_name_and_func(gui: str) -> Tuple[str, Callable]:
    if gui in registered:
        return gui, registered[gui]

    if gui not in backends:
        raise UnknownBackend(gui)

    if gui in aliases:
        return get_inputhook_name_and_func(aliases[gui])

    gui_mod = gui
    if gui.startswith("qt"):
        gui = set_qt_api(gui)
        gui_mod = "qt"

    mod = importlib.import_module("IPython.terminal.pt_inputhooks." + gui_mod)
    return gui, mod.inputhook
