// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1

package configvagrant

import (
	"github.com/hashicorp/go-argmapper"
	"github.com/hashicorp/go-hclog"
	sdk "github.com/hashicorp/vagrant-plugin-sdk"
	"github.com/hashicorp/vagrant-plugin-sdk/component"
	"github.com/hashicorp/vagrant-plugin-sdk/core"
	"github.com/hashicorp/vagrant-plugin-sdk/terminal"
)

var CommandOptions = []sdk.Option{
	sdk.WithComponents(
		&Config{},
	),
	sdk.WithComponent(&Command{}, &component.CommandOptions{Primary: false}),
	sdk.WithName("configvagrant"),
}

type Vagrant struct {
	Sensitive     []string `hcl:"sensitive,optional" json:",omitempty"`
	Host          *string  `hcl:"host,optional" json:"host,omitempty"`
	FinalizedInfo *string  `hcl:"finalized_info,optional" json:"finalized_info,omitempty"`
	Plugins       []Plugin `hcl:"plugins,block" json:"plugins,omitempty"`
}

type Plugin struct {
	Name string `hcl:"name,label"`

	EntryPoint *string  `hcl:"entry_point,optional" json:"entry_point,omitempty"`
	Sources    []string `hcl:"sources,optional" json:"source,omitempty"`
	Version    *string  `hcl:"version,optional" json:"version,omitempty"`
}

type Config struct{}

func (c *Config) Register() (*component.ConfigRegistration, error) {
	return &component.ConfigRegistration{
		Identifier: "vagrants",
	}, nil
}

func (c *Config) InitFunc() any {
	return c.Init
}

func (c *Config) Init(in *component.ConfigData) (*component.ConfigData, error) {
	return in, nil
}

func (c *Config) StructFunc() interface{} {
	return c.Struct
}

func (c *Config) Struct() *Vagrant {
	return &Vagrant{}
}

func (c *Config) MergeFunc() interface{} {
	return c.Merge
}

func (c *Config) Merge(
	input struct {
		argmapper.Struct
		Base    *Vagrant
		Overlay *Vagrant
		Log     hclog.Logger
	},
) (*Vagrant, error) {
	log := input.Log
	log.Info("merging config values in vagrants namespace",
		"base", input.Base, "overlay", input.Overlay)

	result := input.Base
	if input.Overlay.Host != nil {
		result.Host = input.Overlay.Host
	}

	for _, s := range input.Overlay.Sensitive {
		result.Sensitive = append(result.Sensitive, s)
	}

	log.Info("merged config value for vagrants namespace", "config", result)

	return result, nil
}

func (c *Config) FinalizeFunc() interface{} {
	return c.Finalize
}

func (c *Config) Finalize(l hclog.Logger, conf *Vagrant) (*Vagrant, error) {
	l.Warn("checking current content", "host", conf.Host)
	if conf.Host != nil {
		l.Warn("checking current value", "host", *conf.Host)
	}
	info := "go plugin finalization test content"
	conf.FinalizedInfo = &info
	return conf, nil
}

type Command struct{}

func (c *Command) ExecuteFunc(_ []string) interface{} {
	return c.Execute
}

func (c *Command) Execute(ui terminal.UI, p core.Project) int32 {
	ui.Output("Checking for our defined config...")
	v, err := p.Vagrantfile()
	if err != nil {
		ui.Output("Failed to get Vagrantfile instance: %s", err)
		return 1
	}
	ui.Output("Our vagrantfile value is: %#v", v)
	conf, err := v.GetConfig("vagrants")
	if err != nil {
		ui.Output("failed to get configuration for 'vagrants' namespace: %q", err)
		return 1
	}

	ui.Output("We got something here!")
	ui.Output("Config defined host: %s", conf.Data["host"])

	if _, ok := conf.Data["finalized"]; !ok {
		ui.Output("ERROR: finalized data expected and not found in config!")
	}

	if _, ok := conf.Data["merged"]; !ok {
		ui.Output("ERROR: merged data expected and not found in config!")
		return 1
	}
	return 0
}

func (c *Command) CommandInfoFunc() interface{} {
	return c.CommandInfo
}

func (c *Command) CommandInfo() *component.CommandInfo {
	return &component.CommandInfo{
		Name: "configvagrant",
		Help: "I display config",
	}
}
