from __future__ import annotations

import logging
import os
import platform
import sys
from types import ModuleType
from typing import Any, Callable, cast, get_args

from typing_extensions import Literal, TypeAlias

from webview import WebViewException

GUIType: TypeAlias = Literal['qt', 'gtk', 'cef', 'mshtml', 'edgechromium', 'android', 'cocoa']
GUI_TYPES = list(get_args(GUIType))

logger = logging.getLogger('pywebview')
guilib: ModuleType | None = None
forced_gui_: GUIType | None = None


def initialize(forced_gui: GUIType | None = None):
    def import_android():
        global guilib

        try:
            import webview.platforms.android as guilib

            logger.debug('Using Kivy')
            return True
        except (ImportError, ValueError):
            logger.exception('Kivy cannot be loaded')
            return False

    def import_gtk():
        global guilib

        try:
            import webview.platforms.gtk as guilib

            logger.debug('Using GTK')
            return True
        except (ImportError, ValueError):
            logger.exception('GTK cannot be loaded')
            return False

    def import_qt():
        global guilib

        try:
            import webview.platforms.qt as guilib

            return True
        except ImportError:
            logger.exception('QT cannot be loaded')
            return False

    def import_cocoa():
        global guilib

        try:
            import webview.platforms.cocoa as guilib

            return True
        except ImportError:
            logger.exception('PyObjC cannot be loaded')

            return False

    def import_winforms():
        global guilib

        try:
            import webview.platforms.winforms as guilib

            return True
        except ImportError:
            logger.exception('pythonnet cannot be loaded')
            return False

    def try_import(guis: list[Callable[[], Any]]) -> bool:
        while guis:
            import_func = guis.pop(0)

            if import_func():
                return True

        return False

    global forced_gui_

    if not forced_gui:
        forced_gui = 'qt' if 'KDE_FULL_SESSION' in os.environ else None
        forced_gui = cast(
            GUIType,
            os.environ['PYWEBVIEW_GUI'].lower()
            if 'PYWEBVIEW_GUI' in os.environ and os.environ['PYWEBVIEW_GUI'].lower() in GUI_TYPES
            else forced_gui,
        )

    forced_gui_ = forced_gui

    if platform.system() == 'Darwin':
        if forced_gui == 'qt':
            guis = [import_qt, import_cocoa]
        else:
            guis = [import_cocoa, import_qt]

        if not try_import(guis):
            raise WebViewException(
                'You must have either PyObjC (for Cocoa support) or Qt with Python bindings installed in order to use pywebview.'
            )

    elif hasattr(sys, 'getandroidapilevel'):
        try_import([import_android])

    elif platform.system() == 'Linux' or platform.system() == 'OpenBSD':
        if forced_gui == 'qt':
            guis = [import_qt, import_gtk]
        else:
            guis = [import_gtk, import_qt]

        if not try_import(guis):
            raise WebViewException(
                'You must have either QT or GTK with Python extensions installed in order to use pywebview.'
            )

    elif platform.system() == 'Windows':
        if forced_gui == 'qt':
            guis = [import_qt, import_winforms]
        else:
            guis = [import_winforms]

        if not try_import(guis):
            raise WebViewException('You must have pythonnet installed in order to use pywebview.')
    else:
        raise WebViewException(
            'Unsupported platform. Only Windows, Linux, OS X, OpenBSD are supported.'
        )

    guilib.setup_app()
    return guilib
