import functools
import inspect
from types import FunctionType
from typing import Optional

from ..docstring import DocstringParser
from ..reflection import CredentialDescriptor
from ..reflection.factory import BookCustomAuthenticationFactory
from ..reflection.types import CredentialScalarType


def connect(*args, **kwargs):
    fn: Optional[FunctionType] = None
    noun_phrase: Optional[str] = None
    name: Optional[str] = None

    if len(args) == 1 and not kwargs and inspect.isfunction(args[0]):
        fn = args[0]
        noun_phrase = None
        name = None
    else:
        fn = None
        noun_phrase = kwargs.get("noun_phrase", None)
        name = kwargs.get("name", None)

    def decorator(fn):
        if not inspect.isfunction(fn):
            raise TypeError("The connect decorator can only be applied to functions.")

        short_description = None
        signature = inspect.signature(fn)
        credentials = []

        if not fn.__doc__:
            raise ValueError("missing docstring")

        parsed_docstring = DocstringParser.parse(fn.__doc__)

        if parsed_docstring.short_description:
            short_description = parsed_docstring.short_description.strip()

        for param_name, parameter in signature.parameters.items():
            if param_name == "self":
                continue

            if parameter.annotation is not str:
                raise TypeError(f"parameter '{param_name}' is not a supported type")

            if parsed_docstring:
                param_docstring = parsed_docstring.param_by_name(param_name)
            else:
                param_docstring = None

            credential = CredentialDescriptor(
                id=param_name,
                label=param_docstring.label if param_docstring is not None else None,
                type=CredentialScalarType.TEXT,
                description=(param_docstring.description if param_docstring is not None else None),
            )

            credentials.append(credential)

        if not hasattr(fn, "__connect__"):
            fn.__connect__ = BookCustomAuthenticationFactory.create(id=fn.__name__, noun_phrase_str=noun_phrase, credentials=credentials, description=short_description, name=name)

        if not hasattr(fn, "__signature__"):
            fn.__signature__ = signature

        @functools.wraps(fn)
        def wrapper(*args, **kwargs):
            return fn(*args, **kwargs)

        return wrapper

    if fn is None:
        return decorator

    return decorator(fn)
