"""
Template code for building docs includeing conf.py
"""
import ubelt as ub


class DocsBuilder:
    """
    Helper to build sphinx docs related files
    """
    def __init__(docs_builder, config):
        """
        Args:
            config (XCookieConfig.shrinkuser()):
        """
        docs_builder.config = config

    @property
    def docs_dpath(docs_builder):
        repodir = ub.Path(docs_builder.config['repodir']).absolute()
        return repodir / 'docs'

    @property
    def docs_auto_outdir(docs_builder):
        # FIXME:
        # return docs_builder.docs_dpath / 'source'
        return docs_builder.docs_dpath / 'source/auto'

    def sphinx_apidoc_invocation(docs_builder, shrinkuser=False):
        """
        Instructions to invoke sphinx-apidoc.
        Xcookie calls this, but also writes it to the conf.py docstring for
        transparency.
        """
        repodir = ub.Path(docs_builder.config['repodir']).absolute()
        docs_outdir = docs_builder.docs_auto_outdir
        if shrinkuser:
            repodir = repodir.shrinkuser()
            docs_outdir = docs_outdir.shrinkuser()
        rel_mod_dpath = ub.Path(docs_builder.config['rel_mod_parent_dpath']) / docs_builder.config['mod_name']
        mod_abspath = repodir / rel_mod_dpath

        exclude_pattern = None
        if docs_builder.config['mod_name'] in {'mkinit', 'xdoctest'}:
            # Hack: todo make this configurable
            exclude_pattern = '_tokenize.py'

        if exclude_pattern is None:
            invocation = f'sphinx-apidoc --private --separate --force --output-dir {docs_outdir} {mod_abspath}'
        else:
            invocation = f'sphinx-apidoc --private --separate --force --output-dir {docs_outdir} {mod_abspath} \'{exclude_pattern}\''
        return invocation


def build_docs_index(self):
    import ubelt as ub
    parts = []
    tags = set(self.config['tags'])
    mod_name = self.config['mod_name']

    if {'gitlab', 'kitware'}.issubset(tags):
        parts.append(ub.codeblock(
            f'''
            :gitlab_url: https://gitlab.kitware.com/computer-vision/{mod_name}
            '''))

    if {'github'}.issubset(tags):
        parts.append(ub.codeblock(
            f'''
            :github_url: {self.config['url']}
            '''))

    logo_part = ub.codeblock(
        '''
        .. The large version wont work because github strips rst image rescaling. https://i.imgur.com/AcWVroL.png
            # TODO: Add a logo
            .. image:: https://i.imgur.com/PoYIsWE.png
               :height: 100px
               :align: left
        ''')

    this_fpath = str(__file__)

    parts.append(logo_part)

    parts.append(f'.. Autogenerated by templates in {this_fpath}')

    title = f"Welcome to {mod_name}'s documentation!"
    underline = '=' * len(title)
    title_part = title + '\n' + underline
    parts.append(title_part)

    init_part = ub.codeblock(
        f'''
        .. The __init__ files contains the top-level documentation overview
        .. automodule:: {mod_name}.__init__
           :show-inheritance:
        ''')
    parts.append(init_part)

    if 0:
        usefulness_part = ub.codeblock(
            '''
            .. # Computed function usefulness
            .. include:: function_usefulness.rst
            ''')
        parts.append(usefulness_part)

    sidebar_part = ub.codeblock(
        f'''
        .. toctree::
           :maxdepth: 5

           auto/{mod_name}
           auto/modules


        Indices and tables
        ==================

        * :ref:`genindex`
        * :ref:`modindex`
        * :ref:`search`
        ''')
    parts.append(sidebar_part)

    text = '\n\n'.join(parts)
    return text


def build_docs_conf(self):
    import datetime as datetime_mod

    docs_builder = DocsBuilder(self.config)

    author = self.config['author']
    if isinstance(author, list):
        author_str = ' '.join(author)
    else:
        author_str = author

    fmtkw = {
        'repo_name': self.config['repo_name'],
        'repo_dashname': self.config['repo_name'].replace('_', '-'),
        'mod_name': self.config['mod_name'],
        'rel_mod_dpath': str(self.rel_mod_dpath),
        # 'repo_url': self.remote_info['url'],
        'repo_url': self.config.url,
        'author': author_str,
        'year': datetime_mod.datetime.now().year,
        'repodir_wrt_home': self.repodir.shrinkuser()
    }

    fmtkw['invoke_apidoc'] = docs_builder.sphinx_apidoc_invocation(shrinkuser=True)
    fmtkw['docs_dpath_wrt_home'] = docs_builder.docs_dpath.shrinkuser()
    fmtkw['rel_add_dir'] = docs_builder.docs_auto_outdir.relative_to(docs_builder.docs_dpath)

    text = ub.codeblock(
        r'''
        """
        Notes:
            Based on template code in:
                ~/code/xcookie/xcookie/builders/docs.py
                ~/code/xcookie/xcookie/rc/conf_ext.py

            http://docs.readthedocs.io/en/latest/getting_started.html

            pip install sphinx sphinx-autobuild sphinx_rtd_theme sphinxcontrib-napoleon

            cd {repodir_wrt_home}
            mkdir -p docs
            cd docs

            sphinx-quickstart

            # need to edit the conf.py

            cd {docs_dpath_wrt_home}
            {invoke_apidoc}

            # Note: the module should importable before running this
            # (e.g. install it in developer mode or munge the PYTHONPATH)
            make html

            git add {rel_add_dir}/*.rst

            Also:
                To turn on PR checks

                https://docs.readthedocs.io/en/stable/guides/autobuild-docs-for-pull-requests.html

                https://readthedocs.org/dashboard/{repo_dashname}/advanced/

                ensure your github account is connected to readthedocs
                https://readthedocs.org/accounts/social/connections/

                ### For gitlab

                To enable the read-the-docs go to https://readthedocs.org/dashboard/ and login

                The user will need to enable the repo on their readthedocs account:
                https://readthedocs.org/dashboard/import/manual/?

                Enter the following information:
                    Set the Repository NAME: {repo_name}
                    Set the Repository URL: {repo_url}

                Make sure you have a .readthedocs.yml file

                For gitlab you also need to setup an integrations. Navigate to:

                    https://readthedocs.org/dashboard/{repo_dashname}/integrations/create/

                Then add gitlab incoming webhook and copy the URL (make sure
                you copy the real url and not the text so https is included),
                specifically:

                    In the "Integration type:" dropdown menu, select
                    "Gitlab incoming webhook"

                    Click "Add integration"

                    Copy the text in the "Webhook URL" box to be used later.

                    Copy the text in the "Secret" box to be used later.

                Then go to

                    {repo_url}/hooks

                    Click "Add new webhook".

                    Copy the text previously saved from the "Webhook URL" box
                    in the readthedocs form into the "URL" box in the gitlab
                    form.

                    Copy the text previously saved from the "Secret" box
                    in the readthedocs form into the "Secret token" box in the
                    gitlab form.

                    For trigger permissions select the following checkboxes:
                        push events,
                        tag push events,
                        merge request events

                    Click the "Add webhook" button.

                See Docs for more details https://docs.readthedocs.io/en/stable/integrations.html

                Will also need to activate the main branch:
                    https://readthedocs.org/projects/{repo_dashname}/versions/
        """
        #
        # Configuration file for the Sphinx documentation builder.
        #
        # This file does only contain a selection of the most common options. For a
        # full list see the documentation:
        # http://www.sphinx-doc.org/en/stable/config

        # -- Path setup --------------------------------------------------------------

        # If extensions (or modules to document with autodoc) are in another directory,
        # add these directories to sys.path here. If the directory is relative to the
        # documentation root, use os.path.abspath to make it absolute, like shown here.
        #
        # import os
        # import sys
        # sys.path.insert(0, os.path.abspath('.'))


        # -- Project information -----------------------------------------------------
        import sphinx_rtd_theme
        from os.path import exists
        from os.path import dirname
        from os.path import join


        def parse_version(fpath):
            """
            Statically parse the version number from a python file
            """
            import ast
            if not exists(fpath):
                raise ValueError('fpath={{!r}} does not exist'.format(fpath))
            with open(fpath, 'r') as file_:
                sourcecode = file_.read()
            pt = ast.parse(sourcecode)
            class VersionVisitor(ast.NodeVisitor):
                def visit_Assign(self, node):
                    for target in node.targets:
                        if getattr(target, 'id', None) == '__version__':
                            self.version = node.value.s
            visitor = VersionVisitor()
            visitor.visit(pt)
            return visitor.version

        project = '{repo_name}'
        copyright = '{year}, {author}'
        author = '{author}'
        modname = '{mod_name}'

        repo_dpath = dirname(dirname(dirname(__file__)))
        mod_dpath = join(repo_dpath, '{rel_mod_dpath}')
        src_dpath = dirname(mod_dpath)
        modpath = join(mod_dpath, '__init__.py')
        release = parse_version(modpath)
        version = '.'.join(release.split('.')[0:2])

        # Hack to ensure the module is importable
        # sys.path.insert(0, os.path.abspath(src_dpath))

        # -- General configuration ---------------------------------------------------

        # If your documentation needs a minimal Sphinx version, state it here.
        #
        # needs_sphinx = '1.0'

        # Add any Sphinx extension module names here, as strings. They can be
        # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
        # ones.
        extensions = [
            # 'autoapi.extension',
            'sphinx.ext.autodoc',
            'sphinx.ext.autosummary',
            'sphinx.ext.intersphinx',
            'sphinx.ext.napoleon',
            'sphinx.ext.todo',
            'sphinx.ext.viewcode',
            'myst_parser',  # For markdown docs
            'sphinx.ext.imgconverter',  # For building latexpdf
            'sphinx.ext.githubpages',
            # 'sphinxcontrib.redirects',
            'sphinx_reredirects',
        ]

        todo_include_todos = True
        napoleon_google_docstring = True
        napoleon_use_param = False
        napoleon_use_ivar = True

        #autoapi_type = 'python'
        #autoapi_dirs = [mod_dpath]

        autodoc_inherit_docstrings = False

        # Hack for geowatch, todo configure
        autosummary_mock_imports = [
            'geowatch.utils.lightning_ext._jsonargparse_ext_ge_4_24_and_lt_4_xx',
            'geowatch.utils.lightning_ext._jsonargparse_ext_ge_4_22_and_lt_4_24',
            'geowatch.utils.lightning_ext._jsonargparse_ext_ge_4_21_and_lt_4_22',
            'geowatch.tasks.fusion.datamodules.temporal_sampling.affinity_sampling',
            'geowatch.tasks.depth_pcd.model',
            'geowatch.tasks.cold.export_change_map',
        ]

        autodoc_member_order = 'bysource'
        autoclass_content = 'both'
        # autodoc_mock_imports = ['torch', 'torchvision', 'visdom']

        # autoapi_modules = {{
        #     modname: {{
        #         'override': False,
        #         'output': 'auto'
        #     }}
        # }}
        # autoapi_dirs = [f'../../src/{{modname}}']
        # autoapi_keep_files = True

        # References:
        # https://stackoverflow.com/questions/21538983/specifying-targets-for-intersphinx-links-to-numpy-scipy-and-matplotlib

        intersphinx_mapping = {{
            # 'pytorch': ('http://pytorch.org/docs/master/', None),
            'python': ('https://docs.python.org/3', None),
            'click': ('https://click.palletsprojects.com/', None),
            # 'xxhash': ('https://pypi.org/project/xxhash/', None),
            # 'pygments': ('https://pygments.org/docs/', None),
            # 'tqdm': ('https://tqdm.github.io/', None),
            # Requries that the repo have objects.inv
            'kwarray': ('https://kwarray.readthedocs.io/en/latest/', None),
            'kwimage': ('https://kwimage.readthedocs.io/en/latest/', None),
            # 'kwplot': ('https://kwplot.readthedocs.io/en/latest/', None),
            'ndsampler': ('https://ndsampler.readthedocs.io/en/latest/', None),
            'ubelt': ('https://ubelt.readthedocs.io/en/latest/', None),
            'xdoctest': ('https://xdoctest.readthedocs.io/en/latest/', None),
            'networkx': ('https://networkx.org/documentation/stable/', None),
            'scriptconfig': ('https://scriptconfig.readthedocs.io/en/latest/', None),
            'rich': ('https://rich.readthedocs.io/en/latest/', None),

            'numpy': ('https://numpy.org/doc/stable/', None),
            'sympy': ('https://docs.sympy.org/latest/', None),
            'scikit-learn': ('https://scikit-learn.org/stable/', None),
            'pandas': ('https://pandas.pydata.org/docs/', None),
            'matplotlib': ('https://matplotlib.org/stable/', None),

            'pytest': ('https://docs.pytest.org/en/latest/', None),
            'platformdirs': ('https://platformdirs.readthedocs.io/en/latest/', None),

            'timerit': ('https://timerit.readthedocs.io/en/latest/', None),
            'progiter': ('https://progiter.readthedocs.io/en/latest/', None),
            'dateutil': ('https://dateutil.readthedocs.io/en/latest/', None),
            # 'pytest._pytest.doctest': ('https://docs.pytest.org/en/latest/_modules/_pytest/doctest.html', None),
            # 'colorama': ('https://pypi.org/project/colorama/', None),
            # 'cv2' : ('http://docs.opencv.org/2.4/', None),
            # 'h5py' : ('http://docs.h5py.org/en/latest/', None)
        }}
        __dev_note__ = """
        python -m sphinx.ext.intersphinx https://docs.python.org/3/objects.inv
        python -m sphinx.ext.intersphinx https://kwcoco.readthedocs.io/en/latest/objects.inv
        python -m sphinx.ext.intersphinx https://networkx.org/documentation/stable/objects.inv
        python -m sphinx.ext.intersphinx https://kwarray.readthedocs.io/en/latest/objects.inv
        python -m sphinx.ext.intersphinx https://kwimage.readthedocs.io/en/latest/objects.inv
        python -m sphinx.ext.intersphinx https://ubelt.readthedocs.io/en/latest/objects.inv
        python -m sphinx.ext.intersphinx https://networkx.org/documentation/stable/objects.inv

        sphobjinv suggest -t 90 -u https://readthedocs.org/projects/pytest/reference/objects.inv
        "signal.convolve2d"

        python -m sphinx.ext.intersphinx https://pygments-doc.readthedocs.io/en/latest/objects.inv
        """


        # Add any paths that contain templates here, relative to this directory.
        templates_path = ['_templates']

        # The suffix(es) of source filenames.
        # You can specify multiple suffix as a list of string:
        #
        source_suffix = ['.rst', '.md']

        # The master toctree document.
        master_doc = 'index'

        # The language for content autogenerated by Sphinx. Refer to documentation
        # for a list of supported languages.
        #
        # This is also used if you do content translation via gettext catalogs.
        # Usually you set "language" from the command line for these cases.
        language = 'en'

        # List of patterns, relative to source directory, that match files and
        # directories to ignore when looking for source files.
        # This pattern also affects html_static_path and html_extra_path .
        exclude_patterns = []

        # The name of the Pygments (syntax highlighting) style to use.
        pygments_style = 'sphinx'


        # -- Options for HTML output -------------------------------------------------

        # The theme to use for HTML and HTML Help pages.  See the documentation for
        # a list of builtin themes.
        #
        html_theme = 'sphinx_rtd_theme'
        html_theme_path = [sphinx_rtd_theme.get_html_theme_path()]

        # Theme options are theme-specific and customize the look and feel of a theme
        # further.  For a list of options available for each theme, see the
        # documentation.
        #
        html_theme_options = {{
            'collapse_navigation': False,
            'display_version': True,
            'navigation_depth': -1,
            # 'logo_only': True,
        }}
        # html_logo = '.static/{repo_name}.svg'
        # html_favicon = '.static/{repo_name}.ico'

        # Add any paths that contain custom static files (such as style sheets) here,
        # relative to this directory. They are copied after the builtin static files,
        # so a file named "default.css" will overwrite the builtin "default.css".
        html_static_path = ['_static']

        # Custom sidebar templates, must be a dictionary that maps document names
        # to template names.
        #
        # The default sidebars (for documents that don't match any pattern) are
        # defined by theme itself.  Builtin themes are using these templates by
        # default: ``['localtoc.html', 'relations.html', 'sourcelink.html',
        # 'searchbox.html']``.
        #
        # html_sidebars = {{}}


        # -- Options for HTMLHelp output ---------------------------------------------

        # Output file base name for HTML help builder.
        htmlhelp_basename = project + 'doc'


        # -- Options for LaTeX output ------------------------------------------------

        # References:
        # https://tex.stackexchange.com/questions/546246/centos-8-the-font-freeserif-cannot-be-found

        """
        # https://www.sphinx-doc.org/en/master/usage/builders/index.html#sphinx.builders.latex.LaTeXBuilder
        # https://tex.stackexchange.com/a/570691/83399
        sudo apt install fonts-freefont-otf texlive-luatex texlive-latex-extra texlive-fonts-recommended texlive-latex-recommended tex-gyre latexmk
        make latexpdf LATEXMKOPTS="-shell-escape --synctex=-1 -src-specials -interaction=nonstopmode"
        make latexpdf LATEXMKOPTS="-lualatex -interaction=nonstopmode"
        make LATEXMKOPTS="-lualatex -interaction=nonstopmode"

        """
        # latex_engine = 'lualatex'
        # latex_engine = 'xelatex'

        latex_elements = {{
            # The paper size ('letterpaper' or 'a4paper').
            #
            # 'papersize': 'letterpaper',

            # The font size ('10pt', '11pt' or '12pt').
            #
            # 'pointsize': '10pt',

            # Additional stuff for the LaTeX preamble.
            #
            # 'preamble': '',

            # Latex figure (float) alignment
            #
            # 'figure_align': 'htbp',
        }}

        # Grouping the document tree into LaTeX files. List of tuples
        # (source start file, target name, title,
        #  author, documentclass [howto, manual, or own class]).
        latex_documents = [
            (master_doc, '{repo_name}.tex', '{repo_name} Documentation',
             '{author}', 'manual'),
        ]


        # -- Options for manual page output ------------------------------------------

        # One entry per manual page. List of tuples
        # (source start file, name, description, authors, manual section).
        man_pages = [
            (master_doc, '{repo_name}', '{repo_name} Documentation',
             [author], 1)
        ]


        # -- Options for Texinfo output ----------------------------------------------

        # Grouping the document tree into Texinfo files. List of tuples
        # (source start file, target name, title, author,
        #  dir menu entry, description, category)
        texinfo_documents = [
            (master_doc, '{repo_name}', '{repo_name} Documentation',
             author, '{repo_name}', 'One line description of project.',
             'Miscellaneous'),
        ]


        # -- Extension configuration -------------------------------------------------
        ''').format(**fmtkw)

    from xcookie import rc

    util_text = rc.resource_fpath('conf_ext.py').read_text()

    if self.config.render_doc_images:
        util_text = util_text.replace('render_doc_images = 0', 'render_doc_images = 1')

    if self.config['repo_name'] == 'kwcoco':
        util_text = util_text.replace('HACK_FOR_KWCOCO = 0', 'HACK_FOR_KWCOCO = 1')

    text = text + '\n' + util_text
    return text


def build_docs_requirements(self):
    import ubelt as ub

    docs_requierments = ub.codeblock(
        '''
        sphinx>=5.0.1
        sphinx-autobuild>=2021.3.14
        sphinx_rtd_theme>=1.0.0
        sphinxcontrib-napoleon>=0.7
        sphinx-autoapi>=1.8.4
        Pygments>=2.9.0
        myst_parser>=0.18.0
        sphinx-reredirects>=0.0.1
        xdoctest>=1.1.2
        ''')

    if self.config.render_doc_images:
        if self.config['mod_name'] != 'kwplot':
            if 'cv2' in self.tags:
                docs_requierments += '\nkwplot>=0.4.15'
            else:
                docs_requierments += '\nkwplot[headless]>=0.4.15'

    return docs_requierments
