package cluster

import (
	"context"
	"strings"

	"google.golang.org/protobuf/types/known/emptypb"

	globaldedicatedv1 "github.com/tidbcloud/tidb-management-service/api/spec/global/dedicated/v1"
	tidbmgmtservicev1 "github.com/tidbcloud/tidb-management-service/api/spec/tidb_mgmt_service/v1"
	"github.com/tidbcloud/tidb-management-service/tidb-mgmt-service/domain/entity"
	"github.com/tidbcloud/tidb-management-service/utilities/bizerr"
	"github.com/tidbcloud/tidb-management-service/utilities/grpc/metadata"
)

func (c *serverImpl) ApplyGKEHeterogeneousDeploy(
	ctx context.Context,
	request *tidbmgmtservicev1.ApplyGKEHeterogeneousDeployRequest,
) (*tidbmgmtservicev1.GKEHeterogeneousDeploy, error) {
	if request == nil {
		return nil, bizerr.New(bizerr.ErrCodeParams, "request is nil")
	}
	if request.GetTenantId() == 0 {
		return nil, bizerr.New(bizerr.ErrCodeParams, "tenant id is invalid")
	}
	if request.GetProjectId() == 0 {
		return nil, bizerr.New(bizerr.ErrCodeParams, "project id is invalid")
	}
	if request.GetClusterId() == 0 {
		return nil, bizerr.New(bizerr.ErrCodeParams, "cluster id is invalid")
	}
	if request.GetComponentType() == globaldedicatedv1.ComponentType_COMPONENT_TYPE_UNSPECIFIED {
		return nil, bizerr.New(bizerr.ErrCodeParams, "component type is invalid")
	}
	if request.GetComponentType() == globaldedicatedv1.ComponentType_TIDB && request.GetTidbGroupId() == 0 {
		return nil, bizerr.New(bizerr.ErrCodeParams, "tidb group id is required")
	}
	if request.GetComponentType() != globaldedicatedv1.ComponentType_TIDB && request.GetTidbGroupId() != 0 {
		return nil, bizerr.New(bizerr.ErrCodeParams, "tidb group id must be 0 for non-tidb component")
	}
	currentInstanceType := strings.TrimSpace(request.GetCurrentInstanceType())
	if currentInstanceType == "" {
		return nil, bizerr.New(bizerr.ErrCodeParams, "current instance type is required")
	}
	alternativeInstanceType := strings.TrimSpace(request.GetAlternativeInstanceType())
	if alternativeInstanceType == "" {
		return nil, bizerr.New(bizerr.ErrCodeParams, "alternative instance type is required")
	}
	if currentInstanceType == alternativeInstanceType {
		return nil, bizerr.New(bizerr.ErrCodeParams, "current and alternative instance types must be different")
	}

	cluster, err := c.dedicatedClusterSvc.GetCluster(ctx, request.GetClusterId(), entity.ClusterViewClusterViewLocal, false, false)
	if err != nil {
		return nil, bizerr.WrapError(err, "get cluster failed")
	}
	if cluster == nil {
		return nil, bizerr.New(bizerr.ErrCodeUnknown, "cluster not found")
	}
	ctx = metadata.AppendToOutgoingMeta(ctx, &metadata.RequestMeta{
		OrgID:            cluster.OrganizationId,
		DefaultProjectID: cluster.ProjectId,
		ValidProjectList: []uint64{cluster.ProjectId},
		ClusterID:        request.GetClusterId(),
		UserID:           "0",
	})

	resp, err := c.dedicatedCli.ApplyGKEHeterogeneousDeploy(ctx, &globaldedicatedv1.ApplyGKEHeterogeneousDeployRequest{
		TenantId:                request.GetTenantId(),
		ProjectId:               request.GetProjectId(),
		ClusterId:               request.GetClusterId(),
		ComponentType:           request.GetComponentType(),
		CurrentInstanceType:     currentInstanceType,
		AlternativeInstanceType: alternativeInstanceType,
		TidbGroupId:             request.GetTidbGroupId(),
	})
	if err != nil {
		return nil, err
	}

	return cvtGKEHeterogeneousDeploy(resp), nil
}

func (c *serverImpl) GetGKEHeterogeneousDeploy(
	ctx context.Context,
	request *tidbmgmtservicev1.GetGKEHeterogeneousDeployRequest,
) (*tidbmgmtservicev1.GKEHeterogeneousDeploy, error) {
	if request == nil {
		return nil, bizerr.New(bizerr.ErrCodeParams, "request is nil")
	}
	if request.GetClusterId() == 0 {
		return nil, bizerr.New(bizerr.ErrCodeParams, "cluster id is invalid")
	}
	if request.GetTenantId() == 0 {
		return nil, bizerr.New(bizerr.ErrCodeParams, "tenant id is invalid")
	}
	if request.GetProjectId() == 0 {
		return nil, bizerr.New(bizerr.ErrCodeParams, "project id is invalid")
	}
	if request.GetComponentType() == globaldedicatedv1.ComponentType_COMPONENT_TYPE_UNSPECIFIED {
		return nil, bizerr.New(bizerr.ErrCodeParams, "component type is invalid")
	}
	if request.GetComponentType() == globaldedicatedv1.ComponentType_TIDB && request.GetTidbGroupId() == 0 {
		return nil, bizerr.New(bizerr.ErrCodeParams, "tidb group id is required")
	}
	if request.GetComponentType() != globaldedicatedv1.ComponentType_TIDB && request.GetTidbGroupId() != 0 {
		return nil, bizerr.New(bizerr.ErrCodeParams, "tidb group id must be 0 for non-tidb component")
	}

	resp, err := c.dedicatedCli.GetGKEHeterogeneousDeploy(ctx, &globaldedicatedv1.GetGKEHeterogeneousDeployRequest{
		ClusterId:     request.GetClusterId(),
		ComponentType: request.GetComponentType(),
		TidbGroupId:   request.GetTidbGroupId(),
		TenantId:      request.GetTenantId(),
		ProjectId:     request.GetProjectId(),
	})
	if err != nil {
		return nil, err
	}

	return cvtGKEHeterogeneousDeploy(resp), nil
}

func (c *serverImpl) DeleteGKEHeterogeneousDeploy(
	ctx context.Context,
	request *tidbmgmtservicev1.DeleteGKEHeterogeneousDeployRequest,
) (*emptypb.Empty, error) {
	if request == nil {
		return nil, bizerr.New(bizerr.ErrCodeParams, "request is nil")
	}
	if request.GetClusterId() == 0 {
		return nil, bizerr.New(bizerr.ErrCodeParams, "cluster id is invalid")
	}
	if request.GetTenantId() == 0 {
		return nil, bizerr.New(bizerr.ErrCodeParams, "tenant id is invalid")
	}
	if request.GetProjectId() == 0 {
		return nil, bizerr.New(bizerr.ErrCodeParams, "project id is invalid")
	}
	if request.GetComponentType() == globaldedicatedv1.ComponentType_COMPONENT_TYPE_UNSPECIFIED {
		return nil, bizerr.New(bizerr.ErrCodeParams, "component type is invalid")
	}
	if request.GetComponentType() == globaldedicatedv1.ComponentType_TIDB && request.GetTidbGroupId() == 0 {
		return nil, bizerr.New(bizerr.ErrCodeParams, "tidb group id is required")
	}
	if request.GetComponentType() != globaldedicatedv1.ComponentType_TIDB && request.GetTidbGroupId() != 0 {
		return nil, bizerr.New(bizerr.ErrCodeParams, "tidb group id must be 0 for non-tidb component")
	}

	cluster, err := c.dedicatedClusterSvc.GetCluster(ctx, request.GetClusterId(), entity.ClusterViewClusterViewLocal, false, false)
	if err != nil {
		return nil, bizerr.WrapError(err, "get cluster failed")
	}
	if cluster == nil {
		return nil, bizerr.New(bizerr.ErrCodeUnknown, "cluster not found")
	}
	ctx = metadata.AppendToOutgoingMeta(ctx, &metadata.RequestMeta{
		OrgID:            cluster.OrganizationId,
		DefaultProjectID: cluster.ProjectId,
		ValidProjectList: []uint64{cluster.ProjectId},
		ClusterID:        request.GetClusterId(),
		UserID:           "0",
	})

	_, err = c.dedicatedCli.DeleteGKEHeterogeneousDeploy(ctx, &globaldedicatedv1.DeleteGKEHeterogeneousDeployRequest{
		ClusterId:     request.GetClusterId(),
		ComponentType: request.GetComponentType(),
		TidbGroupId:   request.GetTidbGroupId(),
		TenantId:      request.GetTenantId(),
		ProjectId:     request.GetProjectId(),
	})
	if err != nil {
		return nil, err
	}

	return &emptypb.Empty{}, nil
}

func cvtGKEHeterogeneousDeploy(in *globaldedicatedv1.GKEHeterogeneousDeploy) *tidbmgmtservicev1.GKEHeterogeneousDeploy {
	if in == nil {
		return nil
	}
	podCounts := make([]*globaldedicatedv1.GKEHeterogeneousDeployPodCount, 0, len(in.GetPodCounts()))
	for _, pc := range in.GetPodCounts() {
		podCounts = append(podCounts, &globaldedicatedv1.GKEHeterogeneousDeployPodCount{
			MachineType: pc.GetMachineType(),
			PodCount:    pc.GetPodCount(),
		})
	}
	return &tidbmgmtservicev1.GKEHeterogeneousDeploy{
		Enabled:                 in.GetEnabled(),
		ComponentType:           in.GetComponentType(),
		CurrentInstanceType:     in.GetCurrentInstanceType(),
		AlternativeInstanceType: in.GetAlternativeInstanceType(),
		TidbGroupId:             in.GetTidbGroupId(),
		Status:                  in.GetStatus(),
		PodCounts:               podCounts,
	}
}
