package spu

import (
	"errors"
	"fmt"

	globalcommonv1 "github.com/tidbcloud/tidb-management-service/api/spec/global/common/v1"
	globaldedicatedv1 "github.com/tidbcloud/tidb-management-service/api/spec/global/dedicated/v1"
	"github.com/tidbcloud/tidb-management-service/dedicated-global-service/domain/entity"
	"github.com/tidbcloud/tidb-management-service/utilities/bizerr"
)

type skuSelector struct {
	selectors map[globaldedicatedv1.Sku_Condition_Key][]*Selector
}

func newSkuSelector(sku *entity.Sku) (*skuSelector, error) {
	if sku.GetSpuName() == "" {
		return nil, errors.New("empty tier")
	}
	if sku.GetInstanceType() == "" {
		return nil, errors.New("empty instance type")
	}

	selectors := make(map[globaldedicatedv1.Sku_Condition_Key][]*Selector)

	for _, condition := range sku.Conditions {
		selector, err := NewSelector(condition.Key, condition.Operator, condition.Values)
		if err != nil {
			return nil, err
		}
		selectors[selector.key] = appendSelector(selectors, selector)
	}

	if sku.ComponentType == globaldedicatedv1.ComponentType_COMPONENT_TYPE_UNSPECIFIED {
		return nil, errors.New("unsupported component type")
	}

	tierSelector, err := NewSelector(
		globaldedicatedv1.Sku_Condition_TIER_ID,
		globaldedicatedv1.Sku_Condition_EQ,
		[]string{fmt.Sprintf("%d", sku.GetSpuId())},
	)
	if err != nil {
		return nil, err
	}
	selectors[tierSelector.key] = appendSelector(selectors, tierSelector)

	componentSelector, err := NewSelector(
		globaldedicatedv1.Sku_Condition_COMPONENT,
		globaldedicatedv1.Sku_Condition_EQ,
		[]string{sku.ComponentType.String()},
	)
	if err != nil {
		return nil, err
	}
	selectors[componentSelector.key] = appendSelector(selectors, componentSelector)

	cpuArchSelector, err := NewSelector(
		globaldedicatedv1.Sku_Condition_CPUARCH,
		globaldedicatedv1.Sku_Condition_EQ,
		[]string{sku.GetCpuArch().String()},
	)
	if err != nil {
		return nil, err
	}
	selectors[cpuArchSelector.key] = appendSelector(selectors, cpuArchSelector)

	providerSelector, err := NewSelector(
		globaldedicatedv1.Sku_Condition_PROVIDER,
		globaldedicatedv1.Sku_Condition_EQ,
		[]string{sku.GetCloudProvider().String()},
	)
	if err != nil {
		return nil, err
	}
	selectors[providerSelector.key] = appendSelector(selectors, providerSelector)

	if _, ok := selectors[globaldedicatedv1.Sku_Condition_TENANT]; !ok {
		tenantSelector, err := NewSelector(
			globaldedicatedv1.Sku_Condition_TENANT,
			globaldedicatedv1.Sku_Condition_EQ,
			[]string{"*"},
		)
		if err != nil {
			return nil, err
		}
		selectors[tenantSelector.key] = appendSelector(selectors, tenantSelector)
	}

	if _, ok := selectors[globaldedicatedv1.Sku_Condition_REGION]; !ok {
		regionSelector, err := NewSelector(
			globaldedicatedv1.Sku_Condition_REGION,
			globaldedicatedv1.Sku_Condition_EQ,
			[]string{"*"},
		)
		if err != nil {
			return nil, err
		}

		selectors[regionSelector.key] = appendSelector(selectors, regionSelector)
	}

	return &skuSelector{
		selectors: selectors,
	}, nil
}

func appendSelector(selectors map[globaldedicatedv1.Sku_Condition_Key][]*Selector, selector *Selector) []*Selector {
	out, ok := selectors[selector.key]
	if !ok {
		out = make([]*Selector, 0)
	}
	out = append(out, selector)

	return out
}

func (s *skuSelector) matches(labels Labels) bool {
	for key := range labels {
		selectors, ok := s.selectors[key]
		if !ok {
			return false
		}

		for idx := range selectors {
			if !selectors[idx].Matches(labels) {
				return false
			}
		}
	}

	return true
}

type componentLabelsBuilder struct {
	ls Labels
}

func newLabelBuilder() *componentLabelsBuilder {
	return &componentLabelsBuilder{
		ls: make(map[globaldedicatedv1.Sku_Condition_Key]string),
	}
}

func (l *componentLabelsBuilder) WithTenant(tenantId uint64) *componentLabelsBuilder {
	l.ls[globaldedicatedv1.Sku_Condition_TENANT] = fmt.Sprintf("%d", tenantId)
	return l
}

func (l *componentLabelsBuilder) WithProvider(provider globalcommonv1.CloudProvider_Name) *componentLabelsBuilder {
	l.ls[globaldedicatedv1.Sku_Condition_PROVIDER] = provider.String()
	return l
}

func (l *componentLabelsBuilder) WithRegion(region globalcommonv1.Region_Name) *componentLabelsBuilder {
	l.ls[globaldedicatedv1.Sku_Condition_REGION] = region.String()
	return l
}

func (l *componentLabelsBuilder) WithComponent(componentType globaldedicatedv1.ComponentType) *componentLabelsBuilder {
	l.ls[globaldedicatedv1.Sku_Condition_COMPONENT] = componentType.String()
	return l
}

func (l *componentLabelsBuilder) WithSpu(spuId uint64) *componentLabelsBuilder {
	l.ls[globaldedicatedv1.Sku_Condition_TIER_ID] = fmt.Sprintf("%d", spuId)
	return l
}

func (l *componentLabelsBuilder) WithCpuArch(archType globaldedicatedv1.InstanceMu_CpuArch) *componentLabelsBuilder {
	l.ls[globaldedicatedv1.Sku_Condition_CPUARCH] = archType.String()
	return l
}

func (l *componentLabelsBuilder) WithTotalTiKVCpu(totalCpuCores uint32) *componentLabelsBuilder {
	l.ls[globaldedicatedv1.Sku_Condition_TIKV_TOTAL_CPU] = fmt.Sprintf("%d", totalCpuCores)
	return l
}

func (l *componentLabelsBuilder) WithTiKVSku(tikvSkuKey string) *componentLabelsBuilder {
	l.ls[globaldedicatedv1.Sku_Condition_ATTACH_TIERUNIT] = tikvSkuKey
	return l
}

func (l *componentLabelsBuilder) Build() (Labels, bizerr.BizError) {
	if l.ls.Get(globaldedicatedv1.Sku_Condition_TENANT) == "" {
		return nil, bizerr.New(bizerr.ErrCodeParams, "missing tenant parameter")
	}

	if l.ls.Get(globaldedicatedv1.Sku_Condition_PROVIDER) == "" {
		return nil, bizerr.New(bizerr.ErrCodeParams, "missing provider parameter")
	}

	if l.ls.Get(globaldedicatedv1.Sku_Condition_REGION) == "" {
		return nil, bizerr.New(bizerr.ErrCodeParams, "missing region parameter")
	}

	// v, err := enum.FromString[globaldedicatedv1.ComponentType](l.ls.Get(globaldedicatedv1.Sku_Condition_COMPONENT))
	// if err != nil {
	//	return nil, bizerr.WrapError(err, "invalid component parameter")
	// }
	// if globaldedicatedv1.ComponentType(v) == globaldedicatedv1.ComponentType_COMPONENT_TYPE_UNSPECIFIED {
	//	return nil, bizerr.New(bizerr.ErrCodeParams, "missing component parameter")
	// }

	return l.ls, nil
}
