import ast
import datetime
import subprocess

version = open("../debian/changelog").readlines()[0].split()[1].strip("()")
today = datetime.datetime.now().strftime("%d/%m/%Y")


def get_current_commit():
    p = subprocess.Popen(
        "git rev-parse --verify HEAD",
        shell=True,
        stdout=subprocess.PIPE,
        stderr=subprocess.STDOUT,
    )
    stdout, stderr = p.communicate()

    current_commit = stdout.strip().decode("utf-8")
    return current_commit


current_commit = get_current_commit()


def print_config_panel_docs():
    fname = "../src/utils/configpanel.py"
    content = open(fname).read()

    # NB: This magic is because we want to be able to run this script outside of a YunoHost context,
    # in which we cant really 'import' the file because it will trigger a bunch of moulinette/yunohost imports...
    tree = ast.parse(content)

    ConfigPanelClasses = reversed(
        [
            c
            for c in tree.body
            if isinstance(c, ast.ClassDef)
            and c.name in {"SectionModel", "PanelModel", "ConfigPanelModel"}
        ]
    )

    print("## Configuration panel structure")

    for c in ConfigPanelClasses:
        doc = ast.get_docstring(c)
        print("")
        print(f"### {c.name.replace('Model', '')}")
        print("")
        print(doc)
        print("")
        print("---")


def print_form_doc():
    fname = "../src/utils/form.py"
    content = open(fname).read()

    # NB: This magic is because we want to be able to run this script outside of a YunoHost context,
    # in which we cant really 'import' the file because it will trigger a bunch of moulinette/yunohost imports...
    tree = ast.parse(content)

    OptionClasses = [
        c
        for c in tree.body
        if isinstance(c, ast.ClassDef) and c.name.endswith("Option")
    ]

    OptionDocString = {}

    print("## List of all option types")

    for c in OptionClasses:
        if not isinstance(c.body[0], ast.Expr):
            continue
        option_type = None

        if c.name in {"BaseOption", "BaseInputOption"}:
            option_type = c.name
        elif c.body[1].target.id == "type":
            option_type = c.body[1].value.attr

        generaltype = (
            c.bases[0].id.replace("Option", "").replace("Base", "").lower()
            if c.bases
            else None
        )

        docstring = ast.get_docstring(c)
        if docstring:
            if "#### Properties" not in docstring:
                docstring += """
#### Properties

- [common properties](#common-properties)"""
            OptionDocString[option_type] = {
                "doc": docstring,
                "generaltype": generaltype,
            }

    # Dirty hack to have "BaseOption" as first and "BaseInputOption" as 2nd in list

    base = OptionDocString.pop("BaseOption")
    baseinput = OptionDocString.pop("BaseInputOption")
    OptionDocString2 = {
        "BaseOption": base,
        "BaseInputOption": baseinput,
    }
    OptionDocString2.update(OptionDocString)

    for option_type, infos in OptionDocString2.items():
        if option_type == "display_text":
            # display_text is kind of legacy x_x
            continue
        print("")
        if option_type == "BaseOption":
            print("### Common properties")
        elif option_type == "BaseInputOption":
            print("### Common inputs properties")
        else:
            print(
                f"### `{option_type}`"
                + (f" ({infos['generaltype']})" if infos["generaltype"] else "")
            )
        print("")
        print(infos["doc"])
        print("")
        print("---")


print(
    rf"""---
title: Technical details for config panel structure and form option types
template: docs
taxonomy:
    category: docs
routes:
  default: '/dev/forms'
---

Doc auto-generated by [this script](https://github.com/YunoHost/yunohost/blob/{current_commit}/doc/generate_options_doc.py) on {today} (YunoHost version {version})

## Glossary

You may encounter some named types which are used for simplicity.

- `Translation`: a translated property
    - used for properties: `ask`, `help` and `Pattern.error`
    - a `dict` with locales as keys and translations as values:
        ```toml
        ask.en = "The text in english"
        ask.fr = "Le texte en français"
        ```
        It is not currently possible for translators to translate those string in weblate.
    - a single `str` for a single english default string
        ```toml
        help = "The text in english"
        ```
- `JSExpression`: a `str` JS expression to be evaluated to `true` or `false`:
    - used for properties: `visible` and `enabled`
    - operators availables: `==`, `!=`, `>`, `>=`, `<`, `<=`, `!`, `&&`, `||`, `+`, `-`, `*`, `/`, `%` and `match()`
- `Binding`: bind a value to a file/property/variable/getter/setter/validator
    - save the value in `settings.yaml` when not defined
    - nothing at all with `"null"`
    - a custom getter/setter/validator with `"null"` + a function starting with `get__`, `set__`, `validate__` in `scripts/config`
    - a variable/property in a file with `:__FINALPATH__/my_file.php`
    - a whole file with `__FINALPATH__/my_file.php`
- `Pattern`: a `dict` with a regex to match the value against and an error message
    ```toml
    pattern.regexp = '^[A-F]\d\d$'
    pattern.error = "Provide a room number such as F12: one uppercase and 2 numbers"
    # or with translated error
    pattern.error.en = "Provide a room number such as F12: one uppercase and 2 numbers"
    pattern.error.fr = "Entrez un numéro de salle comme F12: une lettre majuscule et deux chiffres."
    ```
    - IMPORTANT: your `pattern.regexp` should be between simple quote, not double.

"""
)

print_config_panel_docs()
print_form_doc()