package query

import (
	"context"
	"fmt"

	"github.com/shopspring/decimal"
	"github.com/tidbcloud/serverless-metering/pkg/config"
	"github.com/tidbcloud/serverless-metering/pkg/repository"
)

type MetricType string

func (mt MetricType) String() string {
	return string(mt)
}

const (
	MetricTypeTikvStorage       MetricType = "tikv_storage"
	MetricTypeTikvStorageIA     MetricType = "tikv_storage_ia"
	MetricTypeTiflashStorage    MetricType = "tiflash_storage"
	MetricTypeTidbRU            MetricType = "tidb_ru"
	MetricTypeTidbRUV2          MetricType = "tidb_ru_v2"
	MetricTypeLoadDataRU        MetricType = "load_data_ru"
	MetricTypeTiflashRU         MetricType = "tiflash_ru"
	MetricTypeTiflashRUV2       MetricType = "tiflash_ru_v2"
	MetricTypePublicEgress      MetricType = "public_egress"
	MetricTypePrivatelinkEgress MetricType = "privatelink_egress"
	MetricTypeRCU               MetricType = "rcu"
)

var (
	KiB                  = int64(1024)
	MiB                  = KiB * 1024
	KiDe                 = decimal.NewFromInt(KiB)
	MiDe                 = decimal.NewFromInt(MiB)
	NetworkRU            = decimal.NewFromInt(1024)     // 1 network RU = 1 KiB
	PrivatelinkNetworkRU = decimal.NewFromInt(1024 * 4) // 1 network RU = 4 KiB
)

type Usage struct {
	KeyspaceId string     `json:"k"`
	PrimaryId  string     `json:"-"`
	Value      int64      `json:"v"`
	MetricType MetricType `json:"-"`

	clusterBranch *repository.ClusterBranch
}

func (u *Usage) FillCluster(ctx context.Context, repo repository.ClusterBranchRepo) error {
	var clusterBranch *repository.ClusterBranch
	var err error
	switch u.MetricType {
	case MetricTypePublicEgress, MetricTypePrivatelinkEgress:
		clusterBranch, err = repo.GetClusterBranchByPrimaryID(ctx, u.PrimaryId)
	default:
		clusterBranch, err = repo.GetClusterBranchByKeyspaceID(ctx, u.KeyspaceId)
	}
	if err != nil {
		return err
	}

	if !clusterBranch.IsEssentialPlan() && u.MetricType == MetricTypeRCU {
		return fmt.Errorf("cluster not found, skip rcu for starter plan")
	} else if clusterBranch.IsEssentialPlan() && u.IsStarterRU() {
		return fmt.Errorf("cluster not found, skip tidb/tiflash ru for essential plan")
	}

	u.clusterBranch = clusterBranch
	return nil
}

func (u *Usage) GetCost() int64 {
	var cost int64
	switch u.MetricType {
	case MetricTypeTikvStorage, MetricTypeTikvStorageIA, MetricTypeTiflashStorage:
		storageSizeMiDe := decimal.NewFromInt(u.Value).Div(MiDe)
		cost = storageSizeMiDe.IntPart()
	case MetricTypePublicEgress:
		trafficBytesKiDe := decimal.NewFromInt(u.Value).Div(NetworkRU)
		cost = trafficBytesKiDe.RoundUp(1).IntPart()
	case MetricTypePrivatelinkEgress:
		trafficBytesKiDe := decimal.NewFromInt(u.Value).Div(PrivatelinkNetworkRU)
		cost = trafficBytesKiDe.RoundUp(1).IntPart()
	default:
		cost = u.Value
	}
	// convert ru to rcu for essential plan
	if u.clusterBranch.IsEssentialPlan() && (u.MetricType == MetricTypeLoadDataRU || u.MetricType == MetricTypePublicEgress || u.MetricType == MetricTypePrivatelinkEgress) {
		cost = cost / 60
	}
	return cost
}

func (u *Usage) GetSkuName(cloudProvider, region string) string {
	plan := "starter"
	if u.clusterBranch.IsEssentialPlan() {
		plan = "essential"
	}
	switch u.MetricType {
	case MetricTypeTikvStorage:
		if u.clusterBranch.IsEncryptKeyspace() {
			return fmt.Sprintf("serverless-row-storage-dual-encryption:%s:%s:%s", cloudProvider, region, plan)
		}
		return fmt.Sprintf("serverless-row-storage:%s:%s:%s", cloudProvider, region, plan)
	case MetricTypeTikvStorageIA:
		if config.Global().EnableIASku {
			return fmt.Sprintf("serverless-row-storage-ia:%s:%s:%s", cloudProvider, region, plan)
		}
		if u.clusterBranch.IsEncryptKeyspace() {
			return fmt.Sprintf("serverless-row-storage-dual-encryption:%s:%s:%s", cloudProvider, region, plan)
		}
		return fmt.Sprintf("serverless-row-storage:%s:%s:%s", cloudProvider, region, plan)
	case MetricTypeTiflashStorage:
		if u.clusterBranch.IsEncryptKeyspace() {
			return fmt.Sprintf("serverless-column-storage-dual-encryption:%s:%s:%s", cloudProvider, region, plan)
		}
		return fmt.Sprintf("serverless-column-storage:%s:%s:%s", cloudProvider, region, plan)
	case MetricTypeTidbRU, MetricTypeTidbRUV2, MetricTypeTiflashRU, MetricTypeTiflashRUV2, MetricTypeLoadDataRU, MetricTypePublicEgress, MetricTypePrivatelinkEgress:
		// convert ru to rcu for essential plan
		if u.clusterBranch.IsEssentialPlan() {
			return fmt.Sprintf("serverless-request-capacity-unit:%s:%s:%s", cloudProvider, region, plan)
		}
		return fmt.Sprintf("serverless-request-unit:%s:%s:%s", cloudProvider, region, plan)
	case MetricTypeRCU:
		return fmt.Sprintf("serverless-request-capacity-unit:%s:%s:%s", cloudProvider, region, plan)
	default:
		return ""
	}
}

func (u *Usage) GetMetaCluster() *repository.ClusterBranch {
	return u.clusterBranch
}

func (u *Usage) GetMetricTypeString() string {
	return u.MetricType.String()
}

func (u *Usage) IsTiflashRU() bool {
	return u.MetricType == MetricTypeTiflashRU || u.MetricType == MetricTypeTiflashRUV2
}

func (u *Usage) String() string {
	return fmt.Sprintf("primaryId:%s, keyspaceId:%s, value:%d, mt:%s", u.PrimaryId, u.KeyspaceId, u.Value, u.MetricType)
}

func (u *Usage) IsRu() bool {
	switch u.MetricType {
	case MetricTypeTidbRU, MetricTypeTidbRUV2, MetricTypeTiflashRU, MetricTypeTiflashRUV2, MetricTypeLoadDataRU, MetricTypePublicEgress, MetricTypePrivatelinkEgress:
		return true
	default:
		return false
	}
}

func (u *Usage) IsStarterRU() bool {
	switch u.MetricType {
	case MetricTypeTidbRU, MetricTypeTidbRUV2, MetricTypeTiflashRU, MetricTypeTiflashRUV2:
		return true
	default:
		return false
	}
}

func (mt MetricType) IsRUV2() bool {
	return mt == MetricTypeTidbRUV2 || mt == MetricTypeTiflashRUV2
}

// ExecCount is calculated from tidb_executor_statement_total, indicates statement execution count of each database.
// format: {cluster_id: {db_name: count}}
type ExecCount map[string]map[string]int64

func (ec ExecCount) GetExecCountOfEachDB(clusterId string) map[string]int64 {
	return ec[clusterId]
}
