package applier

import (
	"fmt"

	clusterng "github.com/tidbcloud/cluster-service-ng/api/cluster-service-ng/v1beta"
	"github.com/tidbcloud/devops/pkg/infra/clients/mgmthttpcli"
	nextgenv1 "github.com/tidbcloud/tidb-management-service/api/spec/global/nextgen/v1"

	ccproto "github.com/tidbcloud/config-center/proto/config_center"
)

func inputClusterMetaRequest(clusterType ccproto.ClusterType, clusterID string, input *Input) *ccproto.UpdateClusterMetaRequest {
	req := &ccproto.UpdateClusterMetaRequest{
		ClusterType:               clusterType,
		ClusterId:                 clusterID,
		ComponentRequests:         nil,
		ConfigurationFileRequests: nil,
	}
	fillClusterMetaRequestFromInput(req, input)
	return req
}

func fillClusterMetaRequestFromInput(req *ccproto.UpdateClusterMetaRequest, input *Input) {
	if input == nil {
		return
	}
	for _, file := range input.Files {
		req.ConfigurationFileRequests = append(req.ConfigurationFileRequests, &ccproto.UpdateClusterConfigurationFileRequest{
			ConfigurationFilename: file.Filename,
			ConfigurationType:     &file.ConfigType,
		})
	}
	for _, component := range input.Components {
		req.ComponentRequests = append(req.ComponentRequests, &ccproto.UpdateClusterComponentRequest{
			ComponentName: component.Component,
		})
	}
}

func targetComponentsAvailable(components []*mgmthttpcli.Component, targets []*clusterng.TargetComponent) (bool, string) {
	for _, target := range targets {
		matched := false
		for _, component := range components {
			if component.ComponentType != target.GetComponentType().String() {
				continue
			}
			if target.GetId() != "" && component.Id != target.GetId() {
				continue
			}
			matched = true
			if component.Phase != "AVAILABLE" {
				return false, fmt.Sprintf("component %s phase is '%s'", target.GetComponentType(), component.Phase)
			}
		}
		if !matched {
			if target.GetId() == "" {
				return false, fmt.Sprintf("component %s is not found", target.GetComponentType())
			}
			return false, fmt.Sprintf("component %s/%s is not found", target.GetComponentType(), target.GetId())
		}
	}
	return true, ""
}

func componentGroupAvailabilityTargets(clusterType ccproto.ClusterType, targets []*clusterng.TargetComponent) []*clusterng.TargetComponent {
	filtered := make([]*clusterng.TargetComponent, 0, len(targets))
	for _, target := range targets {
		if target == nil || !requiresComponentGroupAvailabilityCheck(clusterType, target.GetComponentType()) {
			continue
		}
		filtered = append(filtered, target)
	}
	return filtered
}

func requiresComponentGroupAvailabilityCheck(clusterType ccproto.ClusterType, componentType clusterng.ComponentType) bool {
	if isBRComponent(componentType) {
		return false
	}
	if clusterType == ccproto.ClusterType_SHARED_POOL && skipSharedPoolComponentApplyViaClusterService(clusterType, componentType) {
		return false
	}
	return true
}

func isBRComponent(componentType clusterng.ComponentType) bool {
	switch componentType {
	case clusterng.ComponentType_TIBR_AUTOBACKUP,
		clusterng.ComponentType_TIBRGC_T2_DFSGC,
		clusterng.ComponentType_TIBRGC_T3_ARCHIVE,
		clusterng.ComponentType_TIBR_RESTORE,
		clusterng.ComponentType_CLASSIC_RESTORE:
		return true
	default:
		return false
	}
}

func targetComponentsReliable(targets []*clusterng.TargetComponent) bool {
	if len(targets) == 0 {
		return false
	}
	for _, target := range targets {
		if target == nil || target.GetComponentType() == clusterng.ComponentType_COMPONENT_TYPE_UNSPECIFIED {
			return false
		}
	}
	return true
}

func resourcePoolRegion(profile *nextgenv1.ResourcePoolProfile) (string, string, bool) {
	if profile == nil || profile.GetRegion() == nil {
		return "", "", false
	}
	return profile.GetRegion().GetLogicalName(), profile.GetRegion().GetCloudProviderId().String(), true
}
