// Copyright 2015-2017 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 compose

import (
	"os"
	"strconv"

	"github.com/aws/amazon-ecs-cli/ecs-cli/modules/cli/compose/container"
	composeFactory "github.com/aws/amazon-ecs-cli/ecs-cli/modules/cli/compose/factory"
	ecscompose "github.com/aws/amazon-ecs-cli/ecs-cli/modules/cli/compose/project"
	"github.com/aws/amazon-ecs-cli/ecs-cli/modules/commands/flags"
	"github.com/flynn/go-shlex"
	log "github.com/sirupsen/logrus"
	"github.com/urfave/cli"
)

// COMPOSE
// displayTitle flag is used to print the title for the fields
const displayTitle = true

// ProjectAction is an adapter to allow the use of ordinary functions as libcompose actions.
// Any function that has the appropriate signature can be register as an action on a urfave/cli command.
//
// cli.Command{
//		Name:   "ps",
//		Usage:  "List containers",
//		Action: app.WithProject(factory, app.ProjectPs),
//	}
type ProjectAction func(project ecscompose.Project, c *cli.Context)

// WithProject is an helper function to create a cli.Command action with a ProjectFactory.
func WithProject(factory composeFactory.ProjectFactory, action ProjectAction, isService bool) func(context *cli.Context) {
	return func(context *cli.Context) {
		// TODO, instead of passing isService around, we can determine
		// the command name cliContext.Parent().Command.Name = service and set appropriate context
		// However, parentContext is not being set appropriately by cli. Investigate.
		p, err := factory.Create(context, isService)
		if err != nil {
			log.WithFields(log.Fields{
				"error": err,
			}).Fatal("Unable to create and read ECS Compose Project")
		}
		action(p, context)
	}
}

// ProjectCreate creates the task definition required for the containers but does not start them.
func ProjectCreate(p ecscompose.Project, c *cli.Context) {
	err := p.Create()
	if err != nil {
		log.Fatal(err)
	}
}

// ProjectStart starts containers.
func ProjectStart(p ecscompose.Project, c *cli.Context) {
	err := p.Start()
	if err != nil {
		log.Fatal(err)
	}
}

// ProjectUp brings all containers up.
func ProjectUp(p ecscompose.Project, c *cli.Context) {
	err := p.Up()
	if err != nil {
		log.Fatal(err)
	}
}

// ProjectPs lists the containers.
func ProjectPs(p ecscompose.Project, c *cli.Context) {
	allInfo, err := p.Info(c.String(flags.DesiredTaskStatus))
	if err != nil {
		log.Fatal(err)
	}
	os.Stdout.WriteString(allInfo.String(container.ContainerInfoColumns, displayTitle))
}

// ProjectRun starts containers and executes one-time command against the container
// TODO These only account for command overrides within a ContainerOverride: https://docs.aws.amazon.com/AmazonECS/latest/APIReference/API_ContainerOverride.html
func ProjectRun(p ecscompose.Project, c *cli.Context) {
	args := c.Args()
	if len(args)%2 != 0 {
		log.Fatal("Please pass arguments in the form: CONTAINER \"COMMAND ...\" [CONTAINER \"COMMAND...\"] ...")
	}
	commandOverrides := make(map[string][]string)
	for i := 0; i < len(args); i += 2 {
		parts, err := shlex.Split(args[i+1])
		if err != nil {
			log.WithFields(log.Fields{
				"container-name": args[i],
				"error":          err,
			}).Fatal("Unable to parse run commands")
		}
		commandOverrides[args[i]] = parts
	}
	err := p.Run(commandOverrides)
	if err != nil {
		log.Fatal(err)
	}
}

// ProjectScale scales containers.
func ProjectScale(p ecscompose.Project, c *cli.Context) {
	if len(c.Args()) != 1 {
		log.Fatal("Please pass arguments in the form: ecs-cli compose scale COUNT")
	}
	count, err := strconv.Atoi(c.Args().First())
	if err != nil {
		log.Fatal("Please pass an integer value for argument COUNT")
	}
	err = p.Scale(count)
	if err != nil {
		log.Fatal(err)
	}
}

// ProjectStop brings all containers down.
func ProjectStop(p ecscompose.Project, c *cli.Context) {
	err := p.Stop()
	if err != nil {
		log.Fatal(err)
	}
}

// ProjectDown brings all containers down.
func ProjectDown(p ecscompose.Project, c *cli.Context) {
	err := p.Down()
	if err != nil {
		log.Fatal(err)
	}
}
