package manager

import (
	"fmt"

	"github.com/aws/amazon-ssm-agent/agent/appconfig"
	"github.com/aws/amazon-ssm-agent/agent/context"
	"github.com/aws/amazon-ssm-agent/agent/contracts"
	"github.com/aws/amazon-ssm-agent/agent/framework/processor/executer/iohandler"
	"github.com/aws/amazon-ssm-agent/agent/jsonutil"
	"github.com/aws/amazon-ssm-agent/agent/task"
)

var lrpName = appconfig.PluginNameCloudWatch
var getInstance = GetInstance

func CreateResult(msg string, status contracts.ResultStatus, res *contracts.PluginResult) {
	res.Output = msg

	if status == contracts.ResultStatusFailed {
		res.StandardError = msg
		res.Code = 1
	} else {
		res.StandardOutput = msg
		res.Code = 0
	}

	res.Status = status
	return
}

func Invoke(context context.T, pluginID string, res *contracts.PluginResult, orchestrationDir string) {
	var lrpm T
	var err error
	var startType = res.StandardOutput
	var property string
	jsonutil.Remarshal(res.Output, &property)
	res.StandardOutput = ""
	res.Output = ""
	log := context.Log()
	lrpm, err = getInstance()
	var pluginsMap = lrpm.GetRegisteredPlugins()
	if _, ok := pluginsMap[lrpName]; !ok {
		log.Errorf("Given plugin - %s is not registered", lrpName)
		CreateResult(fmt.Sprintf("Plugin %s is not registered by agent", lrpName),
			contracts.ResultStatusFailed, res)

		return
	}
	cancelFlag := task.NewChanneledCancelFlag()
	//NOTE: All long running plugins have json node similar to aws:cloudWatch as mentioned in SSM document - AWS-ConfigureCloudWatch

	//check if plugin is enabled or not - which would be stored in settings
	switch startType {
	case "Enabled":
		enablePlugin(context, orchestrationDir, pluginID, lrpm, cancelFlag, property, res)

	case "Disabled":
		log.Infof("Disabling %s", lrpName)
		if err = lrpm.StopPlugin(lrpName, cancelFlag); err != nil {
			log.Errorf("Unable to stop the plugin - %s: %s", pluginID, err.Error())
			CreateResult(fmt.Sprintf("Encountered error while stopping the plugin: %s", err.Error()),
				contracts.ResultStatusFailed, res)

		} else {
			CreateResult(fmt.Sprintf("Disabled the plugin - %s successfully", lrpName),
				contracts.ResultStatusSuccess, res)
			res.Status = contracts.ResultStatusSuccess
		}

	default:
		log.Errorf("Allowed Values of StartType: Enabled | Disabled but provided value is: %s", startType)
		CreateResult("Allowed Values of StartType: Enabled | Disabled",
			contracts.ResultStatusFailed, res)
	}

	return
}

func enablePlugin(context context.T, orchestrationDirectory string, pluginID string, lrpm T, cancelFlag task.CancelFlag, property string, res *contracts.PluginResult) {
	log := context.Log()
	log.Infof("Enabling %s", lrpName)

	//loading properties as string since aws:cloudWatch uses properties as string. Properties has new configuration for cloudwatch plugin.
	//For more details refer to AWS-ConfigureCloudWatch
	// TODO cannot check if string is a valid json for cloudwatch
	//stop the plugin before reconfiguring it
	log.Debugf("Stopping %s - before applying new configuration", lrpName)
	if err := lrpm.StopPlugin(lrpName, cancelFlag); err != nil {
		log.Errorf("Unable to stop the plugin - %s: %s", lrpName, err.Error())
	}

	ioConfig := contracts.IOConfiguration{
		OrchestrationDirectory: orchestrationDirectory,
		OutputS3BucketName:     res.OutputS3BucketName,
		OutputS3KeyPrefix:      res.OutputS3KeyPrefix,
	}
	out := iohandler.NewDefaultIOHandler(context, ioConfig)
	defer out.Close()
	out.Init(appconfig.PluginNameCloudWatch)
	//start the plugin with the new configuration
	if err := lrpm.StartPlugin(lrpName, property, orchestrationDirectory, cancelFlag, out); err != nil {
		log.Errorf("Unable to start the plugin - %s: %s", lrpName, err.Error())
		CreateResult(fmt.Sprintf("Encountered error while starting the plugin: %s", err.Error()),
			contracts.ResultStatusFailed, res)
	} else {

		if len(out.GetStderr()) > 0 {
			log.Errorf("Unable to start the plugin - %s: %s", lrpName, out.GetStderr())

			// Stop the plugin if configuration failed.
			if err := lrpm.StopPlugin(lrpName, cancelFlag); err != nil {
				log.Errorf("Unable to start the plugin - %s: %s", lrpName, err.Error())
			}

			CreateResult(fmt.Sprintf("Encountered error while starting the plugin: %s", out.GetStderr()),
				contracts.ResultStatusFailed, res)

		} else {
			log.Info("Start Cloud Watch successfully.")
			CreateResult("success", contracts.ResultStatusSuccess, res)
		}
	}
	out.Close()
	return
}
