// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0

package command

import (
	"fmt"
	"io"
	"os"
	"strings"

	"github.com/hashicorp/vault/api"
	"github.com/mitchellh/cli"
	"github.com/posener/complete"
)

var (
	_ cli.Command             = (*AuditEnableCommand)(nil)
	_ cli.CommandAutocomplete = (*AuditEnableCommand)(nil)
)

type AuditEnableCommand struct {
	*BaseCommand

	flagDescription string
	flagPath        string
	flagLocal       bool

	testStdin io.Reader // For tests
}

func (c *AuditEnableCommand) Synopsis() string {
	return "Enables an audit device"
}

func (c *AuditEnableCommand) Help() string {
	helpText := `
Usage: vault audit enable [options] TYPE [CONFIG K=V...]

  Enables an audit device at a given path.

  This command enables an audit device of TYPE. Additional options for
  configuring the audit device can be specified after the type in the same
  format as the "vault write" command in key/value pairs.

  For example, to configure the file audit device to write audit logs at the
  path "/var/log/audit.log":

      $ vault audit enable file file_path=/var/log/audit.log

` + c.Flags().Help()

	return strings.TrimSpace(helpText)
}

func (c *AuditEnableCommand) Flags() *FlagSets {
	set := c.flagSet(FlagSetHTTP)

	f := set.NewFlagSet("Command Options")

	f.StringVar(&StringVar{
		Name:       "description",
		Target:     &c.flagDescription,
		Default:    "",
		EnvVar:     "",
		Completion: complete.PredictAnything,
		Usage: "Human-friendly description for the purpose of this audit " +
			"device.",
	})

	f.StringVar(&StringVar{
		Name:       "path",
		Target:     &c.flagPath,
		Default:    "", // The default is complex, so we have to manually document
		EnvVar:     "",
		Completion: complete.PredictAnything,
		Usage: "Place where the audit device will be accessible. This must be " +
			"unique across all audit devices. This defaults to the \"type\" of the " +
			"audit device.",
	})

	f.BoolVar(&BoolVar{
		Name:    "local",
		Target:  &c.flagLocal,
		Default: false,
		EnvVar:  "",
		Usage: "Mark the audit device as a local-only device. Local devices " +
			"are not replicated or removed by replication.",
	})

	return set
}

func (c *AuditEnableCommand) AutocompleteArgs() complete.Predictor {
	return complete.PredictSet(
		"file",
		"syslog",
		"socket",
	)
}

func (c *AuditEnableCommand) AutocompleteFlags() complete.Flags {
	return c.Flags().Completions()
}

func (c *AuditEnableCommand) Run(args []string) int {
	f := c.Flags()

	if err := f.Parse(args); err != nil {
		c.UI.Error(err.Error())
		return 1
	}

	args = f.Args()
	if len(args) < 1 {
		c.UI.Error("Error enabling audit device: audit type missing. Valid types include 'file', 'socket' and 'syslog'.")
		return 1
	}

	// Grab the type
	auditType := strings.TrimSpace(args[0])

	auditPath := c.flagPath
	if auditPath == "" {
		auditPath = auditType
	}
	auditPath = ensureTrailingSlash(auditPath)

	// Pull our fake stdin if needed
	stdin := (io.Reader)(os.Stdin)
	if c.testStdin != nil {
		stdin = c.testStdin
	}

	options, err := parseArgsDataString(stdin, args[1:])
	if err != nil {
		c.UI.Error(fmt.Sprintf("Failed to parse K=V data: %s", err))
		return 1
	}

	client, err := c.Client()
	if err != nil {
		c.UI.Error(err.Error())
		return 2
	}

	if err := client.Sys().EnableAuditWithOptions(auditPath, &api.EnableAuditOptions{
		Type:        auditType,
		Description: c.flagDescription,
		Options:     options,
		Local:       c.flagLocal,
	}); err != nil {
		c.UI.Error(fmt.Sprintf("Error enabling audit device: %s", err))
		return 2
	}

	c.UI.Output(fmt.Sprintf("Success! Enabled the %s audit device at: %s", auditType, auditPath))
	return 0
}
