"""
Manifest Revisions Table
========================

This extension allows to render a table containing the revisions of the projects
present in a manifest file.

Usage
*****

This extension introduces a new directive: ``manifest-projects-table``. It can
be used in the code as::

    .. manifest-projects-table::
        :filter: active

where the ``:filter:`` option can have the following values: active, inactive, all.

Options
*******

- ``manifest_projects_table_manifest``: Path to the manifest file.

Copyright (c) Nordic Semiconductor ASA 2022
Copyright (c) Intel Corp 2023
SPDX-License-Identifier: Apache-2.0
"""

import re
from typing import Any

from docutils import nodes
from docutils.parsers.rst import directives
from sphinx.application import Sphinx
from sphinx.util.docutils import SphinxDirective
from west.manifest import Manifest

__version__ = "0.1.0"


class ManifestProjectsTable(SphinxDirective):
    """Manifest revisions table."""

    option_spec = {
        "filter": directives.unchanged,
    }

    @staticmethod
    def rev_url(base_url: str, rev: str) -> str:
        """Return URL for a revision.

        Notes:
            Revision format is assumed to be a git hash or a tag. URL is
            formatted assuming a GitHub base URL.

        Args:
            base_url: Base URL of the repository.
            rev: Revision.

        Returns:
            URL for the revision.
        """

        if re.match(r"^[0-9a-f]{40}$", rev):
            return f"{base_url}/commit/{rev}"

        return f"{base_url}/releases/tag/{rev}"

    def run(self) -> list[nodes.Element]:
        active_filter = self.options.get("filter", None)

        manifest = Manifest.from_file(self.env.config.manifest_projects_table_manifest)
        projects = []
        for project in manifest.projects:
            if project.name == "manifest":
                continue
            if (
                active_filter == "active"
                and manifest.is_active(project)
                or active_filter == "inactive"
                and not manifest.is_active(project)
                or active_filter == "all"
                or active_filter is None
            ):
                projects.append(project)

        # build table
        table = nodes.table()

        tgroup = nodes.tgroup(cols=2)
        tgroup += nodes.colspec(colwidth=1)
        tgroup += nodes.colspec(colwidth=1)
        table += tgroup

        thead = nodes.thead()
        tgroup += thead

        row = nodes.row()
        thead.append(row)

        entry = nodes.entry()
        entry += nodes.paragraph(text="Project")
        row += entry
        entry = nodes.entry()
        entry += nodes.paragraph(text="Revision")
        row += entry

        rows = []
        for project in projects:
            row = nodes.row()
            rows.append(row)

            entry = nodes.entry()
            entry += nodes.paragraph(text=project.name)
            row += entry
            entry = nodes.entry()
            par = nodes.paragraph()
            par += nodes.reference(
                project.revision,
                project.revision,
                internal=False,
                refuri=ManifestProjectsTable.rev_url(project.url, project.revision),
            )
            entry += par
            row += entry

        tbody = nodes.tbody()
        tbody.extend(rows)
        tgroup += tbody

        return [table]


def setup(app: Sphinx) -> dict[str, Any]:
    app.add_config_value("manifest_projects_table_manifest", None, "env")

    directives.register_directive("manifest-projects-table", ManifestProjectsTable)

    return {
        "version": __version__,
        "parallel_read_safe": True,
        "parallel_write_safe": True,
    }
