package wand

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

func formatManCommand(printf func(string, ...any), println func(...any), doc doc) {
	println(".SH Synopsis")
	printf(".B %s", escapeRoff(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],
		)
	}

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

		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 {
		if min > 0 || max > 0 {
			println(".PP")
		}

		for i, sc := range doc.subcommands {
			if i > 0 {
				println(".br")
			}

			printf("%s %s\n", escapeRoff(doc.name), sc.name)
		}
	}

	if doc.description != "" {
		println(".SH Description")
		println(manParagraphs(escapeRoff(doc.description)))
	}

	if len(doc.options) > 0 {
		println(".SH Options")
		if doc.hasBoolOptions || doc.hasListOptions {
			println(".PP")
		}

		if doc.hasBoolOptions {
			println(".B [b]:")
			println("booelan flag, true or false, or no argument means true")
		}

		if doc.hasListOptions {
			if doc.hasBoolOptions {
				println(".br")
			}

			println(".B [*]:")
			println("accepts multiple instances of the same option")
		}

		names, descriptions := prepareOptions(doc.options)
		for _, n := range names {
			println(".TP")
			printf(".B %s\n", escapeRoff(n))
			if descriptions[n] != "" {
				println(manLines(escapeRoff(descriptions[n])))
			}
		}
	}

	if len(doc.options) > 0 && commandNameExpression.MatchString(doc.appName) {
		println(".SH Environment Variables")
		println(manParagraphs(escapeRoff(envDocs)))
		println(".PP Example environment variable:")
		o := doc.options[0]
		println(".TP")
		printf(strcase.ToSnake(fmt.Sprintf("%s-%s", doc.appName, o.name)))
		printf("=")
		if o.isBool {
			printf("true")
		} else {
			printf("42")
		}

		println()
	}

	if len(doc.options) > 0 && len(doc.configFiles) > 0 {
		println(".SH Configuration Files")
		println(manParagraphs(escapeRoff(configDocs)))
		println(".PP Config files:")
		for i, cf := range doc.configFiles {
			if i > 0 {
				println(".br")
			}

			if cf.fromOption {
				println(escapeRoff("zero or more configuration files defined by the --config option"))
				continue
			}

			if cf.fn != "" {
				printf(escapeRoff(cf.fn))
				if cf.optional {
					printf(" (optional)")
				}

				println()
				continue
			}
		}

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

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

func formatManMultiCommand(out io.Writer, doc doc) error {
	printf, println, finish := printer(out)
	printf(".TH %s 1 %s \"%s\"\n", escapeRoff(doc.appName), escapeRoff(doc.date.Format(time.DateOnly)), escapeRoff(doc.appName))
	printf(".SH Name\n%s\n", escapeRoff(doc.appName))
	println(".SH Provides several commands:")
	println(".PP")
	allCommands := allCommands(doc)
	for i, c := range allCommands {
		if i > 0 {
			println(".br")
		}

		println(escapeRoff(c.fullCommand))
	}

	for _, c := range allCommands {
		printf(".SH %s\n", escapeRoff(strings.ToUpper(c.fullCommand)))
		formatManCommand(printf, println, c)
	}

	return finish()
}

func formatManSingleCommand(out io.Writer, doc doc) error {
	printf, println, finish := printer(out)
	printf(".TH %s 1 %s \"%s\"\n", escapeRoff(doc.appName), escapeRoff(doc.date.Format(time.DateOnly)), escapeRoff(doc.appName))
	printf(".SH Name\n%s\n", escapeRoff(doc.appName))
	formatManCommand(printf, println, doc)
	return finish()
}

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

		hasSubcommands = true
		break
	}

	if hasSubcommands {
		return formatManMultiCommand(out, doc)
	}

	return formatManSingleCommand(out, doc)
}
