package v1

import (
	"context"
	"fmt"

	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/util/validation/field"
	ctrl "sigs.k8s.io/controller-runtime"
	logf "sigs.k8s.io/controller-runtime/pkg/log"
	"sigs.k8s.io/controller-runtime/pkg/webhook"
	"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
)

// log is for logging in this package.
var restorelog = logf.Log.WithName("restore-resource")

func (r *Restore) setupWebhookWithManager(mgr ctrl.Manager) error {
	return ctrl.NewWebhookManagedBy(mgr).
		For(r).
		Complete()
}

//+kubebuilder:webhook:path=/mutate-cloud-pingcap-com-v1-restore,mutating=true,failurePolicy=fail,sideEffects=None,groups=cloud.pingcap.com,resources=restores,verbs=create;update,versions=v1,name=mrestore.kb.io,admissionReviewVersions={v1,v1beta1}

var _ webhook.Defaulter = &Restore{}

// Default implements webhook.Defaulter so a webhook will be registered for the type
func (r *Restore) Default() {
	if r.Labels == nil {
		r.Labels = map[string]string{}
	}

	var c Cluster
	err := webhookClient.Get(context.TODO(), r.GetClusterRef(), &c)
	if err != nil {
		restorelog.Error(err, "error get cluster of restore, forward to validation")
		return
	}
	r.Labels[ProviderTypeLabel] = c.GetProviderType()
	r.Labels[RegionLabel] = c.Labels[RegionLabel]
}

//+kubebuilder:webhook:path=/validate-cloud-pingcap-com-v1-restore,mutating=false,failurePolicy=fail,sideEffects=None,groups=cloud.pingcap.com,resources=restores,verbs=create;update,versions=v1,name=vrestore.kb.io,admissionReviewVersions={v1,v1beta1}

var _ webhook.Validator = &Restore{}

// ValidateCreate implements webhook.Validator so a webhook will be registered for the type
func (r *Restore) ValidateCreate() (admission.Warnings, error) {
	restorelog.V(4).Info("validate create", "name", r.Name)

	var allErrs field.ErrorList
	if errs := r.validateSpec(); errs != nil {
		allErrs = append(allErrs, errs...)
	}
	if err := validateProviderLabelForClusterSubresource(r); err != nil {
		allErrs = append(allErrs, err)
	}

	return nil, invalidOrNil(allErrs, r)
}

// ValidateUpdate implements webhook.Validator so a webhook will be registered for the type
func (r *Restore) ValidateUpdate(old runtime.Object) (admission.Warnings, error) {
	restorelog.V(4).Info("validate update", "name", r.Name)

	var allErrs field.ErrorList
	oldR := old.(*Restore)
	if r.Spec.ClusterName != oldR.Spec.ClusterName {
		allErrs = append(allErrs, field.Invalid(field.NewPath("spec").Child("clusterName"), r.Spec.ClusterName, "field is immutable"))
	}

	if r.GetLabels()[ProviderTypeLabel] != oldR.GetLabels()[ProviderTypeLabel] {
		allErrs = append(allErrs, field.Invalid(field.NewPath("metadata").Child("labels"), r.GetLabels()[ProviderTypeLabel],
			fmt.Sprintf("%s label is immutable", ProviderTypeLabel)))
	}

	return nil, invalidOrNil(allErrs, r)
}

// ValidateDelete implements webhook.Validator so a webhook will be registered for the type
func (r *Restore) ValidateDelete() (admission.Warnings, error) {
	return nil, nil
}

func (r *Restore) validateSpec() field.ErrorList {
	var allErrs field.ErrorList
	fld := field.NewPath("spec")
	// skip validate cluster exist for migrated resources
	if !isMigratedResource(r.Annotations) {
		var c Cluster
		err := webhookClient.Get(context.TODO(), r.GetClusterRef(), &c)
		if err != nil {
			allErrs = append(allErrs, field.Invalid(fld.Child("clusterName"), r.Spec.ClusterName,
				fmt.Sprintf("cluster not found, err: %s", err.Error())))
		}
	}

	if r.Spec.Configuration != nil {
		if r.Spec.Configuration.BaseImage == "" {
			allErrs = append(allErrs, field.Invalid(fld.Child("configuration", "baseImage"),
				r.Spec.Configuration, "baseImage in configuration is empty"))
		}
		if r.Spec.Configuration.Tag == "" {
			allErrs = append(allErrs, field.Invalid(fld.Child("configuration", "tag"),
				r.Spec.Configuration, "tag in configuration is empty"))
		}
	}

	return allErrs
}
