from __future__ import annotations

from typing import TYPE_CHECKING, Any

from magicgui.types import Undefined

if TYPE_CHECKING:
    import inspect

    from magicgui.application import AppRef
    from magicgui.widgets import Widget, protocols


def create_widget(
    value: Any = Undefined,
    annotation: Any | None = None,
    name: str = "",
    param_kind: str | inspect._ParameterKind = "POSITIONAL_OR_KEYWORD",
    label: str | None = None,
    gui_only: bool = False,
    app: AppRef = None,
    widget_type: str | type[protocols.WidgetProtocol] | None = None,
    options: dict | None = None,
    is_result: bool = False,
    raise_on_unknown: bool = True,
) -> Widget:
    """Create and return appropriate widget subclass.

    This factory function can be used to create a widget appropriate for the
    provided ``value`` and/or ``annotation`` provided. See
    [Type Mapping Docs](../../type_map.md) for details on how the widget type is
    determined from type annotations.

    Parameters
    ----------
    value : Any, optional
        The starting value for the widget, by default ``None``
    annotation : Any, optional
        The type annotation for the parameter represented by the widget, by default
        ``None``.
    name : str, optional
        The name of the parameter represented by this widget. by default ``""``
    param_kind : str, optional
        The :attr:`inspect.Parameter.kind` represented by this widget.  Used in building
        signatures from multiple widgets, by default "``POSITIONAL_OR_KEYWORD``"
    label : str
        A string to use for an associated Label widget (if this widget is being
        shown in a [`Container`][magicgui.widgets.Container] widget, and labels are on).
        By default, ``name`` will be used. Note: ``name`` refers the name of the
        parameter, as might be used in a signature, whereas label is just the label
        for that widget in the GUI.
    gui_only : bool, optional
        Whether the widget should be considered "only for the gui", or if it should
        be included in any widget container signatures, by default False
    app : str, optional
        The backend to use, by default ``None``
    widget_type : str or Type[WidgetProtocol] or None
        A class implementing a widget protocol or a string with the name of a
        magicgui widget type (e.g. "Label", "PushButton", etc...).
        If provided, this widget type will be used instead of the type
        autodetermined from ``value`` and/or ``annotation`` above.
    options : dict, optional
        Dict of options to pass to the Widget constructor, by default dict()
    is_result : boolean, optional
        Whether the widget belongs to an input or an output. By default, an input
        is assumed.
    raise_on_unknown : bool, optional
        Raise exception if no widget is found for the given type, by default True

    Returns
    -------
    Widget
        An instantiated widget subclass

    Raises
    ------
    TypeError
        If the provided or autodetected ``widget_type`` does not implement any known
        [widget protocols](../protocols.md)

    Examples
    --------
    ```python
    from magicgui.widgets import create_widget

    # create a widget from a string value
    wdg = create_widget(value="hello world")
    assert wdg.value == "hello world"

    # create a widget from a string annotation
    wdg = create_widget(annotation=str)
    assert wdg.value == ""
    ```
    """
    from magicgui.type_map import TypeMap

    return TypeMap.global_instance().create_widget(
        value=value,
        annotation=annotation,
        name=name,
        param_kind=param_kind,
        label=label,
        gui_only=gui_only,
        app=app,
        widget_type=widget_type,
        options=options,
        is_result=is_result,
        raise_on_unknown=raise_on_unknown,
    )
