package cluster

import (
	"context"
	"fmt"
	"strings"

	globalcommonv1 "github.com/tidbcloud/tidb-management-service/api/spec/global/common/v1"
	globalngv1 "github.com/tidbcloud/tidb-management-service/api/spec/global/nextgen/v1"
	"github.com/tidbcloud/tidb-management-service/nextgen-global-service/domain/adapter/billing"
	regionrepo "github.com/tidbcloud/tidb-management-service/nextgen-global-service/domain/repository/region"
	"github.com/tidbcloud/tidb-management-service/nextgen-global-service/domain/service/rcuquota"
	"github.com/tidbcloud/tidb-management-service/utilities/bizerr"
	"github.com/tidbcloud/tidb-management-service/utilities/enum"
)

func buildPremiumRCUSKU(cloudProvider, region string) string {
	return fmt.Sprintf("rcu:%s:%s:premium:all:total", cloudProvider, region)
}

func buildPremiumReservedVcpuSKU(cloudProvider, region string) string {
	return fmt.Sprintf("vcpu:%s:%s:reserved:all", cloudProvider, region)
}

func (s *serviceImpl) EstimatePrice(
	ctx context.Context,
	req *globalngv1.EstimatePriceRequest,
) (*globalngv1.EstimatePriceResponse, bizerr.BizError) {
	if req == nil {
		return nil, bizerr.New(bizerr.ErrCodeParams, "request cannot be nil")
	}
	if req.GetOrganizationId() == 0 {
		return nil, bizerr.New(bizerr.ErrCodeParams, "organization_id is required")
	}
	if req.GetRegionId() == globalcommonv1.Region_NAME_UNSPECIFIED {
		return nil, bizerr.New(bizerr.ErrCodeParams, "region_id is required")
	}
	if req.GetServicePlan() == globalcommonv1.ServicePlan_SERVICE_PLAN_UNSPECIFIED {
		return nil, bizerr.New(bizerr.ErrCodeParams, "service_plan is required")
	}

	if req.GetServicePlan() == globalcommonv1.ServicePlan_Premium && req.GetMaxRcu() <= 0 {
		return nil, bizerr.New(bizerr.ErrCodeParams, "max_rcu must be greater than 0 for premium")
	}

	region, err := s.regionRepo.SelectRegion(ctx, regionrepo.Filter{
		RegionId:    req.GetRegionId(),
		ServicePlan: req.GetServicePlan(),
		TenantId:    req.GetOrganizationId(),
	})
	if err != nil {
		return nil, bizerr.WrapError(err, "load region failed")
	}
	provider := strings.ToLower(enum.ToString(region.GetCloudProviderId()))
	regionName := enum.ToString(region.GetId())

	var (
		sku             string
		cost            int64
		billingCalcCost int64 = 1
	)
	switch req.GetServicePlan() {
	case globalcommonv1.ServicePlan_Premium:
		rcuQuota, loadErr := s.rcuQuotaRepo.Load(ctx, req.GetOrganizationId(), req.GetServicePlan().String())
		if loadErr != nil {
			return nil, bizerr.WrapError(loadErr, "load rcu quota failed")
		}
		sku = buildPremiumRCUSKU(provider, regionName)
		cost = rcuquota.CalculateMinRcuByQuota(req.GetMaxRcu(), rcuQuota)
	case globalcommonv1.ServicePlan_Premium_Reserved:
		if req.GetReservedCapacityTemplateId() == 0 {
			return nil, bizerr.New(bizerr.ErrCodeParams, "reserved_capacity_template_id is required for premium reserved")
		}
		template, loadErr := s.reservedCapacityTemplateRepo.Load(ctx, req.GetReservedCapacityTemplateId(), false)
		if loadErr != nil {
			return nil, bizerr.WrapError(loadErr, "load reserved capacity template failed")
		}
		if template.GetStatus() != globalngv1.ReservedCapacityTemplate_ACTIVE {
			return nil, bizerr.New(bizerr.ErrCodeRecordNotFound, "unmatched reserved capacity template")
		}
		if template.GetScope() == globalngv1.ReservedCapacityTemplate_ORG_PRIVATE && template.GetOrganizationId() != req.GetOrganizationId() {
			return nil, bizerr.New(bizerr.ErrCodeRecordNotFound, "unmatched reserved capacity template")
		}
		if template.GetScope() != globalngv1.ReservedCapacityTemplate_DEFAULT &&
			template.GetScope() != globalngv1.ReservedCapacityTemplate_ORG_PRIVATE {
			return nil, bizerr.New(bizerr.ErrCodeRecordNotFound, "unmatched reserved capacity template")
		}
		sku = buildPremiumReservedVcpuSKU(provider, regionName)
		cost = template.GetPurchasedVcpu()
	default:
		return nil, bizerr.New(bizerr.ErrCodeParams, "service_plan is not supported for estimate_price")
	}

	if sku == "" {
		return nil, bizerr.New(bizerr.ErrCodeUnknown, "sku is not configured")
	}

	priceMap, err := s.billingCli.CalcFee(ctx, []*billing.Report{{
		TenantID: req.GetOrganizationId(),
		Cost:     billingCalcCost,
		Sku:      sku,
		Labels: map[string]string{
			"intervalMin":   "1",
			"intervalMax":   "1",
			"intervalValue": "1",
		},
	}})
	if err != nil {
		return nil, bizerr.WrapError(err, "calculate fee failed")
	}
	charge, ok := priceMap[sku]
	if !ok || charge == nil {
		return nil, bizerr.New(bizerr.ErrCodeUnknown, "sku charge not found")
	}
	rcuPricePerMin := charge.RawPrice
	rcuCostHourly := rcuPricePerMin * float64(cost) * 60
	rcuCostMonthly := rcuCostHourly * 730

	return &globalngv1.EstimatePriceResponse{
		CloudProvider:  provider,
		Region:         regionName,
		Rcu:            cost,
		RcuCostHourly:  fmt.Sprintf("%.2f", rcuCostHourly),
		RcuCostMonthly: fmt.Sprintf("%.2f", rcuCostMonthly),
	}, nil
}
