"""TODO Some parts of this module are still not well documented."""
import inspect
import re

from jedi import debug
from jedi._compatibility import builtins
from jedi.cache import underscore_memoization
from jedi.common import source_to_unicode
from jedi.evaluate import compiled, helpers, iterable
from jedi.evaluate.compiled.fake import get_module
from jedi.parser import load_grammar
from jedi.parser import tree as pt
from jedi.parser.fast import FastParser


def add_namespaces_to_parser(evaluator, namespaces, parser_module) -> None:
    for namespace in namespaces:
        for key, value in namespace.items():
            # Name lookups in an ast tree work by checking names_dict.
            # Therefore we just add fake names to that and we're done.
            arr = parser_module.names_dict.setdefault(key, [])
            arr.append(LazyName(evaluator, parser_module, key, value))


class LazyName(helpers.FakeName):
    def __init__(self, evaluator, module, name, value):
        super().__init__(name)
        self._module = module
        self._evaluator = evaluator
        self._value = value
        self._name = name

    def is_definition(self) -> bool:
        return True

    @property
    @underscore_memoization
    def parent(self):
        """Creating fake statements for the interpreter."""
        obj = self._value
        parser_path = []
        if inspect.ismodule(obj):
            module = obj
        else:
            names = []
            try:
                o = obj.__objclass__
                names.append(obj.__name__)
                obj = o
            except AttributeError:
                pass

            try:
                module_name = obj.__module__
                names.insert(0, obj.__name__)
            except AttributeError:
                # Unfortunately in some cases like `int` there's no __module__
                module = builtins
            else:
                # TODO this import is wrong. Yields x for x.y.z instead of z
                module = __import__(module_name)
            parser_path = names
        get_module(self._value)

        found = []
        try:
            path = module.__file__
        except AttributeError:
            pass
        else:
            path = re.sub('c$', '', path)
            if path.endswith('.py'):
                # cut the `c` from `.pyc`
                with open(path) as f:
                    source = source_to_unicode(f.read())
                mod = FastParser(load_grammar(), source, path[:-1]).module
                if parser_path:
                    assert len(parser_path) == 1
                    found = self._evaluator.find_types(mod, parser_path[0], search_global=True)
                else:
                    found = [self._evaluator.wrap(mod)]

                if not found:
                    debug.warning('Possibly an interpreter lookup for Python code failed %s',
                                  parser_path)

        if not found:
            evaluated = compiled.CompiledObject(obj)
            if evaluated == builtins:
                # The builtins module is special and always cached.
                evaluated = compiled.builtin
            found = [evaluated]

        content = iterable.AlreadyEvaluated(found)
        stmt = pt.ExprStmt([self, pt.Operator(pt.zero_position_modifier,
                                              '=', (0, 0), ''), content])
        stmt.parent = self._module
        return stmt

    @parent.setter
    def parent(self, value):
        """Needed because the super class tries to set parent."""
