"""Extract python code blocks from a markdown file to generate a python script

The goal is to be able to test python code examples shown in a markdown file.

Usage:
    md2py.py <md_inputfile> <py_outputfile>

Arguments:
    <md_inputfile>   path of the markdownfile from which extract python code blocks
    <py_outputfile>  path of the resulting python script

"""

from __future__ import annotations

import commonmark
from docopt import docopt

PYTHON_FLAVORS = ["python", "py3", "python3"]


def extract_pythoncode_from_markdown(markdown_inputfile: str) -> list[str]:
    with open(markdown_inputfile, "rt") as fp:
        doc = fp.read()
    python_nodes = []
    parser = commonmark.Parser()
    ast = parser.parse(doc)
    walker = ast.walker()
    for node, entering in walker:
        if node.t == "code_block" and node.is_fenced and node.info in PYTHON_FLAVORS:
            python_nodes.append(node)
    return [node.literal for node in python_nodes]


def wrap_pythoncode_in_testfunction(pythoncodes: list[str]) -> str:
    docstring = '"""Python code auto-generated by scripts/md2py.py from code blocks within a markdown file."""\n'
    testfunction_header_pattern = "def test_code_within_markdown_{}():"
    tabs = "    "
    testfunctions: list[str] = []
    for i, pythoncode in enumerate(pythoncodes):
        testfunctions.append(
            "\n".join(
                [testfunction_header_pattern.format(i)]
                + [tabs + line if line else line for line in pythoncode.split("\n")]
            )
        )
    return "\n\n".join([docstring] + testfunctions)


if __name__ == "__main__":
    #  arguments
    arguments = docopt(__doc__)
    markdown_inputfile = arguments["<md_inputfile>"]
    python_outputfile = arguments["<py_outputfile>"]

    # extract code lines
    pythoncodes = extract_pythoncode_from_markdown(markdown_inputfile=markdown_inputfile)

    # wrap them in a test function
    python_string = wrap_pythoncode_in_testfunction(pythoncodes=pythoncodes)

    with open(python_outputfile, "wt") as fp:
        fp.write(python_string)
