"""The metadata collector components for sphinx.environment."""

from __future__ import annotations

from typing import TYPE_CHECKING, cast

from docutils import nodes

from sphinx.environment.collectors import EnvironmentCollector

if TYPE_CHECKING:
    from collections.abc import Set

    from sphinx.application import Sphinx
    from sphinx.environment import BuildEnvironment
    from sphinx.util.typing import ExtensionMetadata


class MetadataCollector(EnvironmentCollector):
    """metadata collector for sphinx.environment."""

    def clear_doc(self, app: Sphinx, env: BuildEnvironment, docname: str) -> None:
        env.metadata.pop(docname, None)

    def merge_other(
        self,
        app: Sphinx,
        env: BuildEnvironment,
        docnames: Set[str],
        other: BuildEnvironment,
    ) -> None:
        for docname in docnames:
            env.metadata[docname] = other.metadata[docname]

    def process_doc(self, app: Sphinx, doctree: nodes.document) -> None:
        """Process the docinfo part of the doctree as metadata.

        Keep processing minimal -- just return what docutils says.
        """
        index = doctree.first_child_not_matching_class(nodes.PreBibliographic)  # type: ignore[arg-type]
        if index is None:
            return
        elif isinstance(doctree[index], nodes.docinfo):
            md = app.env.metadata[app.env.current_document.docname]
            for node in doctree[index]:  # type: ignore[attr-defined]
                # nodes are multiply inherited...
                if isinstance(node, nodes.authors):
                    authors = cast('list[nodes.author]', node)
                    md['authors'] = [author.astext() for author in authors]
                elif isinstance(node, nodes.field):
                    assert len(node) == 2
                    field_name = cast('nodes.field_name', node[0])
                    field_body = cast('nodes.field_body', node[1])
                    md[field_name.astext()] = field_body.astext()
                elif isinstance(node, nodes.TextElement):
                    # other children must be TextElement
                    # see: https://docutils.sourceforge.io/docs/ref/doctree.html#bibliographic-elements  # NoQA: E501
                    md[node.__class__.__name__] = node.astext()

            for name, value in md.items():
                if name == 'tocdepth':
                    try:
                        value = int(value)
                    except ValueError:
                        value = 0
                    md[name] = value

            doctree.pop(index)


def setup(app: Sphinx) -> ExtensionMetadata:
    app.add_env_collector(MetadataCollector)

    return {
        'version': 'builtin',
        'parallel_read_safe': True,
        'parallel_write_safe': True,
    }
