// Copyright 2020-2023 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

package cmd

import (
	"bufio"
	"bytes"
	"fmt"
	"io"
	"strings"

	"github.com/spf13/cobra"
	"github.com/spf13/cobra/doc"
)

//nolint:gochecknoinits
func init() {
	rootCmd.AddCommand(generateMarkdownHelpCommand())
}

func generateMarkdownHelpCommand() *cobra.Command {
	return &cobra.Command{
		Args:         cobra.NoArgs,
		Use:          "generate-markdown-help",
		Short:        "Generate markdown help for the current set of non-hidden CLI commands",
		SilenceUsage: true, // do not print usage message when commands fail
		Hidden:       true,
		RunE:         runGenerateMarkdownHelp,
	}
}

func runGenerateMarkdownHelp(cmd *cobra.Command, _ []string) error {
	var generated bytes.Buffer
	if err := generate(&generated); err != nil {
		return err
	}
	if err := write(cmd.OutOrStdout(), &generated, "###### Auto generated by spf13/cobra"); err != nil {
		return err
	}
	return nil
}

func generate(w io.Writer) error {
	if err := generateHeader(w); err != nil {
		return err
	}
	if err := generateCommand(w, rootCmd); err != nil {
		return err
	}
	return nil
}

func generateHeader(w io.Writer) error {
	_, err := fmt.Fprintf(w, `---
title: Command-Line Options Reference
description: Reference for the `+"`pinniped`"+` command-line tool
cascade:
  layout: docs
menu:
  docs:
    name: Command-Line Options
    weight: 30
    parent: reference
---

`)
	return err
}

func generateCommand(w io.Writer, command *cobra.Command) error {
	for _, command := range command.Commands() {
		// if this node is hidden, don't traverse it or its descendents
		if command.Hidden {
			continue
		}

		// generate children
		if err := generateCommand(w, command); err != nil {
			return err
		}

		// generate self, but only if we are a command that people would run to do something interesting
		if command.Run != nil || command.RunE != nil {
			if err := doc.GenMarkdownCustom(command, w, func(_ string) string { return "" }); err != nil {
				return err
			}
		}
	}

	return nil
}

func write(w io.Writer, r io.Reader, unwantedPrefixes ...string) error {
	s := bufio.NewScanner(r)
	for s.Scan() {
		line := s.Text()
		if !containsPrefix(line, unwantedPrefixes) {
			if _, err := fmt.Fprintln(w, line); err != nil {
				return err
			}
		}
	}
	return s.Err()
}

func containsPrefix(s string, prefixes []string) bool {
	for _, prefix := range prefixes {
		if strings.HasPrefix(s, prefix) {
			return true
		}
	}
	return false
}
