import logging
import inspect
import astroid
import operator
import re
from inspect import Parameter
from ._docstring_parser import DocstringParser, TypeHintParser
from ._base_node import NodeEntityBase, get_qualified_name
from ._argtype import ArgType


KW_ARG_NAME = "**kwargs"
VALIDATION_REQUIRED_DUNDER = ["__init__",]
KWARG_NOT_REQUIRED_METHODS = ["close",]
TYPEHINT_NOT_REQUIRED_METHODS = ["close", "__init__"]
REGEX_ITEM_PAGED = "~[\w.]*\.([\w]*)\s?[\[\(][^\n]*[\]\)]"
PAGED_TYPES = ["ItemPaged", "AsyncItemPaged",]
# Methods that are implementation of known interface should be excluded from lint check
# for e.g. get, update, keys
LINT_EXCLUSION_METHODS = [
    "get",
    "has_key",
    "items",
    "keys",
    "update",
    "values",
    "close",    
]
# Find types like ~azure.core.paging.ItemPaged and group returns ItemPaged.
# Regex is used to find shorten such instances in complex type
# for e,g, ~azure.core.ItemPaged.ItemPaged[~azure.communication.chat.ChatThreadInfo] to ItemPaged[ChatThreadInfo]
REGEX_FIND_LONG_TYPE = "((?:~?)[\w.]+\.+([\w]+))"


def is_kwarg_mandatory(func_name):
    return not func_name.startswith("_") and func_name not in KWARG_NOT_REQUIRED_METHODS


def is_typehint_mandatory(func_name):
    return not func_name.startswith("_") and func_name not in TYPEHINT_NOT_REQUIRED_METHODS


class FunctionNode(NodeEntityBase):
    """Function node class represents parsed function signature.
    Keyword args will be parsed and added to signature if docstring is available.
    :param str: namespace
    :param NodeEntityBase: parent_node
    :param function: obj
    :param bool: is_module_level
    """

    def __init__(self, namespace, parent_node, obj, is_module_level=False):
        super().__init__(namespace, parent_node, obj)
        self.annotations = []
        self.args = []
        self.return_type = None
        self.namespace_id = self.generate_id()
        # Set name space level ID as full name
        # Name space ID will be later updated for async methods
        self.full_name = self.namespace_id
        self.is_class_method = False
        self.is_module_level = is_module_level
        # Some of the methods wont be listed in API review
        # For e.g. ABC methods if class implements all ABC methods
        self.hidden = False
        self._inspect()


    def _inspect(self):
        logging.debug("Processing function {0}".format(self.name))
        code = inspect.getsource(self.obj).strip()
        # We cannot do "startswith" check here due to annotations or decorators present for functions
        self.is_async = "async def" in code
        self.def_key = "async def" if self.is_async else "def"
        # Update namespace ID to reflect async status. Otherwise ID will conflict between sync and async methods
        if self.is_async:
            self.namespace_id += ":async"

        # Find decorators and any annotations
        try:
            node = astroid.extract_node(inspect.getsource(self.obj))
            if node.decorators:
                self.annotations = [
                    "@{}".format(x.name)
                    for x in node.decorators.nodes
                    if hasattr(x, "name")
                ]
        except:
            # todo Update exception details in error
            error_message = "Error in parsing decorators for function {}".format(
                self.name
            )
            self.add_error(error_message)