package imports

import (
	"errors"

	batchv1 "k8s.io/api/batch/v1"
	corev1 "k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/api/resource"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/util/intstr"
	"k8s.io/utils/pointer"

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

const lightningConfigFileName = "tidb-lightning.toml"

var lightningToleration = corev1.Toleration{
	Key:      "dedicated",
	Operator: "Equal",
	Value:    "tidb-" + lightningName,
	Effect:   corev1.TaintEffectNoSchedule,
}

func appendEmptyVolume(job *batchv1.Job) {
	volume := corev1.Volume{
		Name: job.Name,
		VolumeSource: corev1.VolumeSource{
			EmptyDir: &corev1.EmptyDirVolumeSource{},
		},
	}
	job.Spec.Template.Spec.Volumes = append(job.Spec.Template.Spec.Volumes, volume)

	mount := corev1.VolumeMount{
		Name:      job.Name,
		MountPath: "/etc/endpoint",
	}

	for i := range job.Spec.Template.Spec.Containers {
		c := job.Spec.Template.Spec.Containers[i]
		c.VolumeMounts = append(c.VolumeMounts, mount)
		job.Spec.Template.Spec.Containers[i] = c
	}
}

func appendVolume(job *batchv1.Job) {
	volume := corev1.Volume{
		Name: job.Name,
		VolumeSource: corev1.VolumeSource{
			PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{
				ClaimName: job.Name,
				ReadOnly:  false,
			},
		},
	}
	job.Spec.Template.Spec.Volumes = append(job.Spec.Template.Spec.Volumes, volume)

	mount := corev1.VolumeMount{
		Name:      job.Name,
		MountPath: "/etc/endpoint",
	}

	for i := range job.Spec.Template.Spec.Containers {
		c := job.Spec.Template.Spec.Containers[i]
		c.VolumeMounts = append(c.VolumeMounts, mount)
		job.Spec.Template.Spec.Containers[i] = c
	}
}

func newLightningService(name string, ns string, instance string) *corev1.Service {
	selector := map[string]string{
		label.NameLabelKey: name,
	}

	svc := &corev1.Service{
		ObjectMeta: metav1.ObjectMeta{
			Name:      name,
			Namespace: ns,
			Labels:    lightningLabels(name, instance),
		},
		Spec: corev1.ServiceSpec{
			Selector: selector,
			Ports: []corev1.ServicePort{
				{
					Protocol:   corev1.ProtocolTCP,
					Port:       lightningPort,
					TargetPort: intstr.FromInt(lightningPort),
				},
			},
		},
	}

	return svc
}

func GetImportIamIdentity(network *cloudv1.Network) (string, error) {
	if network.Status.Import.RoleARN == "" {
		return "", errors.New("no import role arn in network status")
	}

	return network.Status.Import.RoleARN, nil
}

func newPersistentVolumeClaim(name string, ns string, instance string, storageClassName string, requestLimit string) *corev1.PersistentVolumeClaim {
	v := resource.MustParse(requestLimit)

	return &corev1.PersistentVolumeClaim{
		ObjectMeta: metav1.ObjectMeta{
			Name:      name,
			Namespace: ns,
			Labels:    lightningLabels(name, instance),
		},
		Spec: corev1.PersistentVolumeClaimSpec{
			AccessModes: []corev1.PersistentVolumeAccessMode{
				corev1.ReadWriteOnce,
			},
			StorageClassName: pointer.StringPtr(storageClassName),
			Resources: corev1.ResourceRequirements{
				Requests: corev1.ResourceList{
					corev1.ResourceStorage: v,
				},
			},
		},
	}
}

func lightningLabels(name, instance string) map[string]string {
	labels := map[string]string{
		label.ComponentLabelKey: lightningName,
		label.InstanceLabelKey:  instance, // "db" for dedicated and "db${clusterName}" for dev-tier now
		label.NameLabelKey:      name,
	}
	return labels
}

func newLightningSecret(providerType string, name string, ns string, instance string, cfg string, cred []byte) *corev1.Secret {
	cm := &corev1.Secret{
		ObjectMeta: metav1.ObjectMeta{
			Name:      name,
			Namespace: ns,
			Labels:    lightningLabels(name, instance),
		},
		Data: map[string][]byte{
			lightningConfigFileName: []byte(cfg),
		},
	}

	switch providerType {
	case consts.ProviderTypeAWS, consts.ProviderTypeEKS, consts.ProviderTypeAWSFreeTier,
		consts.ProviderTypeAWSSharedTier, consts.ProviderTypeGCPSharedTier:
		if len(cred) > 0 {
			cm.Data[awsCredentialsKey] = cred
		}
	case consts.ProviderTypeGCP, consts.ProviderTypeGKE:
		cm.Data[gcpCredentialsKey] = cred
	}
	return cm
}
