"""
@file
@brief Few helpers for :epkg:`Sphinx`.
"""
import io
import logging
import os
import pickle
import urllib.parse as urllib_parse
import urllib.request as urllib_request
from urllib.parse import urljoin
from sphinx.util.inventory import InventoryFile


def info_blocref(app, doctree, fromdocname, class_name,
                 entry_name, class_node, class_node_list):
    """
    Log information with :epkg:`Sphinx`.

    @param      app                 application (Sphinx)
    @param      doctree             document tree
    @param      fromdocname         document currently being compiled
    @param      class_name          name of the node
    @param      entry_name          entry name in ``TITLES``
    @param      class_node          class node (@see cl blocref_node)
    @param      class_node_list     class node list (@see cl blocreflist)
    """
    incconf = f'{class_name}_include_{class_name}s'
    rows2 = []
    for node in doctree.traverse(class_node_list):
        breftag = node.get("breftag", None)
        rows2.append(f"tag={breftag} do={app.config[incconf]}")
    if len(rows2) == 0:
        return False

    attr_name = f'{class_name}_all_{class_name}s'
    env = app.builder.env
    if hasattr(env, attr_name):
        bloc_list_env = getattr(env, attr_name)
    else:
        bloc_list_env = []

    rows = ["  [info_blocref]",
            f"len(bloc_list_env)={len(bloc_list_env)}", ]
    rows.extend(rows2)
    rows.extend([f"fromdocname='{fromdocname}'",
                 f"entry_name='{entry_name}'",
                 f"class_name='{class_name}'",
                 f"class_node='{class_node}'",
                 f"class_node_list='{class_node_list}'",
                 f"doctree='{type(doctree)}'",
                 f"#doctree={len(doctree)}"])
    message = " ".join(rows)
    logger = logging.getLogger("info_blocref")
    logger.info(message)
    return True


def sphinx_lang(env, default_value='en'):
    """
    Returns the language defined in the configuration file.

    @param      env             environment
    @param      default_value   default value
    @return                     language
    """
    if hasattr(env, "settings"):
        settings = env.settings
        if hasattr(settings, "language_code"):
            lang = env.settings.language_code  # pragma: no cover
        else:
            lang = "en"
    else:
        settings = None  # pragma: no cover
        lang = "en"  # pragma: no cover
    return lang


class TinyNode:
    """
    Returned by @see fn traverse.
    """

    def __init__(self, parent):
        """
        Create a note

        @param      parent      parent node
        """
        self.parent = parent


class NodeEnter(TinyNode):
    """
    Returned by function @see fn traverse.
    """
    pass


class NodeLeave(TinyNode):
    """
    Returned by function @see fn traverse.
    """
    pass


class WrappedNode:
    """
    Wraps a docutils node.
    """

    def __init__(self, node):
        self.node = node


def traverse(node, depth=0):
    """
    Enumerates through all children but insert a node whenever
    digging or leaving the childrens nodes.

    @param      node        node (from doctree)
    @param      depth       current depth
    @return                 enumerate (depth, node)

    @see cl NodeEnter and @see cl NodeLeave are returned whenever entering or leaving nodes.
    """
    if isinstance(node, WrappedNode):
        node = node.node
    ne = NodeEnter(node)
    nl = NodeLeave(node)
    yield (depth, ne)
    yield (depth, node)
    for n in node.children:
        for r in traverse(n, depth + 1):
            yield r
    yield (depth, nl)


def _get_data(url):
    """
    Loads file ``objects.inv`` generated by
    extension :epkg:`sphinx.ext.intersphinx`.

    @param      url     url of documentation, example
                        ``https://pandas.pydata.org/docs/``
    @return             instance of `InventoryFile`
    """
    url_inv = urljoin(url, "objects.inv")
    if urllib_parse.urlparse(url_inv).scheme in ('http', 'https'):
        user_agent = 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.64 Safari/537.11'  # noqa: E501
        headers = {'User-Agent': user_agent}
        req = urllib_request.Request(url_inv, None, headers)
        resp = urllib_request.urlopen(req)
        data = resp.read()
    else:
        with open(url, 'rb') as fid:
            data = fid.read()

    inv = InventoryFile.load(io.BytesIO(data), url, urljoin)
    return inv


def get_index(index_url, cache_dir):
    """
    Retrieves documentation data for a specific module.

    @param      url         url of documentation, example
                            ``https://pandas.pydata.org/docs/``
    @param      cache_dir   restore a cached inventory stored with pickle
    @return                 instance of `InventoryFile`
    """
    if cache_dir is not None:
        base_file = index_url.replace("/", "_").split(':')[-1] + ".pkl"
        full_file = os.path.join(cache_dir, base_file)
        if os.path.exists(full_file):
            with open(full_file, "rb") as f:
                return pickle.load(f)
    index = _get_data(index_url)
    if cache_dir is not None:
        with open(full_file, "wb") as f:
            pickle.dump(index, f)
    return index
