package region

import (
	"strconv"

	commonv1 "github.com/tidbcloud/tidb-management-service/api/spec/global/common/v1"
	nextgenv1 "github.com/tidbcloud/tidb-management-service/api/spec/global/nextgen/v1"
	mgmtv1 "github.com/tidbcloud/tidb-management-service/api/spec/tidb_mgmt_service/v1"
	"go.uber.org/dig"

	regionapi "github.com/tidbcloud/devops/api/region"
	ctrl "github.com/tidbcloud/devops/pkg/controller"
	"github.com/tidbcloud/devops/pkg/infra/clients/coresvccli"
	"github.com/tidbcloud/devops/pkg/infra/config"
	opsvc "github.com/tidbcloud/devops/pkg/service/operation"
)

type ControllerParam struct {
	dig.In

	Conf             *config.RootConfig
	CoreSvcCli       *coresvccli.CoreSvcCli
	OperationService opsvc.OperationServiceAPI
}

type RegionController struct {
	coresvc *coresvccli.CoreSvcCli
	opSvc   opsvc.OperationServiceAPI
}

func (c *RegionController) Name() string {
	return "RegionController"
}

func NewRegionController(p ControllerParam) ctrl.Controller {
	return &RegionController{
		coresvc: p.CoreSvcCli,
		opSvc:   p.OperationService,
	}
}

func (c *RegionController) RegisterAPI(suite *ctrl.Suite) error {
	r := suite.Router
	root := r.Group("/region")
	{
		rg := root.Group("/regions")
		rg.GET("/list", c.ListRegions)
		rg.GET("/get", c.GetRegion)
		rg.POST("/register", c.RegisterRegion)
		rg.POST("/update", c.UpdateRegion)
		rg.POST("/provider-identities", c.RegisterRegionProviderIdentity)
	}
	return nil
}

type RegionConverter struct{}

func (rc RegionConverter) RegionFromPB(pb *mgmtv1.Region) *regionapi.Region {
	if pb == nil {
		return nil
	}

	identities := make([]*regionapi.RegionProviderIdentity, 0, len(pb.RegionProviderIdentities))
	for _, id := range pb.RegionProviderIdentities {
		identities = append(identities, rc.RegionProviderIdentityFromPB(id))
	}

	defaultIdentities := make([]*regionapi.DefaultProviderIdentity, 0, len(pb.DefaultProviderIdentities))
	for _, id := range pb.DefaultProviderIdentities {
		defaultIdentities = append(defaultIdentities, rc.DefaultProviderIdentityFromPB(id))
	}

	return &regionapi.Region{
		Region:                    rc.RegionSpecFromPB(pb.Region),
		DeployType:                mgmtv1.DeployType_name[int32(pb.DeployType)],
		RegionProviderIdentities:  identities,
		DefaultProviderIdentities: defaultIdentities,
	}
}

func (rc RegionConverter) RegionSpecFromPB(pb *commonv1.Region) *regionapi.RegionSpec {
	if pb == nil {
		return nil
	}

	openedTenantIds := make([]string, 0, len(pb.OpenedTenantIds))
	for _, id := range pb.OpenedTenantIds {
		openedTenantIds = append(openedTenantIds, strconv.FormatUint(id, 10))
	}

	regionWhiteLists := make([]*regionapi.RegionWhiteList, 0, len(pb.GetRegionWhiteLists()))
	for _, item := range pb.GetRegionWhiteLists() {
		if item == nil {
			continue
		}
		itemTenantIDs := make([]string, 0, len(item.GetOpenedTenantIds()))
		for _, tenantID := range item.GetOpenedTenantIds() {
			itemTenantIDs = append(itemTenantIDs, strconv.FormatUint(tenantID, 10))
		}
		regionWhiteLists = append(regionWhiteLists, &regionapi.RegionWhiteList{
			ServicePlan:     item.GetServicePlan().String(),
			IsPublic:        item.GetIsPublic(),
			OpenedTenantIds: itemTenantIDs,
		})
	}

	return &regionapi.RegionSpec{
		Id:                 commonv1.Region_Name_name[int32(pb.Id)],
		CloudProviderId:    commonv1.CloudProvider_Name_name[int32(pb.CloudProviderId)],
		DisplayName:        pb.DisplayName,
		OpenedTenantIds:    openedTenantIds,
		RegionWhiteLists:   regionWhiteLists,
		SortPriority:       pb.SortPriority,
		Area:               pb.Area,
		CountryCode:        pb.CountryCode,
		State:              commonv1.Region_State_name[int32(pb.State)],
		IsPublic:           pb.IsPublic,
		DefaultBackupTime:  pb.DefaultBackupTime,
		ServiceAddress:     pb.ServiceAddress,
		LogicalName:        pb.LogicalName,
		ServicePlan:        primaryServicePlan(pb).String(),
		SharedBucketUrl:    pb.SharedBucketUrl,
		OpsAuditLogSetting: rc.RegionOpsAuditLogSettingFromPB(pb.OpsAuditLogSetting),
	}
}

func primaryServicePlan(pb *commonv1.Region) commonv1.ServicePlan {
	if pb == nil {
		return commonv1.ServicePlan_SERVICE_PLAN_UNSPECIFIED
	}
	if len(pb.GetRegionWhiteLists()) > 0 {
		return pb.GetRegionWhiteLists()[0].GetServicePlan()
	}
	return pb.GetServicePlan()
}

func (rc RegionConverter) RegionProviderIdentityFromPB(pb *nextgenv1.RegionProviderIdentity) *regionapi.RegionProviderIdentity {
	if pb == nil {
		return nil
	}
	return &regionapi.RegionProviderIdentity{
		Id:        pb.Id,
		Identity:  pb.Identity,
		IsDefault: pb.IsDefault,
	}
}

func (rc RegionConverter) DefaultProviderIdentityFromPB(pb *nextgenv1.DefaultRegionProviderIdentity) *regionapi.DefaultProviderIdentity {
	if pb == nil {
		return nil
	}
	return &regionapi.DefaultProviderIdentity{
		Id:          pb.Id,
		TenantID:    strconv.FormatUint(pb.TenantId, 10),
		Identity:    pb.Identity,
		ServicePlan: pb.ServicePlan.String(),
	}
}

func (rc RegionConverter) RegionOpsAuditLogSettingFromPB(pb *commonv1.RegionOpsAuditLogSetting) *regionapi.RegionOpsAuditLogSetting {
	if pb == nil {
		return nil
	}
	return &regionapi.RegionOpsAuditLogSetting{
		Enabled:     pb.Enabled,
		LogLocation: rc.RegionOpsAuditLogSettingLogLocationFromPB(pb.LogLocation),
		LogRotation: rc.RegionOpsAuditLogSettingLogRotationFromPB(pb.LogRotation),
	}
}

func (rc RegionConverter) RegionOpsAuditLogSettingLogLocationFromPB(pb *commonv1.RegionOpsAuditLogSetting_LogLocation) *regionapi.RegionOpsAuditLogSettingLogLocation {
	if pb == nil {
		return nil
	}
	return &regionapi.RegionOpsAuditLogSettingLogLocation{
		Type:       pb.Type.String(),
		BucketUri:  pb.BucketUri,
		RoleArn:    pb.RoleArn,
		ExternalId: pb.ExternalId,
		AccountId:  pb.AccountId,
	}
}

func (rc RegionConverter) RegionOpsAuditLogSettingLogRotationFromPB(pb *commonv1.RegionOpsAuditLogSetting_LogRotation) *regionapi.RegionOpsAuditLogSettingLogRotation {
	if pb == nil {
		return nil
	}
	return &regionapi.RegionOpsAuditLogSettingLogRotation{
		FileSizeLimitInMb:     pb.FileSizeLimitInMb,
		RotationIntervalInMin: pb.RotationIntervalInMin,
	}
}
