package cluster

import (
	"context"

	"github.com/tidbcloud/observability/pkg/resourceworker/controllers/reconciler"

	"github.com/go-logr/logr"
	"github.com/tidbcloud/observability/pkg/common/constants"
	"k8s.io/apimachinery/pkg/types"

	observabilitypingcapcomv1 "github.com/tidbcloud/observability/api/regionalserver/resourceworker/v1"
	"sigs.k8s.io/controller-runtime/pkg/client"
	"sigs.k8s.io/controller-runtime/pkg/log"
)

func (cls *Cluster) makeEndpointService(ctx context.Context, clusterv1 *observabilitypingcapcomv1.Cluster) error {
	//create endpoint service crd
	var endpointService observabilitypingcapcomv1.EndpointService
	err := cls.getAndCreate(ctx, clusterv1.Namespace, svcPrefix+clusterv1.Spec.Endpoint.Name, &endpointService, clusterv1, makeEndpointService)
	if err != nil {
		cls.reconcilerRepoter(clusterv1.Name, "", err.Error())
		return err
	}
	return nil
}

func (cls *Cluster) deleteEndpointService(ctx context.Context, clusterv1 *observabilitypingcapcomv1.Cluster) error {
	log := log.FromContext(ctx).WithName("deleteEndpointService")

	var endpointService observabilitypingcapcomv1.EndpointService
	return cls.getAndDelete(ctx, log, clusterv1.Namespace, svcPrefix+clusterv1.Spec.Endpoint.Name, &endpointService)
}

func (cls *Cluster) getEndpointService(ctx context.Context, log logr.Logger, namespace string, name string) (*observabilitypingcapcomv1.EndpointService, error) {
	log.WithName("getEndpointService")
	var endpointService observabilitypingcapcomv1.EndpointService
	err := cls.cli.Get(ctx, types.NamespacedName{Namespace: namespace, Name: name}, &endpointService)
	if err != nil {
		log.Error(err, "get endpoint service crd failed")
		return nil, err
	}
	return &endpointService, nil
}

func (cls *Cluster) EndpointServiceStatus(ctx context.Context, clusterv1 *observabilitypingcapcomv1.Cluster, obj client.Object, checkTimeout bool) (bool, bool, error) {
	endpointService := obj.(*observabilitypingcapcomv1.EndpointService)
	//for next-gen cluster, if endpointService spac is is not empty, it means the endpoint service should be created
	if NextGenNeedEndpointService(clusterv1) {
		if endpointService.Status.Phase != constants.ACTIVE {
			return false, false, nil
		}
		return true, false, nil
	}
	if RejectInsertIngress(clusterv1) {
		//for serverless cluster, we don't need to create endpoint service
		//because it doesn't have insert ingress
		return true, false, nil
	}
	if endpointService.Status.Phase != constants.ACTIVE {
		return false, false, nil
	}
	return true, false, nil
}

func makeEndpointService(cls *Cluster, clusterv1 *observabilitypingcapcomv1.Cluster) (client.Object, error) {
	var endpointService observabilitypingcapcomv1.EndpointService
	endpointService.SetName(svcPrefix + clusterv1.Spec.Endpoint.Name)
	endpointService.SetNamespace(clusterv1.Namespace)
	endpointService.Spec.ServiceName = svcPrefix + clusterv1.Spec.Endpoint.Name
	endpointService.Spec.UserARNs = append(endpointService.Spec.UserARNs, clusterv1.Spec.Endpoint.UserARN)
	endpointService.Spec.Shared = clusterv1.Spec.Endpoint.Shared
	if endpointService.Spec.Shared {
		endpointService.Spec.EndpointServiceID = cls.serviceInfo.AZConfig[cls.GetMonitorAZ(clusterv1)].EndpointServiceID
	}
	return &endpointService, nil
}

func (cls *Cluster) ObserveEndpointServiceResource(ctx context.Context, clusterv1 *observabilitypingcapcomv1.Cluster) (reconciler.SubResourceObservation, client.Object, error) {
	allExist := true
	partExist := false
	var endpointService observabilitypingcapcomv1.EndpointService
	//for serverless cluster, we don't need to create endpoint service
	//because it doesn't have insert ingress
	// for next-gen cluster, if endpointService space is not empty, it means the endpoint service should be created
	if RejectInsertIngress(clusterv1) && !NextGenNeedEndpointService(clusterv1) {
		return reconciler.SubResourceObservation{
			ResourceExists:     true,
			PartResourceExists: true,
		}, &endpointService, nil
	}
	err := cls.cli.Get(ctx, types.NamespacedName{Namespace: clusterv1.Namespace, Name: svcPrefix + clusterv1.Spec.Endpoint.Name}, &endpointService)
	if err != nil {
		if client.IgnoreNotFound(err) != nil {
			return reconciler.SubResourceObservation{}, nil, err
		}
		allExist = allExist && false
	} else {
		partExist = partExist || true
	}

	return reconciler.SubResourceObservation{
		ResourceExists:     allExist,
		PartResourceExists: partExist,
	}, &endpointService, nil
}
