package util

import (
	"context"
	"fmt"
	"time"

	customdomainv1 "github.com/tidbcloud/api-gateway/operator/api/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"sigs.k8s.io/controller-runtime/pkg/log"
)

func SetExclusiveCustomDomainCondition(
	domain *customdomainv1.CustomDomain,
	conditionType customdomainv1.CustomDomainConditionType,
	status customdomainv1.ConditionStatus,
) {
	newCondition := customdomainv1.CustomDomainCondition{
		Type:   conditionType,
		Status: status,
	}
	nowTime := metav1.NewTime(time.Now())
	newCondition.LastTransitionTime = &nowTime

	conditions := make([]customdomainv1.CustomDomainCondition, 1)
	conditions[0] = newCondition

	domain.Status.Conditions = conditions
}

func CustomDomainHasConditon(domain *customdomainv1.CustomDomain, c customdomainv1.CustomDomainCondition) bool {
	if domain == nil {
		return false
	}
	existingConditions := domain.Status.Conditions
	for _, cond := range existingConditions {
		if c.Type == cond.Type && c.Status == cond.Status {
			return true
		}
	}
	return false
}

func GetCustomDomainCondition(domain *customdomainv1.CustomDomain, conditionType customdomainv1.CustomDomainConditionType) *customdomainv1.CustomDomainCondition {
	for _, cond := range domain.Status.Conditions {
		if cond.Type == conditionType {
			return &cond
		}
	}
	return nil
}

func SetCustomDomainDeploymentCondition(
	ctx context.Context,
	deploy *customdomainv1.CustomDomainDeployment,
	conditionType customdomainv1.CustomDomainDeploymentConditionType,
	status customdomainv1.ConditionStatus,
) {
	log := log.FromContext(ctx)
	newCondition := customdomainv1.CustomDomainDeploymentCondition{
		Type:   conditionType,
		Status: status,
	}
	nowTime := metav1.NewTime(time.Now())
	newCondition.LastTransitionTime = &nowTime
	// Search through existing conditions
	for idx, cond := range deploy.Status.Conditions {
		// Skip unrelated conditions
		if cond.Type != conditionType {
			continue
		}

		// If this update doesn't contain a state transition, we don't update
		// the conditions LastTransitionTime to Now()
		if cond.Status == status {
			newCondition.LastTransitionTime = cond.LastTransitionTime
		} else {
			log.Info(fmt.Sprintf("Found status change for CustomDomainDeployment %q condition %q: %q -> %q; setting lastTransitionTime to %v", deploy.Name, conditionType, cond.Status, status, nowTime.Time))
		}

		// Overwrite the existing condition
		deploy.Status.Conditions[idx] = newCondition
		return
	}

	// If we've not found an existing condition of this type, we simply insert
	// the new condition into the slice.
	deploy.Status.Conditions = append(deploy.Status.Conditions, newCondition)
	log.Info(fmt.Sprintf("Setting lastTransitionTime for CustomDomainDeployment %q condition %q to %v", deploy.Name, conditionType, nowTime.Time))
}

func RemoveCustomDomainDeploymentCondition(deploy *customdomainv1.CustomDomainDeployment, conditionType customdomainv1.CustomDomainDeploymentConditionType) {
	var updatedConditions []customdomainv1.CustomDomainDeploymentCondition
	// Search through existing conditions
	for _, cond := range deploy.Status.Conditions {
		// Only add unrelated conditions
		if cond.Type != conditionType {
			updatedConditions = append(updatedConditions, cond)
		}
	}
	deploy.Status.Conditions = updatedConditions
}

func CustomDomainDeploymentHasCondition(deploy *customdomainv1.CustomDomainDeployment, c customdomainv1.CustomDomainDeploymentCondition) bool {
	if deploy == nil {
		return false
	}
	existingConditions := deploy.Status.Conditions
	for _, cond := range existingConditions {
		if c.Type == cond.Type && c.Status == cond.Status {
			return true
		}
	}
	return false
}

func SetCustomDomainAlbCondition(
	ctx context.Context,
	alb *customdomainv1.CustomDomainAlb,
	conditionType customdomainv1.CustomDomainAlbConditionType,
	status customdomainv1.ConditionStatus,
) {
	log := log.FromContext(ctx)
	newCondition := customdomainv1.CustomDomainAlbCondition{
		Type:   conditionType,
		Status: status,
	}
	nowTime := metav1.NewTime(time.Now())
	newCondition.LastTransitionTime = &nowTime
	// Search through existing conditions
	for idx, cond := range alb.Status.Conditions {
		// Skip unrelated conditions
		if cond.Type != conditionType {
			continue
		}

		// If this update doesn't contain a state transition, we don't update
		// the conditions LastTransitionTime to Now()
		if cond.Status == status {
			newCondition.LastTransitionTime = cond.LastTransitionTime
		} else {
			log.Info(fmt.Sprintf("Found status change for CustomDomainAlb %q condition %q: %q -> %q; setting lastTransitionTime to %v", alb.Name, conditionType, cond.Status, status, nowTime.Time))
		}

		// Overwrite the existing condition
		alb.Status.Conditions[idx] = newCondition
		return
	}

	// If we've not found an existing condition of this type, we simply insert
	// the new condition into the slice.
	alb.Status.Conditions = append(alb.Status.Conditions, newCondition)
	log.Info(fmt.Sprintf("Setting lastTransitionTime for CustomDomainAlb %q condition %q to %v", alb.Name, conditionType, nowTime.Time))
}

func RemoveCustomDomainAlbCondition(alb *customdomainv1.CustomDomainAlb, conditionType customdomainv1.CustomDomainAlbConditionType) {
	var updatedConditions []customdomainv1.CustomDomainAlbCondition
	// Search through existing conditions
	for _, cond := range alb.Status.Conditions {
		// Only add unrelated conditions
		if cond.Type != conditionType {
			updatedConditions = append(updatedConditions, cond)
		}
	}
	alb.Status.Conditions = updatedConditions
}

func CustomDomainAlbHasCondition(alb *customdomainv1.CustomDomainAlb, c customdomainv1.CustomDomainAlbCondition) bool {
	if alb == nil {
		return false
	}
	existingConditions := alb.Status.Conditions
	for _, cond := range existingConditions {
		if c.Type == cond.Type && c.Status == cond.Status {
			return true
		}
	}
	return false
}
