from __future__ import annotations

from typing import TYPE_CHECKING

from docutils import nodes
from docutils.parsers.rst.directives.admonitions import BaseAdmonition

from sphinx import addnodes
from sphinx.util.docutils import SphinxDirective

if TYPE_CHECKING:
    from typing import ClassVar

    from docutils.nodes import Node

    from sphinx.application import Sphinx
    from sphinx.util.typing import ExtensionMetadata, OptionSpec


def _collapsible_arg(argument: str | None) -> str:
    if argument is None:
        return 'open'
    if (value := argument.lower().strip()) in {'open', 'closed'}:
        return value
    msg = f'"{argument}" unknown; choose from "open" or "closed".'
    raise ValueError(msg)


class SphinxAdmonition(BaseAdmonition, SphinxDirective):
    option_spec: ClassVar[OptionSpec] = BaseAdmonition.option_spec.copy()  # type: ignore[union-attr]
    option_spec |= {
        'collapsible': _collapsible_arg,
    }

    node_class: type[nodes.Admonition] = nodes.admonition
    """Subclasses must set this to the appropriate admonition node class."""

    def run(self) -> list[Node]:
        (admonition_node,) = super().run()
        return [admonition_node]


class Admonition(SphinxAdmonition):
    required_arguments = 1
    node_class = nodes.admonition


class Attention(SphinxAdmonition):
    node_class = nodes.attention


class Caution(SphinxAdmonition):
    node_class = nodes.caution


class Danger(SphinxAdmonition):
    node_class = nodes.danger


class Error(SphinxAdmonition):
    node_class = nodes.error


class Hint(SphinxAdmonition):
    node_class = nodes.hint


class Important(SphinxAdmonition):
    node_class = nodes.important


class Note(SphinxAdmonition):
    node_class = nodes.note


class Tip(SphinxAdmonition):
    node_class = nodes.tip


class Warning(SphinxAdmonition):
    node_class = nodes.warning


class SeeAlso(SphinxAdmonition):
    """An admonition mentioning things to look at as reference."""

    node_class = addnodes.seealso


def setup(app: Sphinx) -> ExtensionMetadata:
    app.add_directive('admonition', Admonition, override=True)
    app.add_directive('attention', Attention, override=True)
    app.add_directive('caution', Caution, override=True)
    app.add_directive('danger', Danger, override=True)
    app.add_directive('error', Error, override=True)
    app.add_directive('hint', Hint, override=True)
    app.add_directive('important', Important, override=True)
    app.add_directive('note', Note, override=True)
    app.add_directive('tip', Tip, override=True)
    app.add_directive('warning', Warning, override=True)
    app.add_directive('seealso', SeeAlso, override=True)

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