// Package ecs contains functions which read the metadata in Amazon
// Elastic Container Service (ECS) environments.
// This context is made available in logging to provide additional information
// to anyone who may be debugging issues.
package ecs

import (
	"encoding/json"
	"errors"
	"io/ioutil"
	"time"
)

type Metadata struct {
	// The availability zone of the host EC2 container instance.
	AvailabilityZone string
	// The name of the cluster that the container's task is running on.
	ClusterName string `json:"Cluster"`
	// The full Amazon Resource Name (ARN) of the host container instance.
	ContainerInstanceARN string
	// The full ARN of the task that the container belongs to.
	TaskARN      string
	TaskFamily   string `json:"TaskDefinitionFamily"`
	TaskRevision string `json:"TaskDefinitionRevision"`
	// The Docker container ID (and not the ECS container ID) for the container.
	ContainerID string
	// The container name from the task definition.
	ContainerName string
	// The container name that the Docker daemon uses.
	// For example, the name that shows up in the `docker ps` command output.
	DockerContainerName string
	// The EC2 container instance's public IPv4 address.
	HostPublicIP string `json:"HostPublicIPv4Address"`
	// The SHA digest for the Docker image used to start the container.
	ImageID string
	// The image name and tag for the Docker image used to start the container.
	ImageName string
	// Any port mappings associated with the container.
	PortMappings []PortMapping

	Networks []Network

	// The status of the metadata file.
	// When status is "READY", the metadata file is current and complete.
	// If the file is not ready yet (for example, the moment the task started),
	// a truncated version of the file format is available.
	// To avoid a likely race condition where the container has started, but the
	// metadata hs not yet been written, you can parse the metadata file and wait
	// for this parameter to be set to "READY" before depending on the metadata.
	// This is usually available in less than 1 second from when the container starts.
	Status string `json:"MetadataFileStatus"`
}

type PortMapping struct {
	// The exposed container port.
	ContainerPort int
	// The exposed host container instance port.
	HostPort int
	// IP assigned to the container by Docker.
	BindIP string `json:"BindIp"`
	// The network protocol used for the port mapping.
	Protocol string
}

type Network struct {
	// The network mode for the task  to which the container belongs.
	NetworkMode string
	// The IP addresses associated with the container.
	IPv4Addresses []string
}

var errFileUnspecified = errors.New("unspecified container metadata filename")

func parseFile(filename string) (Metadata, error) {
	b, err := ioutil.ReadFile(filename)
	if err != nil {
		return Metadata{}, err
	}

	var m Metadata
	if err := json.Unmarshal(b, &m); err != nil {
		return Metadata{}, err
	}
	return m, nil
}

// ReadMetadata attempts to read the ECS container context. This file is placed by the ECS agent on container creation, and may not be fully written at time of logger initialization
func ReadMetadata(filename string, retryInterval time.Duration, timeout time.Duration) (Metadata, error) {
	if filename == "" {
		return Metadata{}, errFileUnspecified
	}

	startTime := time.Now()
	var m Metadata
	var err error

	for {
		if time.Since(startTime) > timeout {
			m.Status = "TIMEOUT"
			return m, nil
		}

		m, err = parseFile(filename)
		if err != nil {
			m.Status = "ERRORED"
			return m, err
		}

		if m.Status == "READY" {
			return m, nil
		}

		time.Sleep(retryInterval)
	}
}
