"""
Silique (https://www.silique.fr)
Copyright (C) 2026

This program is free software: you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by the
Free Software Foundation, either version 3 of the License, or (at your
option) any later version.

This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
details.

You should have received a copy of the GNU Lesser General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.
"""

from ruamel.yaml import CommentedMap
from pathlib import Path
from copy import deepcopy

from rougail import SUPPORTED_VERSION

from ..output_doc import RougailOutputDoc
from ..output_doc.utils import dump
from .i18n import _


class Doc(RougailOutputDoc):
    output_name = "ansible"
    # This documentation is not only for operator but also for integrator
    # That means we want to have hidden variable too in documentation
    only_disabled = True

    def __init__(
        self,
        config: "Config",
        *,
        rougailconfig: "RougailConfig" = None,
        **kwargs,
    ) -> None:
        self.config = config
        self.ori_rougailconfig = rougailconfig
        self.doc_rougailconfig = {
            "doc.tabulars.with_commandline": False,
            "doc.tabulars.with_environment": False,
            "main_namespace": None,
            "step.output": "ansible",
            "doc.tabulars.without_family": False,
            "doc.tabular_template": "six_columns",
            "doc.output_format": rougailconfig["ansible.doc.output_format"],
            "doc.true_color": True,
            "doc.document_a_type": True,
            "doc.examples.comment": False,
            "doc.examples.comment": True,
            "doc.examples.comment_column": 40,
            "doc.other_root_filenames": {},
            "modes_level": rougailconfig["modes_level"],
            "doc.title_level": 3,
        }
        self.comment_examples = True
        self.comment_examples_column = None
        super().__init__(config, rougailconfig=self.doc_rougailconfig, **kwargs)

    def run(self) -> str:
        types = [
            config.name()
            for config in self.config
            if config.name() != self.ori_rougailconfig["ansible.host_namespace"]
        ]
        self.root_config = self.config
        self.load()
        self.load_formatter()
        self.root_directory = self.ori_rougailconfig["ansible.doc.root_directory"]
        self.extra_vars = dict(self.ori_rougailconfig["ansible.doc.extra_vars"].items())
        if self.root_directory is None:
            self.root_directory = Path(".")
        else:
            self.root_directory = Path(self.root_directory)
        config = self._build_example_config()
        examples = self._gen_doc_examples(config, True, with_secret_manager=False)
        config.property.read_only()
        hidden = CommentedMap()
        config.property.remove("mandatory")
        self._example_parse_family(
            config.value.get(), hidden, dump_type="hidden", with_true_path=True
        )
        config.property.read_write()
        self.ansible_name = self.ori_rougailconfig["ansible.doc.project_name"]
        self._build_defaults(config)
        # FIXME        self._build_vars(examples)
        # Description
        author = self.ori_rougailconfig["ansible.doc.author"]
        name = f"{author}.{self.ansible_name}"
        help_ = None
        description = None
        if self.ansible_name in self.informations:
            if "help" in self.informations[self.ansible_name]["informations"]:
                help_ = self.informations[self.ansible_name]["informations"].pop("help")
            if "description" in self.informations[self.ansible_name]["informations"]:
                description = self.informations[self.ansible_name]["informations"][
                    "description"
                ]
                title = f"{name} - {description}"
            else:
                title = name
        else:
            title = name
        datas = [self.formatter.title(title, 1, collapse=False)]
        datas.append(
            _("This repository contains the {0} Ansible collection.").format(
                self.formatter.prop(name, False, False, False)
            )
        )
        if help_:
            datas.append("\n".join(help_))
        self.doc_rougailconfig["doc.title_level"] = 2
        for type_ in self.informations:
            current_informations = self.informations[type_]["informations"]
            if "description" in current_informations:
                current_informations["description"] = _(
                    'The group variables "{0}" - {1}'
                ).format(
                    current_informations["name"], current_informations["description"]
                )
            else:
                current_informations["description"] = _(
                    "The group variables {0}"
                ).format(current_informations["name"])
        datas.append(self.formatter.title(_("Variables"), 2, collapse=False))
        self.formatter.options()
        datas.extend(self.formatter.dict_to_dict(self.informations, level=3))
        yaml = self._build_collection(description, name)
        for t in types:
            tags = self.config.option(t).information.get("tags", [])
            for y in yaml:
                if self.extra_vars:
                    y["vars"].update(self.extra_vars)
            for y in yaml:
                if tags:
                    found = True
                    for tag in tags:
                        if y["ansible.builtin.import_playbook"].endswith("." + tag):
                            break
                    else:
                        found = False
                    if not found:
                        continue
                y["vars"][t] = f"{{{{ my_{t} }}}}"
        #
        rougail_version = float(SUPPORTED_VERSION[-1])
        if "version" in types:
            key = "_version"
        else:
            key = "version"

        if hidden:
            msg = "Hidden variables can only be modified within a structure file."
            datas.append(self.formatter.display_family_informations([msg]))
        end_family = self.formatter.end_family(level=2, collapse=False)
        if end_family:
            datas.append(end_family)
        datas.append(self.formatter.title(_("Usage"), 2, collapse=False))
        datas.append(self.formatter.title(_("Example Playbook with Rougail"), 3))
        structural = {key: rougail_version}
        for t in types:
            structural[f"my_{t}"] = {"type": t}
        if structural:
            customize = CommentedMap()
            old_config = self.config
            test_config = config.config.copy()
            test_config.property.read_only()
            test_config.property.remove("mandatory")
            test_config = test_config.forcepermissive
            self.config = test_config
            self._example_parse_family(
                test_config.value.get(),
                customize,
                dump_type="empty",
                with_secret_manager=False,
                with_true_path=True,
            )
            self.config = old_config
            datas.append(_("Add to your structural file something like:"))
            datas.append(self.formatter.yaml(dump(structural), yaml_version="1.2"))
            for t in types:
                if t in customize and t in hidden:
                    structural[f"my_{t}"].update(customize[t])
            datas.append(_("Customizing hidden variables in structure file:"))
            datas.append(self.formatter.yaml(dump(structural), yaml_version="1.2"))
            text = [_("Do not forget to add Rougail structure file as Rougail types.")]
            datas.append(self.formatter.display_family_informations(text))
        user_data = {}
        for t in types:
            if t in examples:
                user_data.update({f"my_{t}": examples[t]})
        if user_data:
            datas.append(
                _("For example you can add an YAML user data with something like:")
            )
            datas.append(self.formatter.yaml(dump(user_data)))
        datas.append(_("Add to your Play:"))
        _dump = "\n".join([d[2:] for d in ("  " + dump(yaml)).split("\n")])
        datas.append(self.formatter.yaml(_dump))
        #
        examples = self._gen_doc_examples(config, True)
        for t in types:
            if t in examples:
                for y in yaml:
                    if t in y["vars"]:
                        y["vars"][t] = deepcopy(examples[t])
                    if not y["vars"]:
                        del y["vars"][t]
            else:
                for y in yaml:
                    if not y["vars"]:
                        del y["vars"][t]
        _dump = "\n".join([d[2:] for d in ("  " + dump(yaml)).split("\n")])
        end_family = self.formatter.end_family(level=3)
        if end_family:
            datas.append(end_family)
        datas.append(self.formatter.title(_("Example Playbook without Rougail"), 3))
        datas.append(
            self.formatter.display_family_informations(
                [_("The variables will not be properly validated without Rougail.")]
            )
        )
        datas.append(self.formatter.yaml(_dump))
        end_family = self.formatter.end_family(level=3)
        if end_family:
            datas.append(end_family)
        end_family = self.formatter.end_family(level=2, collapse=False)
        if end_family:
            datas.append(end_family)
        end_family = self.formatter.end_family(level=1, collapse=False)
        if end_family:
            datas.append(end_family)
        return True, self.formatter.compute(datas)

    def _build_collection(self, description, name):
        self.collection_type = self.ori_rougailconfig["ansible.doc.collection_type"]
        done = False
        if self.collection_type != "roles":
            playbooks = dict(self.ori_rougailconfig["ansible.doc.playbooks"].items())
            playbooks_dir = self.root_directory / "playbooks"
            if playbooks_dir.is_dir():
                lst = []
                for playbook in playbooks_dir.iterdir():
                    if playbook.is_file() and playbook.suffix in [".yml", ".yaml"]:
                        playbook_name = playbook.stem
                        playbook_import = f"{name}.{playbook_name}"
                        done = True
                        if playbook_name in playbooks:
                            playbook_description = playbooks[playbook_name]
                        else:
                            playbook_description = f"Collection | {playbook_import}"
                        yaml = {
                            "name": playbook_description,
                            "hosts": "servers",
                            "vars": {},
                        }
                        yaml["ansible.builtin.import_playbook"] = playbook_import
                        lst.append(yaml)
        if self.collection_type != "playbooks" and not done:
            roles_dir = self.root_directory / "roles"
            if roles_dir.is_dir():
                yaml = {
                    "name": description,
                    "hosts": "servers",
                    "vars": {},
                }
                yaml["roles"] = []
                for role in roles_dir.iterdir():
                    if role.is_dir():
                        role_name = role.name
                        done = True
                        yaml["roles"].append({"role": f"{name}.{role_name}"})
                lst = [yaml]
        if not done:
            raise Exception(
                _(
                    "Unable to find a Playbook in playbooks/ directory or a role in roles/ directory"
                )
            )
        return lst

    def _build_defaults(self, config):
        values = config.value.get()
        results = CommentedMap()
        self._example_parse_family(values, results, dump_type="default")
        self._build_write("defaults", results)

    def _build_vars(self, results):
        self._build_write("vars", results)

    def _build_write(self, write_type, results):
        if not results:
            return
        role_dir = self.root_directory / "roles" / self.ansible_name
        if role_dir.is_dir():
            filedir = role_dir / write_type
        else:
            filedir = self.root_directory / write_type
        filedir.mkdir(exist_ok=True)
        with (filedir / "main.yml").open("w") as fh:
            fh.write(f"---\n{dump(results)}\n")
