#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from __future__ import print_function
import os
import re


def compile(service, version, variable, alias, image, scheme, ports, sponsors, options, unimplemented, dokku_version):
    prefix = "\n\n".join([
        header(service),
        description(service, image, version),
    ])

    if len(sponsors) > 0:
        prefix += "\n\n"
        prefix += sponsors_section(service, sponsors)

    return (
        "\n\n".join(
            [
                prefix,
                requirements_section(dokku_version),
                installation_section(service, dokku_version),
                commands_section(service, variable, alias, image, scheme, ports, unimplemented),
                usage_section(service, variable, alias, image, scheme, ports, options, unimplemented),
            ]
        )
        .replace("\n\n\n\n\n", "\n")
        .replace("\n\n\n\n", "\n")
        .replace("\n\n\n", "\n\n")
    )


def header(service):
    return " ".join(
        [
            f"# dokku {service}",
            f'[![Build Status](https://img.shields.io/github/actions/workflow/status/dokku/dokku-{service}/ci.yml?branch=master&style=flat-square "Build Status")](https://github.com/dokku/dokku-{service}/actions/workflows/ci.yml?query=branch%3Amaster)',
            f'[![IRC Network](https://img.shields.io/badge/irc-libera-blue.svg?style=flat-square "IRC Libera")](https://webchat.libera.chat/?channels=dokku)',
        ]
    )


def description(service, full_image, version):
    base = "_"
    image = full_image
    if "/" in full_image:
        base = "r/" + full_image.split("/")[0]
        image = full_image.split("/")[1]

    return f"Official {service} plugin for dokku. Currently defaults to installing [{full_image} {version}](https://hub.docker.com/{base}/{image}/)."


def sponsors_section(service, sponsors):
    if len(sponsors) == 0:
        return ""

    sponsor_data = ["## Sponsors", "", f"The {service} plugin was generously sponsored by the following:", ""]
    sponsor_data.extend([f"- [{s}](https://github.com/{s})" for s in sponsors])

    return "\n".join(
        sponsor_data
    )


def requirements_section(dokku_version):
    return "\n".join(
        ["## Requirements", "", f"- dokku {dokku_version}", "- docker 1.8.x",]
    )


def installation_section(service, dokku_version):
    return "\n".join(
        [
            "## Installation",
            "",
            "```shell",
            f"# on {dokku_version}",
            f"sudo dokku plugin:install https://github.com/dokku/dokku-{service}.git {service}",
            "```",
        ]
    )


def commands_section(service, variable, alias, image, scheme, ports, unimplemented):
    content = [
        "## Commands",
        "",
        "```",
    ]

    subcommands = os.listdir("subcommands")
    subcommands.sort()

    command_list = []
    descriptions = []
    for filename in subcommands:
        if filename in unimplemented:
            continue
        data = command_data(filename, service, variable, alias, image, scheme, ports)
        description = data["description"]
        arguments = data["arguments_string"]

        command_list.append(f"{service}:{filename} {arguments}")
        descriptions.append(description)

    maxlen = max(map(len, command_list))
    if maxlen > 50:
        maxlen = 50
    for command, description in zip(command_list, descriptions):
        space_count = maxlen - len(command)
        content.append("{0}{1} # {2}".format(command, " " * space_count, description))

    content.append("```")
    return "\n".join(content)


def usage_section(service, variable, alias, image, scheme, ports, options, unimplemented):
    return "\n\n".join(
        [
            "## Usage",
            f"Help for any commands can be displayed by specifying the command as an argument to {service}:help. Plugin help output in conjunction with any files in the `docs/` folder is used to generate the plugin documentation. Please consult the `{service}:help` command for any undocumented commands.",
            usage_intro(service, variable, alias, image, scheme, ports, options, unimplemented),
            usage_lifecycle(service, variable, alias, image, scheme, ports, options, unimplemented),
            usage_automation(service, variable, alias, image, scheme, ports, options, unimplemented),
            usage_data_management(service, variable, alias, image, scheme, ports, options, unimplemented),
            usage_backup(service, variable, alias, image, scheme, ports, options, unimplemented),
            usage_docker_pull(service, variable, alias, image, scheme, ports, options, unimplemented),
        ]
    )


def usage_intro(service, variable, alias, image, scheme, ports, options, unimplemented):
    commands = ["create", "info", "list", "logs", "link", "unlink", "set"]
    content = ["### Basic Usage"]

    return fetch_commands_content(
        service, variable, alias, image, scheme, ports, options, unimplemented, commands, content
    )


def usage_lifecycle(service, variable, alias, image, scheme, ports, options, unimplemented):
    commands = [
        "connect",
        "enter",
        "expose",
        "unexpose",
        "promote",
        "start",
        "stop",
        "pause",
        "restart",
        "upgrade",
    ]
    content = [
        "### Service Lifecycle",
        "",
        "The lifecycle of each service can be managed through the following commands:",
        "",
    ]

    return fetch_commands_content(
        service, variable, alias, image, scheme, ports, options, unimplemented, commands, content
    )


def usage_automation(service, variable, alias, image, scheme, ports, options, unimplemented):
    commands = ["app-links", "clone", "exists", "linked", "links"]
    content = [
        "### Service Automation",
        "",
        "Service scripting can be executed using the following commands:",
        "",
    ]

    return fetch_commands_content(
        service, variable, alias, image, scheme, ports, options, unimplemented, commands, content
    )


def usage_data_management(service, variable, alias, image, scheme, ports, options, unimplemented):
    commands = ["import", "export"]
    content = [
        "### Data Management",
        "",
        "The underlying service data can be imported and exported with the following commands:",
        "",
    ]

    return fetch_commands_content(
        service, variable, alias, image, scheme, ports, options, unimplemented, commands, content
    )


def usage_backup(service, variable, alias, image, scheme, ports, options, unimplemented):
    commands = [
        "backup-auth",
        "backup-deauth",
        "backup",
        "backup-set-encryption",
        "backup-unset-encryption",
        "backup-schedule",
        "backup-schedule-cat",
        "backup-unschedule",
    ]
    content = [
        "### Backups",
        "",
        "Datastore backups are supported via AWS S3 and S3 compatible services like [minio](https://github.com/minio/minio).",
        "",
        "You may skip the `backup-auth` step if your dokku install is running within EC2 and has access to the bucket via an IAM profile. In that case, use the `--use-iam` option with the `backup` command.",
        "",
        "Backups can be performed using the backup commands:",
        "",
    ]

    return fetch_commands_content(
        service, variable, alias, image, scheme, ports, options, unimplemented, commands, content
    )


def usage_docker_pull(service, variable, alias, image, scheme, ports, options, unimplemented):
    service_prefix = service.upper()
    return "\n".join(
        [
            "### Disabling `docker image pull` calls",
            "",
            f"If you wish to disable the `docker image pull` calls that the plugin triggers, you may set the `{service_prefix}_DISABLE_PULL` environment variable to `true`. Once disabled, you will need to pull the service image you wish to deploy as shown in the `stderr` output.",
            "",
            "Please ensure the proper images are in place when `docker image pull` is disabled.",
        ]
    )


def fetch_commands_content(
    service, variable, alias, image, scheme, ports, options, unimplemented, commands, content
):
    i = 0
    for command in commands:
        output = command_help(command, service, variable, alias, image, scheme, ports, options, unimplemented)
        if output == "":
            continue
        content.append(output)
        i += 1

    if i == 0:
        return ""

    return "\n".join(content)


def parse_args(line):
    line = line.strip()
    arguments = []
    for arg in re.findall("([A-Z_]+)", line):
        arg = arg.replace("_", "-").lower()
        if arg.endswith("optional-flag"):
            arg = arg.replace("-optional-flag", "")
            arguments.append(f"[--{arg}]")
        elif arg.endswith("-flag"):
            if arg == "info-flag":
                arguments.append(f"[--single-info-flag]")
            else:
                arg = arg.replace("-flag", "")
                first_letter = arg[0]
                arguments.append(f"[-{first_letter}|--{arg}]")
        elif arg.endswith("-flags-list"):
            arg = arg.replace("-list", "")
            arguments.append(f"[--{arg}...]")
        elif arg.endswith("list"):
            arg = arg.replace("-list", "")
            arguments.append(f"<{arg}...>")
        else:
            arguments.append(f"<{arg}>")
    return " ".join(arguments)


def command_help(command, service, variable, alias, image, scheme, ports, options, unimplemented):
    if command in unimplemented:
        return ""

    data = command_data(command, service, variable, alias, image, scheme, ports)
    content = [
        f"### {data['description']}",
        "",
        "```shell",
        "# usage",
        f"dokku {service}:{command} {data['arguments_string']}",
        "```",
    ]

    # if len(data["arguments"]) > 0:
    #     content.append("")
    #     content.append("arguments:")
    #     content.append("")
    # for argument in data["arguments"]:
    #     content.append(f"- {argument}")

    if len(data["flags"]) > 0:
        content.append("")
        content.append("flags:")
        content.append("")
    for flag in data["flags"]:
        if "--config-options" in flag and options != "":
            flag = f"{flag} (default: `{options}`)"
        content.append(f"- {flag}")

    if len(data["examples"]) > 0:
        content.append("")
        content.append(data["examples"])

    doc_file = os.path.join("docs", f"{command}.md")
    if os.path.isfile(doc_file):
        content.append("")
        with open(doc_file) as f:
            content.append(f.read())

    return "\n" + "\n".join(content)


def command_data(command, service, variable, alias, image, scheme, ports):
    description = None
    arguments = []
    arguments_string = ""
    example_lines = []
    flags = []
    with open(os.path.join("subcommands", command)) as f:
        for line in f.readlines():
            line = line.strip()
            line = line.replace("$PLUGIN_SERVICE", service)
            line = line.replace("$PLUGIN_COMMAND_PREFIX", service)
            line = line.replace("${PLUGIN_COMMAND_PREFIX}", service)
            line = line.replace("${PLUGIN_VARIABLE}", variable)
            line = line.replace("${PLUGIN_DEFAULT_ALIAS}", alias)
            line = line.replace("${PLUGIN_IMAGE}", image)
            line = line.replace("${PLUGIN_SCHEME}", scheme)
            line = line.replace("${PLUGIN_DATASTORE_PORTS[0]}", ports[0])
            line = line.replace("${PLUGIN_DATASTORE_PORTS[@]}", " ".join(ports))

            if "declare desc" in line:
                description = re.search('"(.+)"', line).group(1)
            elif "$1" in line:
                arguments_string = parse_args(line)
            elif line.startswith("#A "):
                argument = line.replace("#A ", "")
                parts = [a.strip() for a in argument.split(",", 1)]
                arguments.append(f"`{parts[0]}`: {parts[1]}")
            elif line.startswith("#F "):
                flag = line.replace("#F ", "")
                parts = [a.strip() for a in flag.split(",", 1)]
                flags.append(f"`{parts[0]}`: {parts[1]}")
            elif line.startswith("#E "):
                example_lines.append(line.replace("#E ", ""))

    examples = []
    sentence_lines = []
    command_lines = []
    codeblock_lines = []
    blockquote_lines = []
    for line in example_lines:
        if line.startswith("export") or line.startswith("dokku"):
            if len(blockquote_lines) > 0:
                examples.append("\n" + process_blockquote(blockquote_lines))
                blockquote_lines = []
            if len(codeblock_lines) > 0:
                examples.append("\n" + process_codeblock(codeblock_lines))
                codeblock_lines = []
            if len(sentence_lines) > 0:
                examples.append("\n" + process_sentence(sentence_lines))
                sentence_lines = []

            command_lines.append(line)
        elif line.startswith("    "):
            if len(blockquote_lines) > 0:
                examples.append("\n" + process_blockquote(blockquote_lines))
                blockquote_lines = []
            if len(command_lines) > 0:
                examples.append("\n" + process_command(command_lines))
                command_lines = []
            if len(sentence_lines) > 0:
                examples.append("\n" + process_sentence(sentence_lines))
                sentence_lines = []

            codeblock_lines.append(line.strip())
        elif line.startswith(">"):
            if len(codeblock_lines) > 0:
                examples.append("\n" + process_codeblock(codeblock_lines))
                codeblock_lines = []
            if len(command_lines) > 0:
                examples.append("\n" + process_command(command_lines))
                command_lines = []
            if len(sentence_lines) > 0:
                examples.append("\n" + process_sentence(sentence_lines))
                sentence_lines = []

            blockquote_lines.append(line)
        else:
            if len(blockquote_lines) > 0:
                examples.append("\n" + process_blockquote(blockquote_lines))
                blockquote_lines = []
            if len(codeblock_lines) > 0:
                examples.append("\n" + process_codeblock(codeblock_lines))
                codeblock_lines = []
            if len(command_lines) > 0:
                examples.append("\n" + process_command(command_lines))
                command_lines = []

            sentence_lines.append(line)

    if len(blockquote_lines) > 0:
        examples.append("\n" + process_blockquote(blockquote_lines))
        blockquote_lines = []
    if len(codeblock_lines) > 0:
        examples.append("\n" + process_codeblock(codeblock_lines))
        codeblock_lines = []
    if len(command_lines) > 0:
        examples.append("\n" + process_command(command_lines))
        command_lines = []
    if len(sentence_lines) > 0:
        examples.append("\n" + process_sentence(sentence_lines))
        sentence_lines = []

    return {
        "description": description,
        "arguments_string": arguments_string,
        "arguments": arguments,
        "flags": flags,
        "examples": "\n".join(examples).strip(),
    }


def process_sentence(sentence_lines):
    sentence_lines = " ".join(sentence_lines)
    sentences = ". ".join(
        upperfirst(i.strip()) for i in sentence_lines.split(". ")
    ).strip()
    if not sentences.endswith(".") and not sentences.endswith(":"):
        sentences += ":"

    text = []
    for sentence in sentences.split(". "):
        parts = []
        for word in sentence.strip().split(" "):
            if word.isupper() and len(word) > 1:
                for ending in [':', '.']:
                    if word.endswith(ending):
                        word = '`{0}`{1}'.format(word[:-1], ending)
                else:
                    word = '`{0}`'.format(word)
            parts.append(word)
        text.append(" ".join(parts))

    text = ". ".join(text)

    # some cleanup
    text = text.replace("(0.0.0.0)", "(`0.0.0.0`)")
    text = text.replace("'", "`")
    text = text.replace("`s", "'s")
    text = text.replace("``", "`")
    text = text.strip(" ")

    return text


def upperfirst(x):
    return x[:1].upper() + x[1:]


def process_blockquote(blockquote_lines):
    return "\n".join(blockquote_lines)


def process_command(command_lines):
    command_lines = "\n".join(command_lines)
    return f"```shell\n{command_lines}\n```"


def process_codeblock(codeblock_lines):
    codeblock_lines = "\n".join(codeblock_lines)
    return f"```\n{codeblock_lines}\n```"


def main():
    service = None
    version = None
    variable = None
    image = None
    alias = None
    options = None
    unimplemented = []

    with open("Dockerfile") as f:
        for line in f.readlines():
          if "FROM " in line:
              image, version = line.split(" ")[1].split(":")
              image = image.strip()
              version = version.strip()

    with open("config") as f:
        for line in f.readlines():
            if "PLUGIN_COMMAND_PREFIX=" in line:
                service = re.search('"(.+)"', line).group(1)
            if "PLUGIN_DEFAULT_ALIAS=" in line:
                alias = re.search('"(.+)"', line).group(1)
            if "PLUGIN_VARIABLE=" in line:
                variable = re.search('"(.+)"', line).group(1)
            if "PLUGIN_SCHEME=" in line:
                scheme = re.search('"(.+)"', line).group(1)
            if "PLUGIN_DATASTORE_PORTS=" in line:
                ports = re.search("\((.+)\)", line).group(1).split(" ")
            if "PLUGIN_UNIMPLEMENTED_SUBCOMMANDS=" in line:
                match = re.search("\((.+)\)", line)
                if match is not None:
                    unimplemented = [s.strip('"') for s in match.group(1).split(" ")]

    with open("config") as f:
        for line in f.readlines():
            if f"{variable}_CONFIG_OPTIONS" in line:
                match = re.search('"(.+)"', line)
                if match is not None:
                    options = match.group(1)

    sponsors = []
    with open("plugin.toml") as f:
        for line in f.readlines():
            if line.startswith("sponsors"):
                sponsors = re.search("\[([\"\w\s,_-]+)\]", line).group(1)
                sponsors = [s.strip("\"") for s in sponsors.split(",")]

    text = compile(service, version, variable, alias, image, scheme, ports, sponsors, options, unimplemented, "0.19.x+")

    base_path = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
    readme_file = os.path.join(base_path, "README.md")
    with open(readme_file, "w") as f:
        f.write(text + "\n")


if __name__ == "__main__":
    main()
