"""
Silique (https://www.silique.fr)
Copyright (C) 2024-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 pathlib import Path
from rougail.utils import load_modules
from .i18n import _


ROUGAIL_VARIABLE_TYPE = (
    "https://rougail.readthedocs.io/en/latest/variable.html#variables-types"
)


OUTPUTS = None
TABULARS = None


def get_outputs_or_tabulars(type_) -> None:
    """Load all outputs or tabulars"""
    module_name = f"rougail.output_doc.{type_}"
    outputs = {}
    names = []
    for path in (Path(__file__).parent / type_).iterdir():
        name = path.name
        if not name.endswith(".py") or name.endswith("__.py"):
            continue
        module = load_modules(module_name + "." + name[:-3], str(path))
        if type_ == "output":
            if "Formatter" not in dir(module):
                continue
            obj_class = module.Formatter
        elif type_ == "tabular":
            if "Tabular" not in dir(module):
                continue
            obj_class = module.Tabular
        level = obj_class.level
        if level in outputs:
            raise ImportError(
                _('duplicated level rougail-output-doc for {0} "{1}": {2} and {3}').format(
                    type_, level, obj_class.name, outputs[level].name
                )
            )
        if obj_class.name in names:
            raise ImportError(
                _('duplicated name "{0}" in rougail-output-doc').format(
                    obj_class.name
                )
            )
        names.append(obj_class.name)
        outputs[level] = obj_class
    return {outputs[level].name: outputs[level] for level in sorted(outputs)}


class OutPuts:  # pylint: disable=R0903
    """Transformations applied on a object instance"""

    def __init__(
        self,
    ) -> None:
        global OUTPUTS
        if OUTPUTS is None:
            OUTPUTS = get_outputs_or_tabulars("output")

    def get(self) -> dict:
        """Get all outputs"""
        return OUTPUTS


class Tabulars:  # pylint: disable=R0903
    """Transformations applied on a object instance"""

    def __init__(
        self,
    ) -> None:
        global TABULARS
        if TABULARS is None:
            TABULARS = get_outputs_or_tabulars("tabular")

    def get(self) -> dict:
        """Get all tabulars"""
        return TABULARS


def get_rougail_config(
    *,
    backward_compatibility=True,  # pylint: disable=unused-argument
) -> dict:
    """Get documentation for output_doc modules"""
    outputs = list(OutPuts().get())
    output_format_default = outputs[0]
    tabulars = list(Tabulars().get())
    tabular_default = tabulars[0]
    rougail_options = f"""
cli:

  read_write:
    redefine: true
    exists: true
    type: boolean
    default:
      jinja: |-
        {{{{ __.step.output is not propertyerror and "doc" in __.step.output }}}}
      description: {_('is true if doc is activate')}

doc:
  description: {_('Generate documentation from structural files')}
  help: {_('The structural files contain all the information related to the variables. This output generates the documentation for all or some of these variables.')}
  disabled:
    jinja: |-
      {{{{ step.output is propertyerror or step.output != 'doc' }}}}
    return_type: boolean
    description: {_('if doc is not set in "step.output"')}

  output_format:
    description: {_('The output format of the generated documentation')}
    alternative_name: do
    default: { output_format_default }
    choices:
"""
    for output in outputs:
        rougail_options += f"      - {output}\n"
    rougail_options += f"""
  tabular_template:
    description: {_('Generate document with this tabular model')}
    help: {_("The variables are documented with a tabular view. A template selection allows you to choose the content of each column.")}
    alternative_name: dm
    default: { tabular_default }
    disabled:
      jinja: |-
        {{{{ _.output_format == 'json' }}}}
      return_type: boolean
      description: >
        {_('"_.output_format" in json is not compatible with this variable')}
    choices:
"""
    for tabular in tabulars:
        rougail_options += f"      - {tabular}\n"
    rougail_options += f"""
  contents:
    description: {_('Generated content')}
    help: {_('You can generate three type of document. All variables ("variables"), an example file in YAML format ("example") or change log ("changelog").')}
    alternative_name: dc
    choices:
      - variables
      - example
      - changelog
    default:
      - variables
    hidden:
      jinja: |-
        {{{{ _.output_format == 'json' }}}}
      return_type: boolean
      description: >
        {_('"_.output_format" in json is not compatible with changelog or example "_.contents"')}

  title_level:
    description: {_('Starting title level')}
    alternative_name: dt
    default: 1

  default_values:
    description: {_('Modify values to document all variables')}
    help: {_('To document leadership or dynamic family variables, it is sometimes necessary to generate values, which can change the values in the configuration. Be careful if you reuse this configuration.')}
    default: true

  true_color:
    description: {_('Display documentation in console always with true color terminal')}
    default: false
    disabled:
      variable: _.output_format
      when_not: console

  other_root_filenames:
    description: {_("The variables in this family are documented in another file")}
    help: {_("If you separate the variables into different files, the links between the variables will break. Therefore, you must define different filenames for the files containing these variables.")}
    type: leadership

    root_path:
      description: {_("This file contains child variables of the family")}
      mandatory: false

    filename:
      description: {_("Name of the file")}
      type: unix_filename
      params:
        allow_relative: true
        types:
          - file

  document_a_type:
    description: {_("Documentation a structural type")}
    default: false

  tabulars: 
    description: {_('Variables and changelog documentation')}
    disabled:
      jinja: |-
        {{{{ step.output is propertyerror or "doc" not in step.output or _.output_format == "json" or ("variables" not in _.contents and "changelog" not in _.contents) }}}}
      return_type: boolean
      description: {_('if "_.output_format" is json or "_.contents" hasn\'t variables or changelog')}

    without_family: 
      description: {_('Do not add families in documentation')}
      default: false
      alternative_name: df

    with_commandline:
      description: {_('Add command line informations in documentation')}
      alternative_name: dw
      default: false

    with_environment:
      description: {_('Add environment variable informations in documentation')}
      alternative_name: de
      default: false

    environment_prefix:
      description: {_("Environment variables prefix name")}
      alternative_name: dv
      default: ROUGAIL
      validators:
        - jinja: |-
            {{{{ (_.environment_prefix | upper) != _.environment_prefix }}}}
          return_type: boolean
          description: {_("should only use uppercase characters")}
      disabled:
        jinja: |-
          {{{{ ___.main_namespace is not defined or ___.main_namespace == none or _.with_environment is false }}}}
        return_type: boolean
        description: {_('if "main_namespace" is not set or "_.with_environment" is false')}

  changelog:
    description: {_('Changelog documentation')}
    disabled:
      jinja: |-
        {{{{ step.output is propertyerror or "doc" not in step.output or _.output_format == "json" or "changelog" not in _.contents }}}}
      return_type: boolean
      description: {_('if changelog in not in "_.contents"')}

    previous_json_file:
      description: {_('Previous description file in JSON format')}
      help: {_('To generate the changelog, you need to compare the old list of variables (in json format) with the current variables.')}
      alternative_name: dp

  examples: 
    description: {_('Examples configuration')}
    disabled:
      jinja: |-
        {{{{ step.output is propertyerror or "doc" not in step.output or "example" not in _.contents }}}}
      return_type: boolean
      description: {_('if example is not in "_.contents"')}

    comment:
      description: {_('Add description of variables and families when generate examples')}
      alternative_name: dx
      default: false

    comment_column:
      description: {_('Comment in examples starts at column')}
      default: 30
      disabled:
        variable: _.comment
        when: false
"""
    return {
        "name": "doc",
        "process": "output",
        "options": rougail_options,
        "allow_user_data": False,
        "level": 50,
    }

__all__ = ("OutPuts", "get_rougail_config")
