package controller

import (
	"testing"

	"github.com/stefanprodan/flagger/pkg/apis/flagger/v1alpha3"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

func TestCanaryDeployer_Sync(t *testing.T) {
	mocks := SetupMocks()
	err := mocks.deployer.Sync(mocks.canary)
	if err != nil {
		t.Fatal(err.Error())
	}

	depPrimary, err := mocks.kubeClient.AppsV1().Deployments("default").Get("podinfo-primary", metav1.GetOptions{})
	if err != nil {
		t.Fatal(err.Error())
	}

	dep := newTestDeployment()
	configMap := NewTestConfigMap()
	secret := NewTestSecret()

	primaryImage := depPrimary.Spec.Template.Spec.Containers[0].Image
	sourceImage := dep.Spec.Template.Spec.Containers[0].Image
	if primaryImage != sourceImage {
		t.Errorf("Got image %s wanted %s", primaryImage, sourceImage)
	}

	hpaPrimary, err := mocks.kubeClient.AutoscalingV2beta1().HorizontalPodAutoscalers("default").Get("podinfo-primary", metav1.GetOptions{})
	if err != nil {
		t.Fatal(err.Error())
	}

	if hpaPrimary.Spec.ScaleTargetRef.Name != depPrimary.Name {
		t.Errorf("Got HPA target %s wanted %s", hpaPrimary.Spec.ScaleTargetRef.Name, depPrimary.Name)
	}

	configPrimary, err := mocks.kubeClient.CoreV1().ConfigMaps("default").Get("podinfo-config-env-primary", metav1.GetOptions{})
	if err != nil {
		t.Fatal(err.Error())
	}

	if configPrimary.Data["color"] != configMap.Data["color"] {
		t.Errorf("Got ConfigMap color %s wanted %s", configPrimary.Data["color"], configMap.Data["color"])
	}

	configPrimaryEnv, err := mocks.kubeClient.CoreV1().ConfigMaps("default").Get("podinfo-config-all-env-primary", metav1.GetOptions{})
	if err != nil {
		t.Fatal(err.Error())
	}

	if configPrimaryEnv.Data["color"] != configMap.Data["color"] {
		t.Errorf("Got ConfigMap %s wanted %s", configPrimaryEnv.Data["a"], configMap.Data["color"])
	}

	configPrimaryVol, err := mocks.kubeClient.CoreV1().ConfigMaps("default").Get("podinfo-config-vol-primary", metav1.GetOptions{})
	if err != nil {
		t.Fatal(err.Error())
	}

	if configPrimaryVol.Data["color"] != configMap.Data["color"] {
		t.Errorf("Got ConfigMap color %s wanted %s", configPrimary.Data["color"], configMap.Data["color"])
	}

	secretPrimary, err := mocks.kubeClient.CoreV1().Secrets("default").Get("podinfo-secret-env-primary", metav1.GetOptions{})
	if err != nil {
		t.Fatal(err.Error())
	}

	if string(secretPrimary.Data["apiKey"]) != string(secret.Data["apiKey"]) {
		t.Errorf("Got primary secret %s wanted %s", secretPrimary.Data["apiKey"], secret.Data["apiKey"])
	}

	secretPrimaryEnv, err := mocks.kubeClient.CoreV1().Secrets("default").Get("podinfo-secret-all-env-primary", metav1.GetOptions{})
	if err != nil {
		t.Fatal(err.Error())
	}

	if string(secretPrimaryEnv.Data["apiKey"]) != string(secret.Data["apiKey"]) {
		t.Errorf("Got primary secret %s wanted %s", secretPrimary.Data["apiKey"], secret.Data["apiKey"])
	}

	secretPrimaryVol, err := mocks.kubeClient.CoreV1().Secrets("default").Get("podinfo-secret-vol-primary", metav1.GetOptions{})
	if err != nil {
		t.Fatal(err.Error())
	}

	if string(secretPrimaryVol.Data["apiKey"]) != string(secret.Data["apiKey"]) {
		t.Errorf("Got primary secret %s wanted %s", secretPrimary.Data["apiKey"], secret.Data["apiKey"])
	}
}

func TestCanaryDeployer_IsNewSpec(t *testing.T) {
	mocks := SetupMocks()
	err := mocks.deployer.Sync(mocks.canary)
	if err != nil {
		t.Fatal(err.Error())
	}

	dep2 := newTestDeploymentV2()
	_, err = mocks.kubeClient.AppsV1().Deployments("default").Update(dep2)
	if err != nil {
		t.Fatal(err.Error())
	}

	isNew, err := mocks.deployer.IsNewSpec(mocks.canary)
	if err != nil {
		t.Fatal(err.Error())
	}

	if !isNew {
		t.Errorf("Got %v wanted %v", isNew, true)
	}
}

func TestCanaryDeployer_Promote(t *testing.T) {
	mocks := SetupMocks()
	err := mocks.deployer.Sync(mocks.canary)
	if err != nil {
		t.Fatal(err.Error())
	}

	dep2 := newTestDeploymentV2()
	_, err = mocks.kubeClient.AppsV1().Deployments("default").Update(dep2)
	if err != nil {
		t.Fatal(err.Error())
	}

	config2 := NewTestConfigMapV2()
	_, err = mocks.kubeClient.CoreV1().ConfigMaps("default").Update(config2)
	if err != nil {
		t.Fatal(err.Error())
	}

	err = mocks.deployer.Promote(mocks.canary)
	if err != nil {
		t.Fatal(err.Error())
	}

	depPrimary, err := mocks.kubeClient.AppsV1().Deployments("default").Get("podinfo-primary", metav1.GetOptions{})
	if err != nil {
		t.Fatal(err.Error())
	}

	primaryImage := depPrimary.Spec.Template.Spec.Containers[0].Image
	sourceImage := dep2.Spec.Template.Spec.Containers[0].Image
	if primaryImage != sourceImage {
		t.Errorf("Got image %s wanted %s", primaryImage, sourceImage)
	}

	configPrimary, err := mocks.kubeClient.CoreV1().ConfigMaps("default").Get("podinfo-config-env-primary", metav1.GetOptions{})
	if err != nil {
		t.Fatal(err.Error())
	}

	if configPrimary.Data["color"] != config2.Data["color"] {
		t.Errorf("Got primary ConfigMap color %s wanted %s", configPrimary.Data["color"], config2.Data["color"])
	}
}

func TestCanaryDeployer_IsReady(t *testing.T) {
	mocks := SetupMocks()
	err := mocks.deployer.Sync(mocks.canary)
	if err != nil {
		t.Fatal(err.Error())
	}

	_, err = mocks.deployer.IsPrimaryReady(mocks.canary)
	if err != nil {
		t.Fatal(err.Error())
	}

	_, err = mocks.deployer.IsCanaryReady(mocks.canary)
	if err != nil {
		t.Fatal(err.Error())
	}
}

func TestCanaryDeployer_SetFailedChecks(t *testing.T) {
	mocks := SetupMocks()
	err := mocks.deployer.Sync(mocks.canary)
	if err != nil {
		t.Fatal(err.Error())
	}

	err = mocks.deployer.SetStatusFailedChecks(mocks.canary, 1)
	if err != nil {
		t.Fatal(err.Error())
	}

	res, err := mocks.flaggerClient.FlaggerV1alpha3().Canaries("default").Get("podinfo", metav1.GetOptions{})
	if err != nil {
		t.Fatal(err.Error())
	}

	if res.Status.FailedChecks != 1 {
		t.Errorf("Got %v wanted %v", res.Status.FailedChecks, 1)
	}
}

func TestCanaryDeployer_SetState(t *testing.T) {
	mocks := SetupMocks()
	err := mocks.deployer.Sync(mocks.canary)
	if err != nil {
		t.Fatal(err.Error())
	}

	err = mocks.deployer.SetStatusPhase(mocks.canary, v1alpha3.CanaryProgressing)
	if err != nil {
		t.Fatal(err.Error())
	}

	res, err := mocks.flaggerClient.FlaggerV1alpha3().Canaries("default").Get("podinfo", metav1.GetOptions{})
	if err != nil {
		t.Fatal(err.Error())
	}

	if res.Status.Phase != v1alpha3.CanaryProgressing {
		t.Errorf("Got %v wanted %v", res.Status.Phase, v1alpha3.CanaryProgressing)
	}
}

func TestCanaryDeployer_SyncStatus(t *testing.T) {
	mocks := SetupMocks()
	err := mocks.deployer.Sync(mocks.canary)
	if err != nil {
		t.Fatal(err.Error())
	}

	status := v1alpha3.CanaryStatus{
		Phase:        v1alpha3.CanaryProgressing,
		FailedChecks: 2,
	}
	err = mocks.deployer.SyncStatus(mocks.canary, status)
	if err != nil {
		t.Fatal(err.Error())
	}

	res, err := mocks.flaggerClient.FlaggerV1alpha3().Canaries("default").Get("podinfo", metav1.GetOptions{})
	if err != nil {
		t.Fatal(err.Error())
	}

	if res.Status.Phase != status.Phase {
		t.Errorf("Got state %v wanted %v", res.Status.Phase, status.Phase)
	}

	if res.Status.FailedChecks != status.FailedChecks {
		t.Errorf("Got failed checks %v wanted %v", res.Status.FailedChecks, status.FailedChecks)
	}

	if res.Status.TrackedConfigs == nil {
		t.Fatalf("Status tracking configs are empty")
	}
	configs := *res.Status.TrackedConfigs
	secret := NewTestSecret()
	if _, exists := configs["secret/"+secret.GetName()]; !exists {
		t.Errorf("Secret %s not found in status", secret.GetName())
	}
}

func TestCanaryDeployer_Scale(t *testing.T) {
	mocks := SetupMocks()
	err := mocks.deployer.Sync(mocks.canary)
	if err != nil {
		t.Fatal(err.Error())
	}

	err = mocks.deployer.Scale(mocks.canary, 2)

	c, err := mocks.kubeClient.AppsV1().Deployments("default").Get("podinfo", metav1.GetOptions{})
	if err != nil {
		t.Fatal(err.Error())
	}

	if *c.Spec.Replicas != 2 {
		t.Errorf("Got replicas %v wanted %v", *c.Spec.Replicas, 2)
	}

}
