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

package command

import (
	"fmt"
	"sort"
	"strings"

	"github.com/posener/complete"
)

type NodeMetaReadCommand struct {
	Meta
}

func (c *NodeMetaReadCommand) Help() string {
	helpText := `
Usage: nomad node meta read [-json] [-node-id ...]

  Read a node's metadata. This command only works on client agents. The node
  status command can be used to retrieve node metadata from any agent.

  Changes via the "node meta apply" subcommand are batched and may take up to
  10 seconds to propagate to the servers and affect scheduling. This command
  will always return the most recent node metadata while the "node status"
  command can be used to view the metadata that is currently being used for
  scheduling.

General Options:

  ` + generalOptionsUsage(usageOptsDefault|usageOptsNoNamespace) + `

Node Meta Options:

  -node-id
    Reads metadata from the specified node. If not specified the node receiving
    the request will be used by default.

  -json
    Output the node metadata in its JSON format.

  -t
    Format and display node metadata using a Go template.

    Example:
      $ nomad node meta read -node-id 3b58b0a6
`
	return strings.TrimSpace(helpText)
}

func (c *NodeMetaReadCommand) Synopsis() string {
	return "Read node metadata"
}

func (c *NodeMetaReadCommand) Name() string { return "node meta read" }

func (c *NodeMetaReadCommand) Run(args []string) int {
	var nodeID, tmpl string
	var json bool

	flags := c.Meta.FlagSet(c.Name(), FlagSetClient)
	flags.Usage = func() { c.Ui.Output(c.Help()) }
	flags.StringVar(&nodeID, "node-id", "", "")
	flags.StringVar(&tmpl, "t", "", "")
	flags.BoolVar(&json, "json", false, "")

	if err := flags.Parse(args); err != nil {
		return 1
	}

	// Get the HTTP client
	client, err := c.Meta.Client()
	if err != nil {
		c.Ui.Error(fmt.Sprintf("Error initializing client: %s", err))
		return 1
	}

	// Lookup nodeID
	if nodeID != "" {
		nodeID, err = lookupNodeID(client.Nodes(), nodeID)
		if err != nil {
			c.Ui.Error(err.Error())
			return 1
		}
	}

	meta, err := client.Nodes().Meta().Read(nodeID, nil)
	if err != nil {
		c.Ui.Error(fmt.Sprintf("Error reading dynamic node metadata: %s", err))
		return 1
	}

	if json || len(tmpl) > 0 {
		out, err := Format(json, tmpl, meta)
		if err != nil {
			c.Ui.Error(err.Error())
			return 1
		}

		c.Ui.Output(out)
		return 0
	}

	c.Ui.Output(c.Colorize().Color("[bold]All Meta[reset]"))
	c.Ui.Output(formatNodeMeta(meta.Meta))

	// Print dynamic meta
	c.Ui.Output(c.Colorize().Color("\n[bold]Dynamic Meta[reset]"))
	keys := make([]string, 0, len(meta.Dynamic))
	for k := range meta.Dynamic {
		keys = append(keys, k)
	}
	sort.Strings(keys)

	var rows []string
	for _, k := range keys {
		v := "<unset>"
		if meta.Dynamic[k] != nil {
			v = *meta.Dynamic[k]
		}
		rows = append(rows, fmt.Sprintf("%s|%s", k, v))
	}
	c.Ui.Output(formatKV(rows))

	// Print static meta
	c.Ui.Output(c.Colorize().Color("\n[bold]Static Meta[reset]"))
	c.Ui.Output(formatNodeMeta(meta.Static))

	return 0
}

func (c *NodeMetaReadCommand) AutocompleteFlags() complete.Flags {
	return mergeAutocompleteFlags(c.Meta.AutocompleteFlags(FlagSetClient),
		complete.Flags{
			"-node-id": complete.PredictAnything,
			"-json":    complete.PredictNothing,
			"-t":       complete.PredictAnything,
		})
}

func (c *NodeMetaReadCommand) AutocompleteArgs() complete.Predictor {
	return complete.PredictNothing
}
