package controller

import (
	"strconv"
	"testing"

	kapi "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
	kerrors "github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors"

	api "github.com/openshift/origin/pkg/api/latest"
	deployapi "github.com/openshift/origin/pkg/deploy/api"
	deployutil "github.com/openshift/origin/pkg/deploy/util"
)

func TestHandleNewDeploymentConfig(t *testing.T) {
	controller := &DeploymentConfigController{
		Codec: api.Codec,
		DeploymentInterface: &testDeploymentInterface{
			GetDeploymentFunc: func(namespace, name string) (*kapi.ReplicationController, error) {
				t.Fatalf("unexpected call with name %s", name)
				return nil, nil
			},
			CreateDeploymentFunc: func(namespace string, deployment *kapi.ReplicationController) (*kapi.ReplicationController, error) {
				t.Fatalf("unexpected call with deployment %v", deployment)
				return nil, nil
			},
		},
		NextDeploymentConfig: func() *deployapi.DeploymentConfig {
			deploymentConfig := manualDeploymentConfig()
			deploymentConfig.LatestVersion = 0
			return deploymentConfig
		},
	}

	controller.HandleDeploymentConfig()
}

func TestHandleInitialDeployment(t *testing.T) {
	deploymentConfig := manualDeploymentConfig()
	deploymentConfig.LatestVersion = 1

	var deployed *kapi.ReplicationController

	controller := &DeploymentConfigController{
		Codec: api.Codec,
		DeploymentInterface: &testDeploymentInterface{
			GetDeploymentFunc: func(namespace, name string) (*kapi.ReplicationController, error) {
				return nil, kerrors.NewNotFound("replicationController", name)
			},
			CreateDeploymentFunc: func(namespace string, deployment *kapi.ReplicationController) (*kapi.ReplicationController, error) {
				deployed = deployment
				return deployment, nil
			},
		},
		NextDeploymentConfig: func() *deployapi.DeploymentConfig {
			return deploymentConfig
		},
	}

	controller.HandleDeploymentConfig()

	if deployed == nil {
		t.Fatalf("expected a deployment")
	}

	expectedAnnotations := map[string]string{
		deployapi.DeploymentConfigAnnotation:  deploymentConfig.Name,
		deployapi.DeploymentStatusAnnotation:  string(deployapi.DeploymentStatusNew),
		deployapi.DeploymentVersionAnnotation: strconv.Itoa(deploymentConfig.LatestVersion),
	}

	for key, expected := range expectedAnnotations {
		if actual := deployed.Annotations[key]; actual != expected {
			t.Fatalf("expected deployment annotation %s=%s, got %s", key, expected, actual)
		}
	}

	// TODO: add stronger assertion on the encoded value once the controller methods are free
	// of side effects on the deploymentConfig
	if len(deployed.Annotations[deployapi.DeploymentEncodedConfigAnnotation]) == 0 {
		t.Fatalf("expected deployment with DeploymentEncodedConfigAnnotation annotation")
	}
}

func TestHandleConfigChangeNoPodTemplateDiff(t *testing.T) {
	deploymentConfig := manualDeploymentConfig()
	deploymentConfig.LatestVersion = 0

	controller := &DeploymentConfigController{
		Codec: api.Codec,
		DeploymentInterface: &testDeploymentInterface{
			GetDeploymentFunc: func(namespace, name string) (*kapi.ReplicationController, error) {
				return matchingDeployment(deploymentConfig), nil
			},
			CreateDeploymentFunc: func(namespace string, deployment *kapi.ReplicationController) (*kapi.ReplicationController, error) {
				t.Fatalf("unexpected call to to create deployment: %v", deployment)
				return nil, nil
			},
		},
		NextDeploymentConfig: func() *deployapi.DeploymentConfig {
			return deploymentConfig
		},
	}

	controller.HandleDeploymentConfig()
}

func TestHandleConfigChangeWithPodTemplateDiff(t *testing.T) {
	deploymentConfig := manualDeploymentConfig()
	deploymentConfig.LatestVersion = 2
	deploymentConfig.Template.ControllerTemplate.Template.Labels["foo"] = "bar"

	var deployed *kapi.ReplicationController

	controller := &DeploymentConfigController{
		Codec: api.Codec,
		DeploymentInterface: &testDeploymentInterface{
			GetDeploymentFunc: func(namespace, name string) (*kapi.ReplicationController, error) {
				return nil, kerrors.NewNotFound("deployment", name)
			},
			CreateDeploymentFunc: func(namespace string, deployment *kapi.ReplicationController) (*kapi.ReplicationController, error) {
				deployed = deployment
				return deployment, nil
			},
		},
		NextDeploymentConfig: func() *deployapi.DeploymentConfig {
			return deploymentConfig
		},
	}

	controller.HandleDeploymentConfig()

	if deployed == nil {
		t.Fatalf("expected a deployment")
	}

	if e, a := deploymentConfig.Name, deployed.Annotations[deployapi.DeploymentConfigAnnotation]; e != a {
		t.Fatalf("expected deployment annotated with deploymentConfig %s, got %s", e, a)
	}
}

type testDeploymentInterface struct {
	GetDeploymentFunc    func(namespace, name string) (*kapi.ReplicationController, error)
	CreateDeploymentFunc func(namespace string, deployment *kapi.ReplicationController) (*kapi.ReplicationController, error)
}

func (i *testDeploymentInterface) GetDeployment(namespace, name string) (*kapi.ReplicationController, error) {
	return i.GetDeploymentFunc(namespace, name)
}

func (i *testDeploymentInterface) CreateDeployment(namespace string, deployment *kapi.ReplicationController) (*kapi.ReplicationController, error) {
	return i.CreateDeploymentFunc(namespace, deployment)
}

func manualDeploymentConfig() *deployapi.DeploymentConfig {
	return &deployapi.DeploymentConfig{
		ObjectMeta: kapi.ObjectMeta{Name: "manual-deploy-config"},
		Triggers: []deployapi.DeploymentTriggerPolicy{
			{
				Type: deployapi.DeploymentTriggerManual,
			},
		},
		Template: deployapi.DeploymentTemplate{
			Strategy: deployapi.DeploymentStrategy{
				Type: deployapi.DeploymentStrategyTypeRecreate,
			},
			ControllerTemplate: kapi.ReplicationControllerSpec{
				Replicas: 1,
				Selector: map[string]string{
					"name": "test-pod",
				},
				Template: &kapi.PodTemplateSpec{
					ObjectMeta: kapi.ObjectMeta{
						Labels: map[string]string{
							"name": "test-pod",
						},
					},
					Spec: kapi.PodSpec{
						Containers: []kapi.Container{
							{
								Name:  "container-1",
								Image: "registry:8080/openshift/test-image:ref-1",
							},
						},
					},
				},
			},
		},
	}
}

func matchingDeployment(config *deployapi.DeploymentConfig) *kapi.ReplicationController {
	encodedConfig, _ := deployutil.EncodeDeploymentConfig(config, api.Codec)
	return &kapi.ReplicationController{
		ObjectMeta: kapi.ObjectMeta{
			Name: deployutil.LatestDeploymentIDForConfig(config),
			Annotations: map[string]string{
				deployapi.DeploymentConfigAnnotation:        config.Name,
				deployapi.DeploymentEncodedConfigAnnotation: encodedConfig,
			},
			Labels: config.Labels,
		},
		Spec: kapi.ReplicationControllerSpec{
			Replicas: 1,
			Selector: map[string]string{
				"name": "test-pod",
			},
			Template: &kapi.PodTemplateSpec{
				ObjectMeta: kapi.ObjectMeta{
					Labels: map[string]string{
						"name": "test-pod",
					},
				},
				Spec: kapi.PodSpec{
					Containers: []kapi.Container{
						{
							Name:  "container-1",
							Image: "registry:8080/openshift/test-image:ref-1",
						},
					},
				},
			},
		},
	}
}