from __future__ import annotations

import os
from functools import cached_property
from pathlib import Path

from docutils.parsers.rst import directives
from sphinx import addnodes
from sphinx.domains.std import StandardDomain
from sphinx.util.nodes import make_id

from sphinx_gha.common import (
    ActionsItemDirective,
    ActionsFileDirective,
    format_uses,
    yaml,
)


class ActionInputDirective(ActionsItemDirective):
    parent_role = "gh-actions:action"
    fields = ["required", "default"]
    option_spec = {"deprecationMessage": directives.unchanged_required}
    prefix = "input"

    def transform_content(self, content_node) -> None:
        super().transform_content(content_node)
        if deprecation_message := self.options.get("deprecationMessage"):
            admonition, msgs = self.format_deprecationMessage(deprecation_message)
            content_node.insert(0, admonition)


class ActionOutputDirective(ActionsItemDirective):
    parent_role = "gh-actions:action"
    prefix = "output"


class ActionEnvDirective(ActionsItemDirective):
    parent_role = "gh-actions:action"
    fields = ["required"]
    prefix = "env"

    def add_target_and_index(self, name: str, sig: str, signode) -> None:
        super().add_target_and_index(name, sig, signode)
        objtype = "envvar"
        node_id = make_id(self.env, self.state.document, objtype, name)
        signode["ids"].append(node_id)

        std: StandardDomain = self.env.domains["std"]
        std.note_object(objtype, signode["fullname"], node_id, location=signode)


class ActionDirective(ActionsFileDirective):
    role = "gh-actions:action"
    file_type = "action"

    @cached_property
    def path(self):
        path = self.options.get("path")
        if path is None:
            return None
        repo_root = Path(
            self.env.config["sphinx_gha_repo_root"] or os.getcwd()
        ).absolute()
        path = repo_root / Path(path)
        for filename in ["action.yml", "action.yaml"]:
            test_path = path / filename
            if test_path.exists():
                return test_path
        if path.is_file():
            return path

        self.error(f"Could not find an action definition at {path}")

    @classmethod
    def id_from_path(cls, path: Path):
        return path.parent.name

    @cached_property
    def example(self):
        if example_yaml := self.yaml.get("x-example"):
            return example_yaml

        if self.path is None:
            return ""

        slug = format_uses(self.path.parent, self.env)
        name = self.yaml.get("x-example-name")
        inputs = self.yaml.get("x_example_inputs") or {}
        env = self.yaml.get("x_example_env") or {}

        for k, d, e in [("x-env", env, "example"), ("inputs", inputs, "x-example")]:
            if action_inputs := self.yaml.get(k):
                for input_name, input_meta in action_inputs.items():
                    input_meta = input_meta or {}
                    if input_example := input_meta.get(e):
                        d[input_name] = input_example

        example_yaml = {}

        if name:
            example_yaml["name"] = name
        example_yaml["uses"] = slug
        if inputs:
            example_yaml["with"] = inputs
        if env:
            example_yaml["env"] = env

        example_yaml = [example_yaml]
        return yaml.dump(example_yaml)

    def transform_content(self, content_node: addnodes.desc_content) -> None:
        super().transform_content(content_node)

        # Items

        item_lists = [
            ("inputs", "Inputs", "action-input"),
            ("outputs", "Outputs", "action-output"),
            ("x-env", "Environment Variables", "action-envvar"),
        ]

        for key, title, directive in item_lists:
            if item_list := self.yaml.get(key):
                content_node.append(self.format_item_list(title, directive, item_list))
