import json
import sys
from argparse import ArgumentParser, FileType


def describe_array(prop: dict) -> str:
    extra = ""
    if "items" in prop:
        unique_qualifier = ""
        if "uniqueItems" in prop:
            unique_qualifier = "unique" if prop["uniqueItems"] else "non-unique"
        item_type = describe_type(prop["items"])
        extra = " ".join(filter(bool, ["of", unique_qualifier, item_type, "items"]))
    return extra


def describe_number(prop: dict) -> str:
    extra = []
    if "minimum" in prop:
        extra.append(f">= {prop['minimum']}")
    if "maximum" in prop:
        extra.append(f"<= {prop['maximum']}")
    return ",".join(extra)


EXTRA_DESCRIPTORS = {
    "array": describe_array,
    "number": describe_number,
}


def describe_type(prop: dict) -> str:
    prop_type = prop["type"]
    types = prop_type if isinstance(prop_type, list) else [prop_type]
    if "null" in types:
        types.remove("null")
    if len(types) == 1:
        prop_type = types[0]
    parts = [f"`{prop_type}`"]
    for option in types:
        if option in EXTRA_DESCRIPTORS:
            parts.append(EXTRA_DESCRIPTORS[option](prop))
    if "enum" in prop:
        allowed_values = [f"`{value!r}`" for value in prop["enum"]]
        parts.append("(one of: " + ", ".join(allowed_values) + ")")
    return " ".join(parts)


def convert_schema(schema: dict, source: str = None) -> str:
    lines = [
        f"# {schema['title']}",
        schema["description"],
        "",
        "| **Configuration Key** | **Type** | **Description** | **Default** ",
        "|----|----|----|----|",
    ]
    for key, prop in schema["properties"].items():
        description = prop.get("description", "")
        default = json.dumps(prop.get("default", ""))
        lines.append(
            f"| `{key}` | {describe_type(prop)} | {description} | `{default}` |"
        )

    if source:
        lines.append(
            f"\nThis documentation was generated from `{source}`."
            " Please do not edit this file directly."
        )

    # ensure empty line at the end
    lines.append("")

    return "\n".join(lines)


def main(argv) -> None:
    parser = ArgumentParser()
    parser.add_argument("schema", type=FileType())
    parser.add_argument("markdown", type=FileType("w+"), default=sys.stdout)
    arguments = parser.parse_args(argv[1:])
    schema = json.loads(arguments.schema.read())
    markdown = convert_schema(schema, source=arguments.schema.name)
    arguments.markdown.write(markdown)


if __name__ == "__main__":
    main(sys.argv)
