package cluster

import (
	"testing"

	. "github.com/onsi/gomega"
	"github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1"
	corev1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

	cloudv1 "github.com/tidbcloud/infra-api/api/v1"
	"github.com/tidbcloud/infra-api/pkg/consts"
)

func TestMountOOMToHost(t *testing.T) {
	cc := ClusterContext{Target: &cloudv1.Cluster{
		ObjectMeta: metav1.ObjectMeta{
			Labels: map[string]string{
				consts.TenantTagKey:  "test-tenant",
				consts.ProjectTagKey: "test-project",
				consts.ClusterTagKey: "test-cluster",
			},
		},
	},
		Network: &cloudv1.Network{ObjectMeta: metav1.ObjectMeta{Name: "test-network"}},
	}

	// [v6.1.0, v6.4.0)
	v1Cases := []struct {
		tc *v1alpha1.TidbCluster
	}{
		{
			tc: &v1alpha1.TidbCluster{
				Spec: v1alpha1.TidbClusterSpec{Version: "v6.1.0", TiDB: &v1alpha1.TiDBSpec{}},
			},
		},
		{
			tc: &v1alpha1.TidbCluster{
				Spec: v1alpha1.TidbClusterSpec{Version: "v6.1.2", TiDB: &v1alpha1.TiDBSpec{}},
			},
		},
		{
			tc: &v1alpha1.TidbCluster{
				Spec: v1alpha1.TidbClusterSpec{Version: "v6.2.0", TiDB: &v1alpha1.TiDBSpec{
					ComponentSpec: v1alpha1.ComponentSpec{
						AdditionalVolumes: []corev1.Volume{{
							Name: tidbOOMRecordVolumeName,
							VolumeSource: corev1.VolumeSource{
								HostPath: &corev1.HostPathVolumeSource{
									Path: "to-be-overwritten",
								},
							},
						}},
						AdditionalVolumeMounts: []corev1.VolumeMount{{
							Name:      tidbOOMRecordVolumeName,
							MountPath: "to-be-overwritten",
						}},
					},
				}},
			},
		},
		{
			tc: &v1alpha1.TidbCluster{
				Spec: v1alpha1.TidbClusterSpec{Version: "v6.3.0", TiDB: &v1alpha1.TiDBSpec{
					ComponentSpec: v1alpha1.ComponentSpec{
						AdditionalVolumes: []corev1.Volume{{
							Name: "other-volume",
							VolumeSource: corev1.VolumeSource{
								HostPath: &corev1.HostPathVolumeSource{
									Path: "keep",
								},
							},
						}},
						AdditionalVolumeMounts: []corev1.VolumeMount{{
							Name:      "other-mount",
							MountPath: "keep",
						}},
					},
				}},
			},
		},
		{
			tc: &v1alpha1.TidbCluster{
				Spec: v1alpha1.TidbClusterSpec{Version: "v6.3.12", TiDB: &v1alpha1.TiDBSpec{}},
			},
		},
	}
	for _, v1Case := range v1Cases {
		t.Run(v1Case.tc.Spec.Version, func(t *testing.T) {
			g := NewGomegaWithT(t)
			cc.MountOOMPathToHost(v1Case.tc)

			g.Expect(v1Case.tc.Spec.TiDB.AdditionalVolumes).Should(ContainElement(HaveField("Name", Equal(tidbOOMRecordVolumeName))))
			for _, v := range v1Case.tc.Spec.TiDB.AdditionalVolumes {
				if v.Name == tidbOOMRecordVolumeName {
					g.Expect(v.HostPath.Path).To(Equal("/var/tidb_oom_record/test-tenant/test-project/test-cluster"))
					typ := corev1.HostPathDirectoryOrCreate
					g.Expect(v.HostPath.Type).To(Equal(&typ))
					break
				}
			}

			g.Expect(v1Case.tc.Spec.TiDB.AdditionalVolumes).Should(ContainElement(HaveField("Name", Equal(tidbOOMRecordVolumeName))))
			for _, v := range v1Case.tc.Spec.TiDB.AdditionalVolumeMounts {
				if v.Name == tidbOOMRecordVolumeName {
					g.Expect(v.MountPath).To(Equal(tidbOOMRecordContainerPathV1))
					g.Expect(v.SubPathExpr).To(Equal(tidbOOMRecordMountSubPathExpr))
					break
				}
			}
		})
	}

	// cases of older version
	olderCases := []struct {
		tc *v1alpha1.TidbCluster
	}{
		{
			tc: &v1alpha1.TidbCluster{
				Spec: v1alpha1.TidbClusterSpec{Version: "v5.4.3", TiDB: &v1alpha1.TiDBSpec{}},
			},
		},
		{
			tc: &v1alpha1.TidbCluster{
				Spec: v1alpha1.TidbClusterSpec{Version: "v5.4.1", TiDB: &v1alpha1.TiDBSpec{}},
			},
		},
		{
			tc: &v1alpha1.TidbCluster{
				Spec: v1alpha1.TidbClusterSpec{Version: "v5.4.0", TiDB: &v1alpha1.TiDBSpec{}},
			},
		},
		{
			tc: &v1alpha1.TidbCluster{
				Spec: v1alpha1.TidbClusterSpec{Version: "v5.3.1", TiDB: &v1alpha1.TiDBSpec{
					ComponentSpec: v1alpha1.ComponentSpec{
						AdditionalVolumes: []corev1.Volume{{
							Name: "other-volume",
							VolumeSource: corev1.VolumeSource{
								HostPath: &corev1.HostPathVolumeSource{
									Path: "keep",
								},
							},
						}},
						AdditionalVolumeMounts: []corev1.VolumeMount{{
							Name:      "other-mount",
							MountPath: "keep",
						}},
					},
				}},
			},
		},
		{
			tc: &v1alpha1.TidbCluster{
				Spec: v1alpha1.TidbClusterSpec{Version: "v5.0.2", TiDB: &v1alpha1.TiDBSpec{}},
			},
		},
		{
			tc: &v1alpha1.TidbCluster{
				Spec: v1alpha1.TidbClusterSpec{Version: "v5.0.0", TiDB: &v1alpha1.TiDBSpec{}},
			},
		},
	}
	for _, oldVer := range olderCases {
		t.Run(oldVer.tc.Spec.Version, func(t *testing.T) {
			g := NewGomegaWithT(t)
			cc.MountOOMPathToHost(oldVer.tc)

			g.Expect(oldVer.tc.Spec.TiDB.AdditionalVolumes).ShouldNot(ContainElement(HaveField("Name", Equal(tidbOOMRecordVolumeName))))
			g.Expect(oldVer.tc.Spec.TiDB.AdditionalVolumes).ShouldNot(ContainElement(HaveField("Name", Equal(tidbOOMRecordVolumeName))))
		})
	}

	// cases of newer version
	v2Cases := []struct {
		tc *v1alpha1.TidbCluster
	}{
		{
			tc: &v1alpha1.TidbCluster{
				Spec: v1alpha1.TidbClusterSpec{Version: "v6.4.0", TiDB: &v1alpha1.TiDBSpec{}},
			},
		},
		{
			tc: &v1alpha1.TidbCluster{
				Spec: v1alpha1.TidbClusterSpec{Version: "v6.5.0", TiDB: &v1alpha1.TiDBSpec{}},
			},
		},
	}
	for _, v2Case := range v2Cases {
		t.Run(v2Case.tc.Spec.Version, func(t *testing.T) {
			g := NewGomegaWithT(t)
			cc.MountOOMPathToHost(v2Case.tc)

			g.Expect(v2Case.tc.Spec.TiDB.AdditionalVolumes).Should(ContainElement(HaveField("Name", Equal(tidbOOMRecordVolumeName))))
			for _, v := range v2Case.tc.Spec.TiDB.AdditionalVolumes {
				if v.Name == tidbOOMRecordVolumeName {
					g.Expect(v.HostPath.Path).To(Equal("/var/tidb_oom_record/test-tenant/test-project/test-cluster"))
					typ := corev1.HostPathDirectoryOrCreate
					g.Expect(v.HostPath.Type).To(Equal(&typ))
					break
				}
			}

			g.Expect(v2Case.tc.Spec.TiDB.AdditionalVolumes).Should(ContainElement(HaveField("Name", Equal(tidbOOMRecordVolumeName))))
			for _, v := range v2Case.tc.Spec.TiDB.AdditionalVolumeMounts {
				if v.Name == tidbOOMRecordVolumeName {
					g.Expect(v.MountPath).To(Equal(tidbOOMRecordContainerPathV2))
					g.Expect(v.SubPathExpr).To(Equal(tidbOOMRecordMountSubPathExpr))
					break
				}
			}
		})
	}
}

func TestMountPlanReplayerToHost(t *testing.T) {
	cc := ClusterContext{Target: &cloudv1.Cluster{
		ObjectMeta: metav1.ObjectMeta{
			Labels: map[string]string{
				consts.TenantTagKey:  "test-tenant",
				consts.ProjectTagKey: "test-project",
				consts.ClusterTagKey: "test-cluster",
			},
		},
	},
		Network: &cloudv1.Network{ObjectMeta: metav1.ObjectMeta{Name: "test-network"}},
	}

	// [v5.3.0, v6.4.0)
	v1Cases := []struct {
		tc *v1alpha1.TidbCluster
	}{
		{
			tc: &v1alpha1.TidbCluster{
				Spec: v1alpha1.TidbClusterSpec{Version: "v5.3.0", TiDB: &v1alpha1.TiDBSpec{}},
			},
		},
		{
			tc: &v1alpha1.TidbCluster{
				Spec: v1alpha1.TidbClusterSpec{Version: "v6.0.2", TiDB: &v1alpha1.TiDBSpec{}},
			},
		},
		{
			tc: &v1alpha1.TidbCluster{
				Spec: v1alpha1.TidbClusterSpec{Version: "v6.2.0", TiDB: &v1alpha1.TiDBSpec{
					ComponentSpec: v1alpha1.ComponentSpec{
						AdditionalVolumes: []corev1.Volume{{
							Name: tidbPlanReplayerVolumeName,
							VolumeSource: corev1.VolumeSource{
								HostPath: &corev1.HostPathVolumeSource{
									Path: "to-be-overwritten",
								},
							},
						}},
						AdditionalVolumeMounts: []corev1.VolumeMount{{
							Name:      tidbPlanReplayerVolumeName,
							MountPath: "to-be-overwritten",
						}},
					},
				}},
			},
		},
		{
			tc: &v1alpha1.TidbCluster{
				Spec: v1alpha1.TidbClusterSpec{Version: "v6.3.0", TiDB: &v1alpha1.TiDBSpec{
					ComponentSpec: v1alpha1.ComponentSpec{
						AdditionalVolumes: []corev1.Volume{{
							Name: "other-volume",
							VolumeSource: corev1.VolumeSource{
								HostPath: &corev1.HostPathVolumeSource{
									Path: "keep",
								},
							},
						}},
						AdditionalVolumeMounts: []corev1.VolumeMount{{
							Name:      "other-mount",
							MountPath: "keep",
						}},
					},
				}},
			},
		},
		{
			tc: &v1alpha1.TidbCluster{
				Spec: v1alpha1.TidbClusterSpec{Version: "v6.3.12", TiDB: &v1alpha1.TiDBSpec{}},
			},
		},
	}
	for _, v1Case := range v1Cases {
		t.Run(v1Case.tc.Spec.Version, func(t *testing.T) {
			g := NewGomegaWithT(t)
			cc.MountPlanReplayerPathToHost(v1Case.tc)

			g.Expect(v1Case.tc.Spec.TiDB.AdditionalVolumes).Should(ContainElement(HaveField("Name", Equal(tidbPlanReplayerVolumeName))))
			for _, v := range v1Case.tc.Spec.TiDB.AdditionalVolumes {
				if v.Name == tidbPlanReplayerVolumeName {
					g.Expect(v.HostPath.Path).To(Equal("/var/tidb_plan_replayer/test-tenant/test-project/test-cluster"))
					typ := corev1.HostPathDirectoryOrCreate
					g.Expect(v.HostPath.Type).To(Equal(&typ))
					break
				}
			}

			g.Expect(v1Case.tc.Spec.TiDB.AdditionalVolumes).Should(ContainElement(HaveField("Name", Equal(tidbPlanReplayerVolumeName))))
			for _, v := range v1Case.tc.Spec.TiDB.AdditionalVolumeMounts {
				if v.Name == tidbPlanReplayerVolumeName {
					g.Expect(v.MountPath).To(Equal(tidbPlanReplayerContainerPathV1))
					g.Expect(v.SubPathExpr).To(Equal(tidbPlanReplayerMountSubPathExpr))
					break
				}
			}
		})
	}

	// cases of older version
	olderCases := []struct {
		tc *v1alpha1.TidbCluster
	}{
		{
			tc: &v1alpha1.TidbCluster{
				Spec: v1alpha1.TidbClusterSpec{Version: "v5.0.3", TiDB: &v1alpha1.TiDBSpec{}},
			},
		},
		{
			tc: &v1alpha1.TidbCluster{
				Spec: v1alpha1.TidbClusterSpec{Version: "v5.1.1", TiDB: &v1alpha1.TiDBSpec{}},
			},
		},
		{
			tc: &v1alpha1.TidbCluster{
				Spec: v1alpha1.TidbClusterSpec{Version: "v5.2.0", TiDB: &v1alpha1.TiDBSpec{}},
			},
		},
		{
			tc: &v1alpha1.TidbCluster{
				Spec: v1alpha1.TidbClusterSpec{Version: "v5.2.1", TiDB: &v1alpha1.TiDBSpec{
					ComponentSpec: v1alpha1.ComponentSpec{
						AdditionalVolumes: []corev1.Volume{{
							Name: "other-volume",
							VolumeSource: corev1.VolumeSource{
								HostPath: &corev1.HostPathVolumeSource{
									Path: "keep",
								},
							},
						}},
						AdditionalVolumeMounts: []corev1.VolumeMount{{
							Name:      "other-mount",
							MountPath: "keep",
						}},
					},
				}},
			},
		},
	}
	for _, oldVer := range olderCases {
		t.Run(oldVer.tc.Spec.Version, func(t *testing.T) {
			g := NewGomegaWithT(t)
			cc.MountPlanReplayerPathToHost(oldVer.tc)

			g.Expect(oldVer.tc.Spec.TiDB.AdditionalVolumes).ShouldNot(ContainElement(HaveField("Name", Equal(tidbPlanReplayerVolumeName))))
			g.Expect(oldVer.tc.Spec.TiDB.AdditionalVolumes).ShouldNot(ContainElement(HaveField("Name", Equal(tidbPlanReplayerVolumeName))))
		})
	}

	// cases of newer version
	v2Cases := []struct {
		tc *v1alpha1.TidbCluster
	}{
		{
			tc: &v1alpha1.TidbCluster{
				Spec: v1alpha1.TidbClusterSpec{Version: "v6.4.0", TiDB: &v1alpha1.TiDBSpec{}},
			},
		},
		{
			tc: &v1alpha1.TidbCluster{
				Spec: v1alpha1.TidbClusterSpec{Version: "v6.5.0", TiDB: &v1alpha1.TiDBSpec{}},
			},
		},
	}
	for _, v2Case := range v2Cases {
		t.Run(v2Case.tc.Spec.Version, func(t *testing.T) {
			g := NewGomegaWithT(t)
			cc.MountPlanReplayerPathToHost(v2Case.tc)

			g.Expect(v2Case.tc.Spec.TiDB.AdditionalVolumes).Should(ContainElement(HaveField("Name", Equal(tidbPlanReplayerVolumeName))))
			for _, v := range v2Case.tc.Spec.TiDB.AdditionalVolumes {
				if v.Name == tidbPlanReplayerVolumeName {
					g.Expect(v.HostPath.Path).To(Equal("/var/tidb_plan_replayer/test-tenant/test-project/test-cluster"))
					typ := corev1.HostPathDirectoryOrCreate
					g.Expect(v.HostPath.Type).To(Equal(&typ))
					break
				}
			}

			g.Expect(v2Case.tc.Spec.TiDB.AdditionalVolumes).Should(ContainElement(HaveField("Name", Equal(tidbPlanReplayerVolumeName))))
			for _, v := range v2Case.tc.Spec.TiDB.AdditionalVolumeMounts {
				if v.Name == tidbPlanReplayerVolumeName {
					g.Expect(v.MountPath).To(Equal(tidbPlanReplayerContainerPathV2))
					g.Expect(v.SubPathExpr).To(Equal(tidbPlanReplayerMountSubPathExpr))
					break
				}
			}
		})
	}
}
