// Copyright 2016 Amazon.com, Inc. or its affiliates. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License"). You may not
// use this file except in compliance with the License. A copy of the
// License is located at
//
// http://aws.amazon.com/apache2.0/
//
// or in the "license" file accompanying this file. This file is distributed
// on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
// either express or implied. See the License for the specific language governing
// permissions and limitations under the License.

// Package manager encapsulates everything related to long running plugin manager that starts, stops & configures long running plugins
package manager

import (
	"encoding/json"
	"fmt"
	"time"

	"github.com/aws/amazon-ssm-agent/agent/framework/processor/executer/iohandler"
	"github.com/aws/amazon-ssm-agent/agent/longrunning/plugin"
	"github.com/aws/amazon-ssm-agent/agent/longrunning/plugin/cloudwatch"
	"github.com/aws/amazon-ssm-agent/agent/task"
)

//this file contains all methods that will be called by LRPM invoker (a worker plugin) when prompted by SSM Config or MDS core modules.

//todo: we are passing m.Context to p.Handler.Start & p.Handler.Stop -> we might want have to change StartPlugin and StopPlugin to accept context directly
//todo: honor the cancel flag for both Start and Stop plugin functions

var instance = cloudwatch.Instance()

// StopPlugin stops a given plugin from executing
func (m *Manager) StopPlugin(name string, cancelFlag task.CancelFlag) (err error) {

	//todo: if plugin wasn't even running then stop will have no effect -> for those cases we can return something for a better plugin level status

	lock.Lock()
	defer lock.Unlock()

	log := m.context.Log()
	p, isRegisteredPlugin := m.registeredPlugins[name]
	_, isRunningPlugin := m.runningPlugins[name]

	if isRegisteredPlugin && isRunningPlugin {
		//stop the plugin
		if err = p.Handler.Stop(cancelFlag); err != nil {
			// check if cloud watch exe process has been terminated manually
			if p.Handler.IsRunning() {

				log.Errorf("Failed to stop long running plugin - %s because of %s", name, err)
				return
			}
		}
		//remove the entry from the map of running plugins
		delete(m.runningPlugins, name)
		if err = m.dataStore.Write(m.runningPlugins); err != nil {
			log.Errorf("Failed to update datastore - because of %s", err)
		}

		// Update the config file to "IsEnabled": "false"
		if err = instance.Disable(); err != nil {
			log.Errorf("Failed to update config file - because of %s", err)
		}

		return
	}

	log.Debugf("Can't stop %s - since its not even running", name)
	return nil
}

// StartPlugin starts the given plugin with the given configuration
func (m *Manager) StartPlugin(name, configuration string, orchestrationDir string, cancelFlag task.CancelFlag, out iohandler.IOHandler) (err error) {
	lock.Lock()
	defer lock.Unlock()

	log := m.context.Log()
	log.Infof("Starting long running plugin - %s", name)

	//check if the plugin is registered - this is an extra check since ideally we expect invoker to be aware of registered plugins.
	var p plugin.Plugin
	var isRegisteredPlugin bool
	if p, isRegisteredPlugin = m.registeredPlugins[name]; !isRegisteredPlugin {
		err = fmt.Errorf("unable to run %s since it's not even registered", name)
		return
	}

	//set the config path of the long running plugin
	p.Info.Configuration = configuration
	if err = p.Handler.Start(p.Info.Configuration, orchestrationDir, cancelFlag, out); err != nil {
		log.Errorf("Failed to start long running plugin - %s because of %s", name, err)
		return
	}

	//edit the plugin info
	p.Info.State = plugin.PluginState{
		LastConfigurationModifiedTime: time.Now(),
		IsEnabled:                     true,
	}

	// TODO move persisting out of executing logic
	m.runningPlugins[name] = p.Info
	log.Debugf("Persisting info about %s in datastore", p.Info.Name)

	// TODO separate persist part and actual running part
	if err = m.dataStore.Write(m.runningPlugins); err != nil {
		err = fmt.Errorf("Failed to persist info about %s in datastore because : %s", p.Info.Name, err.Error())
		log.Errorf(err.Error())
	}

	// Update the config file with new configuration
	var engineConfigurationParser cloudwatch.EngineConfigurationParser
	json.Unmarshal([]byte(p.Info.Configuration), &engineConfigurationParser)
	log.Debugf("unmarshal engine configuration parser: %v", engineConfigurationParser)
	log.Debugf("instance is %v", cloudwatch.Instance().GetIsEnabled())
	if err = instance.Enable(engineConfigurationParser.EngineConfiguration); err != nil {
		log.Errorf("Failed to update config file - because of %s", err)
	}

	return
}
