package wand

import (
	"fmt"
	"github.com/iancoleman/strcase"
	"io"
	"strings"
)

func header(level int) string {
	s := make([]string, level+2)
	return strings.Join(s, "#")
}

func formatMarkdownCommand(printf func(string, ...any), println func(...any), doc doc, level int) {
	printf("%s Synopsis\n\n", header(level))
	println("```")
	printf(doc.synopsis.command)
	if doc.synopsis.hasOptions {
		printf(" [options...]")
	}

	for i := range doc.synopsis.arguments.names {
		printf(
			" [%s %s]",
			doc.synopsis.arguments.names[i],
			doc.synopsis.arguments.types[i],
		)
	}

	if doc.synopsis.arguments.variadic {
		printf("...")
	}

	println()
	println("```")

	min := doc.synopsis.arguments.minPositional
	max := doc.synopsis.arguments.maxPositional
	if doc.synopsis.arguments.variadic {
		if min > 0 || max > 0 {
			println()
		}

		switch {
		case min > 0 && max > 0:
			printf("min %d and max %d total positional arguments\n", min, max)
		case min > 0:
			printf("min %d total positional arguments\n", min)
		case max > 0:
			printf("max %d total positional arguments\n", max)
		}
	}

	if doc.synopsis.hasSubcommands {
		println()
		println("```")
		for _, sc := range doc.subcommands {
			printf("%s %s\n", escapeMD(doc.name), sc.name)
		}

		println("```")
	}

	if doc.description != "" {
		printf("\n%s Description\n\n", header(level))
		println(escapeMD(paragraphs(doc.description)))
	}

	if len(doc.options) > 0 {
		printf("\n%s Options\n\n", header(level))
		if doc.hasBoolOptions {
			printf("- [b]: booelan flag, true or false, or no argument means true\n")
		}

		if doc.hasListOptions {
			printf("- [*]: accepts multiple instances of the same option\n")
		}

		if doc.hasBoolOptions || doc.hasListOptions {
			println()
		}

		names, descriptions := prepareOptions(doc.options)
		for _, n := range names {
			printf("- **%s**: %s\n", escapeMD(n), escapeMD(lines(descriptions[n])))
		}
	}

	if len(doc.options) > 0 && commandNameExpression.MatchString(doc.appName) {
		printf("\n.%s Environment Variables\n\n", header(level))
		println(escapeMD(paragraphs(envDocs)))
		println()
		println("Example environment variable:")
		println()
		o := doc.options[0]
		println("```")
		printf(strcase.ToSnake(fmt.Sprintf("%s-%s", doc.appName, o.name)))
		printf("=")
		if o.isBool {
			printf("true")
		} else {
			printf("42")
		}

		println()
		println("```")
	}

	if len(doc.options) > 0 && len(doc.configFiles) > 0 {
		printf("\n.Configuration Files\n\n")
		println(escapeMD(paragraphs(configDocs)))
		println()
		println("Config files:")
		println()
		for _, cf := range doc.configFiles {
			if cf.fromOption {
				println("- zero or more configuration files defined by the --config option\n")
				continue
			}

			if cf.fn != "" {
				printf("- %s", cf.fn)
				if cf.optional {
					printf(" (optional)")
				}

				println()
				continue
			}
		}

		println()
		println("Example configuration entry:")
		println()
		o := doc.options[0]
		println("```")
		printf("# default for --%s:\n", o.name)
		printf(strcase.ToSnake(o.name))
		printf(" = ")
		if o.isBool {
			printf("true")
		} else {
			printf("42")
		}

		println()
		println("```")
		println()
		println("Example for discarding an inherited entry:")
		println()
		println("```")
		println("# discarding an inherited entry:")
		println(strcase.ToSnake(o.name))
		println("```")
	}
}

func formatMarkdownMultiCommand(out io.Writer, doc doc, level int) error {
	printf, println, finish := printer(out)
	printf("%s %s\n\n", header(level), escapeMD(doc.appName))
	println("Provides several commands:")
	println()
	allCommands := allCommands(doc)
	for _, c := range allCommands {
		printf("- %s\n", escapeMD(c.fullCommand))
	}

	println()
	for _, c := range allCommands {
		printf("%s %s\n\n", header(level+1), escapeMD(c.fullCommand))
		formatMarkdownCommand(printf, println, c, level+2)
	}

	return finish()
}

func formatMarkdownSingleCommand(out io.Writer, doc doc, level int) error {
	printf, println, finish := printer(out)
	printf("%s %s\n\n", header(level), doc.appName)
	formatMarkdownCommand(printf, println, doc, level+1)
	return finish()
}

func formatMarkdown(out io.Writer, doc doc, level int) error {
	var hasSubcommands bool
	for _, sc := range doc.subcommands {
		if !sc.isHelp && !sc.isVersion {
			continue
		}

		hasSubcommands = true
		break
	}

	if hasSubcommands {
		return formatMarkdownMultiCommand(out, doc, level)
	}

	return formatMarkdownSingleCommand(out, doc, level)
}
