// 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 container

import (
	"fmt"
	"strings"

	"github.com/aws/amazon-ecs-cli/ecs-cli/modules/utils/compose"
	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/service/ecs"
	"github.com/docker/libcompose/project"
)

const (
	containerNameKey  = "Name"
	containerStateKey = "State"
	containerPortsKey = "Ports"
	taskDefinitionKey = "TaskDefinition"
	healthKey         = "Health"
)

// ContainerInfoColumns is the ordered list of info columns for the ps commands
var ContainerInfoColumns = []string{containerNameKey, containerStateKey, containerPortsKey, taskDefinitionKey, healthKey}

// Container is a wrapper around ecsContainer
type Container struct {
	task            *ecs.Task
	EC2IPAddress    string
	networkBindings []*ecs.NetworkBinding

	ecsContainer    *ecs.Container
}

// NewContainer creates a new instance of the container and sets the task id and ecs container to it
func NewContainer(task *ecs.Task, ec2IPAddress string, container *ecs.Container, networkBindings []*ecs.NetworkBinding) Container {
	return Container{
		task:            task,
		EC2IPAddress:    ec2IPAddress,
		networkBindings: networkBindings,
		ecsContainer:    container,
	}
}

// Id returns the ECS container's UUID
func (c *Container) Id() string {
	return utils.GetIdFromArn(aws.StringValue(c.ecsContainer.ContainerArn))
}

// Name returns the task's UUID with the container
// ECS doesn't have a describe container API so providing the task's UUID in the name enables users
// to easily look up this container in the ecs world by invoking DescribeTask
func (c *Container) Name() string {
	taskID := utils.GetIdFromArn(aws.StringValue(c.task.TaskArn))
	return utils.GetFormattedContainerName(taskID, aws.StringValue(c.ecsContainer.Name))
}

// TaskDefinition returns the ECS task definition id which encompasses the container definition, with
// which this container was created
func (c *Container) TaskDefinition() string {
	return utils.GetIdFromArn(aws.StringValue(c.task.TaskDefinitionArn))
}

// State returns the status of the container with the exit code and reason of stopped containers if exists
func (c *Container) State() string {
	ecsContainer := *c.ecsContainer
	status := aws.StringValue(ecsContainer.LastStatus)
	if status != ecs.DesiredStatusStopped {
		return status
	}
	// add the exitCode and reason if present for the stopped containers
	if ecsContainer.ExitCode != nil {
		status = fmt.Sprintf("%s ExitCode: %d", status, aws.Int64Value(ecsContainer.ExitCode))
	}
	if ecsContainer.Reason != nil {
		status = fmt.Sprintf("%s Reason: %s", status, aws.StringValue(ecsContainer.Reason))
	}
	return status
}

// PortString returns a formatted string with container's network bindings
// in a comma separated fashion
func (c *Container) PortString() string {
	result := []string{}
	for _, port := range c.networkBindings {
		protocol := ecs.TransportProtocolTcp
		if port.Protocol != nil {
			protocol = aws.StringValue(port.Protocol)
		}
		ipAddr := aws.StringValue(port.BindIP)
		if c.EC2IPAddress != "" {
			ipAddr = c.EC2IPAddress
		}
		portMapping := fmt.Sprintf("%d->%d/%s",
			aws.Int64Value(port.HostPort),
			aws.Int64Value(port.ContainerPort),
			protocol)
		portString := portMapping
		if ipAddr != "" {
			portString = ipAddr + ":" + portMapping
		}
		result = append(result, portString)
	}
	return strings.Join(result, ", ")
}

// HealthStatus returns the container healthcheck status for the given container
func (c *Container) HealthStatus() string {
	return aws.StringValue(c.ecsContainer.HealthStatus)
}

// ConvertContainersToInfoSet transforms the list of containers into a formatted set of fields
func ConvertContainersToInfoSet(containers []Container) project.InfoSet {
	result := project.InfoSet{}
	for _, cont := range containers {
		info := project.Info{
			// TODO: Add more fields
			containerNameKey:  cont.Name(),
			containerStateKey: cont.State(),
			containerPortsKey: cont.PortString(),
			taskDefinitionKey: cont.TaskDefinition(),
			healthKey:         cont.HealthStatus(),
		}
		result = append(result, info)
	}
	return result
}
