package common

import (
	"fmt"

	"github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1"
	"golang.org/x/mod/semver"
	corev1 "k8s.io/api/core/v1"

	"github.com/tidbcloud/eks-provider/controllers/cluster/task/common/builder"
)

const (
	tidbOOMRecordVolumeName       = builder.TiDBOOMRecordVolumeName
	tidbOOMRecordContainerPathV1  = builder.TiDBOOMRecordContainerPathV1
	tidbOOMRecordContainerPathV2  = builder.TiDBOOMRecordContainerPathV2
	tidbOOMRecordHostPathTmp      = builder.TiDBOOMRecordHostPathTmp
	tidbOOMRecordMountSubPathExpr = builder.TiDBOOMRecordMountSubPathExpr

	tidbPlanReplayerVolumeName       = builder.TiDBPlanReplayerVolumeName
	tidbPlanReplayerContainerPathV1  = builder.TiDBPlanReplayerContainerPathV1
	tidbPlanReplayerContainerPathV2  = builder.TiDBPlanReplayerContainerPathV2
	tidbPlanReplayerHostPathTmp      = builder.TiDBPlanReplayerHostPathTmp
	tidbPlanReplayerMountSubPathExpr = builder.TiDBPlanReplayerMountSubPathExpr
)

func (c *ClusterContext) MountOOMPathToHost(tc *v1alpha1.TidbCluster) {
	if checkMountOOMPathSupportVersionV1(tc.Spec.Version) {
		c.mountOOMPathToHostV1(tc)
	} else if checkMountOOMPathSupportVersionV2(tc.Spec.Version) {
		c.mountOOMPathToHostV2(tc)
	}
}

// [v6.1.0, v6.4.0)
func checkMountOOMPathSupportVersionV1(version string) bool {
	return semver.Compare(version, "v6.1.0") >= 0 && semver.Compare(version, "v6.4.0") < 0
}

func (c *ClusterContext) mountOOMPathToHostV1(tc *v1alpha1.TidbCluster) {
	vol, mount := c.tidbOOMRecordVolumeAndMount(tidbOOMRecordContainerPathV1)
	overlayVolume(&tc.Spec.TiDB.AdditionalVolumes, vol)
	overlayVolumeMount(&tc.Spec.TiDB.AdditionalVolumeMounts, mount)
}

// [v6.4.0, +inf)
func checkMountOOMPathSupportVersionV2(version string) bool {
	return semver.Compare(version, "v6.4.0") >= 0
}

func (c *ClusterContext) mountOOMPathToHostV2(tc *v1alpha1.TidbCluster) {
	vol, mount := c.tidbOOMRecordVolumeAndMount(tidbOOMRecordContainerPathV2)
	overlayVolume(&tc.Spec.TiDB.AdditionalVolumes, vol)
	overlayVolumeMount(&tc.Spec.TiDB.AdditionalVolumeMounts, mount)
}

func (c *ClusterContext) tidbOOMRecordVolumeAndMount(containerPath string) (corev1.Volume, corev1.VolumeMount) {
	tenantID := c.Target.GetTenantID()
	projectID := c.Target.GetProjectID()
	clusterID := c.Target.GetClusterID()

	hostPathType := corev1.HostPathDirectoryOrCreate
	return corev1.Volume{
			Name: tidbOOMRecordVolumeName,
			VolumeSource: corev1.VolumeSource{
				HostPath: &corev1.HostPathVolumeSource{
					Path: fmt.Sprintf(tidbOOMRecordHostPathTmp, tenantID, projectID, clusterID),
					Type: &hostPathType,
				},
			},
		}, corev1.VolumeMount{
			Name:        tidbOOMRecordVolumeName,
			MountPath:   containerPath,
			SubPathExpr: tidbOOMRecordMountSubPathExpr,
		}
}

func (c *ClusterContext) MountPlanReplayerPathToHost(tc *v1alpha1.TidbCluster) {
	if checkMountPlanReplayerSupportVersionV1(tc.Spec.Version) {
		c.mountPlanReplayerPathToHostV1(tc)
	} else if checkMountPlanReplayerSupportVersionV2(tc.Spec.Version) {
		c.mountPlanReplayerPathToHostV2(tc)
	}
}

// [v5.3.0, v6.4.0)
func checkMountPlanReplayerSupportVersionV1(version string) bool {
	return semver.Compare(version, "v5.3.0") >= 0 && semver.Compare(version, "v6.4.0") < 0
}

// [v6.4.0, +inf)
func checkMountPlanReplayerSupportVersionV2(version string) bool {
	return semver.Compare(version, "v6.4.0") >= 0
}

func (c *ClusterContext) mountPlanReplayerPathToHostV1(tc *v1alpha1.TidbCluster) {
	vol, mount := c.tidbPlanReplayerVolumeAndMount(tidbPlanReplayerContainerPathV1)
	overlayVolume(&tc.Spec.TiDB.AdditionalVolumes, vol)
	overlayVolumeMount(&tc.Spec.TiDB.AdditionalVolumeMounts, mount)
}

func (c *ClusterContext) mountPlanReplayerPathToHostV2(tc *v1alpha1.TidbCluster) {
	vol, mount := c.tidbPlanReplayerVolumeAndMount(tidbPlanReplayerContainerPathV2)
	overlayVolume(&tc.Spec.TiDB.AdditionalVolumes, vol)
	overlayVolumeMount(&tc.Spec.TiDB.AdditionalVolumeMounts, mount)
}

func (c *ClusterContext) tidbPlanReplayerVolumeAndMount(containerPath string) (corev1.Volume, corev1.VolumeMount) {
	tenantID := c.Target.GetTenantID()
	projectID := c.Target.GetProjectID()
	clusterID := c.Target.GetClusterID()

	hostPathType := corev1.HostPathDirectoryOrCreate
	return corev1.Volume{
			Name: tidbPlanReplayerVolumeName,
			VolumeSource: corev1.VolumeSource{
				HostPath: &corev1.HostPathVolumeSource{
					Path: fmt.Sprintf(tidbPlanReplayerHostPathTmp, tenantID, projectID, clusterID),
					Type: &hostPathType,
				},
			},
		}, corev1.VolumeMount{
			Name:        tidbPlanReplayerVolumeName,
			MountPath:   containerPath,
			SubPathExpr: tidbPlanReplayerMountSubPathExpr,
		}
}

func overlayVolume(volumes *[]corev1.Volume, vol corev1.Volume) {
	found := false
	for i := range *volumes {
		cur := &(*volumes)[i]
		if cur.Name == vol.Name {
			(*volumes)[i] = vol
			found = true
		}
	}
	if !found {
		*volumes = append(*volumes, vol)
	}
}

func overlayVolumeMount(mounts *[]corev1.VolumeMount, mount corev1.VolumeMount) {
	found := false
	for i := range *mounts {
		cur := &(*mounts)[i]
		if cur.Name == mount.Name {
			(*mounts)[i] = mount
			found = true
		}
	}
	if !found {
		*mounts = append(*mounts, mount)
	}
}
