// Copyright 2015-2019 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 network

import (
	"bytes"
	"io/ioutil"
	"testing"

	"github.com/pkg/errors"

	"github.com/docker/docker/api/types"
	"github.com/docker/docker/api/types/container"

	"github.com/golang/mock/gomock"

	"github.com/aws/amazon-ecs-cli/ecs-cli/modules/cli/local/network/mock_network"
)

type mockStarterCalls func(docker *mock_network.MockLocalEndpointsStarter) *mock_network.MockLocalEndpointsStarter

type notFoundErr struct{}

func (e notFoundErr) Error() string {
	return "Dummy not found error returned from Docker"
}

func (e notFoundErr) NotFound() bool {
	return true
}

func TestSetup(t *testing.T) {
	// We don't check the detailed configurations for the Local Network or the Local Container Endpoints.
	// The validation of whether those fields behave as expected should be captured in integration tests.
	// See https://github.com/aws/amazon-ecs-cli/issues/772
	tests := map[string]struct {
		configureCalls mockStarterCalls
	}{
		"new network, new image, and new container": {
			configureCalls: func(docker *mock_network.MockLocalEndpointsStarter) *mock_network.MockLocalEndpointsStarter {
				containerID := "1234"
				pullImgResp := ioutil.NopCloser(bytes.NewReader([]byte("Pulled image successfully")))
				gomock.InOrder(
					// We expect to create the network if it doesn't exist already.
					docker.EXPECT().NetworkInspect(gomock.Any(), EcsLocalNetworkName, gomock.Any()).Return(types.NetworkResource{}, notFoundErr{}),
					docker.EXPECT().NetworkCreate(gomock.Any(), EcsLocalNetworkName, gomock.Any()).Return(types.NetworkCreateResponse{}, nil),

					// Pull the image if it's not available locally
					docker.EXPECT().
						ImageList(gomock.Any(), gomock.Any()).
						Return([]types.ImageSummary{}, nil),
					docker.EXPECT().
						ImagePull(gomock.Any(), localEndpointsImageName, gomock.Any()).Return(pullImgResp, nil),

					// Don't fetch the ID of the Local Container endpoints if it's the first time we are creating it.
					docker.EXPECT().
						ContainerCreate(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), localEndpointsContainerName).
						Return(container.ContainerCreateCreatedBody{
							ID: containerID,
						}, nil),
					docker.EXPECT().ContainerList(gomock.Any(), gomock.Any()).Times(0),
					docker.EXPECT().ContainerStart(gomock.Any(), containerID, gomock.Any()),
				)
				return docker
			},
		},
		"existing network, existing image, and existing container": {
			configureCalls: func(docker *mock_network.MockLocalEndpointsStarter) *mock_network.MockLocalEndpointsStarter {
				networkID := "abcd"
				containerID := "1234"
				gomock.InOrder(
					// Don't create the network if one already exists
					docker.EXPECT().NetworkInspect(gomock.Any(), EcsLocalNetworkName, gomock.Any()).
						Return(types.NetworkResource{ID: networkID}, nil),
					docker.EXPECT().NetworkCreate(gomock.Any(), gomock.Any(), gomock.Any()).Times(0),

					// Don't pull the image if it's downloaded
					docker.EXPECT().
						ImageList(gomock.Any(), gomock.Any()).
						Return([]types.ImageSummary{
							{
								RepoTags: []string{"amazon/amazon-ecs-local-container-endpoints:latest"},
							},
						}, nil),
					docker.EXPECT().ImagePull(gomock.Any(), gomock.Any(), gomock.Any()).Times(0),

					// Retrieve the container ID if it already exists
					docker.EXPECT().
						ContainerCreate(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), localEndpointsContainerName).
						Return(container.ContainerCreateCreatedBody{}, errors.New("Conflict. The container name is already in use.")),
					docker.EXPECT().ContainerList(gomock.Any(), gomock.Any()).Return([]types.Container{
						{
							ID: containerID,
						},
					}, nil),
					docker.EXPECT().ContainerStart(gomock.Any(), containerID, gomock.Any()),
				)
				return docker
			},
		},
	}

	for name, tc := range tests {
		t.Run(name, func(t *testing.T) {
			ctrl := gomock.NewController(t)
			defer ctrl.Finish()

			mockDocker := mock_network.NewMockLocalEndpointsStarter(ctrl)
			mockDocker = tc.configureCalls(mockDocker)

			Setup(mockDocker)
		})
	}
}
