package costexplorer

import (
	"context"
	"encoding/json"
	"github.com/pingcap/dbaas/billing2/config"
	"github.com/pingcap/dbaas/billing2/handler/tidbmgmtsvchandler"
	"strings"

	"github.com/pingcap/dbaas/billing2/db"
	"github.com/pingcap/dbaas/billing2/forms"
	"github.com/pingcap/dbaas/billing2/handler/centralhandler"
	"github.com/pingcap/dbaas/billing2/handler/infrahandler"
	"github.com/pingcap/dbaas/billing2/utils/log"
	"go.uber.org/zap"
)

const DefaultServiceName = "Others"

func FormatServiceName(in *forms.CostExplorerService) string {
	var service []string
	for _, tier := range []*string{in.Product, in.Category, in.Component} {
		if tier == nil {
			break
		}
		service = append(service, *tier)
	}
	if len(service) == 0 {
		return DefaultServiceName
	}
	return strings.Join(service, " - ")
}

func GetCostExplorerServices() ([]*forms.CostExplorerService, error) {
	sql := "SELECT DISTINCT product, category, component, t1.order_map from ( " +
		"SELECT DISTINCT" +
		" category_item_shows.`show_kind_name` as product," +
		" IF(LENGTH(category_item_shows.`show_category_name`) > 0, category_item_shows.`show_category_name`, NULL) as category," +
		" IF(LENGTH(category_item_shows.`show_category_name`) > 0, " +
		"    IF(category_item_shows.`use_cate_gory`, " +
		"       IF(LENGTH(category_item_shows.`label_key`) > 0 ," +
		"          JSON_ARRAY(JSON_EXTRACT(category_item_shows.`labels`, concat('$.\"', category_item_shows.`label_key`, '\"')))," +
		"          '[null]'" +
		"       )," +
		"       JSON_EXTRACT(category_item_shows.`labels`, '$.*')" +
		"    )," +
		"    '[null]'" +
		"  ) as components," +
		"  order_map" +
		"   FROM category_item_shows)" +
		"  t1, JSON_TABLE(t1.components, '$[*]' COLUMNS (`component` VARCHAR(255) PATH '$')) t2 " +
		"  ORDER BY" +
		"    JSON_EXTRACT(order_map, concat('$.\"', product, '\"'))," +
		"    JSON_EXTRACT(order_map, concat('$.\"', category, '\"'))," +
		"    JSON_EXTRACT(order_map, concat('$.\"', component, '\"'))"
	log.Info("retrieve services", zap.String("sql", sql))
	var queryRes []*forms.CostExplorerService
	err := db.GetDB().Raw(sql).Scan(&queryRes).Error
	if err != nil {
		return nil, err
	}
	for i := 0; i != len(queryRes); i++ {
		svcName := FormatServiceName(queryRes[i])
		queryRes[i].Service = &svcName
	}
	return queryRes, nil
	// todo tree code, may be used in the future

	//var ret []*forms.Service
	//nameIndexMap := make([]map[string]int, len(depthMap))
	//for i := 0; i != len(depthMap); i++ {
	//	nameIndexMap[i] = make(map[string]int)
	//}
	//for _, r := range queryRes {
	//	nameKey := ""
	//	tier := &ret
	//	for depth, name := range []*string{&r.Service, r.Category, r.Component} {
	//		if name == nil {
	//			break
	//		}
	//		nameKey += *name
	//		index, ok := nameIndexMap[depth][nameKey]
	//		if !ok {
	//			index = len(*tier)
	//			nameIndexMap[depth][nameKey] = index
	//			*tier = append(*tier, &forms.Service{TierName: depthMap[depth], Depth: depth, Name: *name})
	//		}
	//		tier = &(*tier)[index].Children
	//	}
	//}
	//return ret, nil
}

func GetCostExplorerProviders() ([]*forms.Provider, error) {
	sql := "select provider, JSON_ARRAYAGG(region) as regions from " +
		"(select provider, region from units where sku_type='normal' and lower(region) !='all' and provider !='' and lower(provider) !='pingcap' and region !='' " +
		"group by provider, region order by provider, region) t group by provider;"
	log.Info("retrieve providers", zap.String("sql", sql))
	var dbOutput []struct {
		Provider string
		Regions  string
	}
	err := db.GetDB().Raw(sql).Scan(&dbOutput).Error
	if err != nil {
		return nil, err
	}
	providers := make([]*forms.Provider, len(dbOutput)+1)
	for i, o := range dbOutput {
		providers[i] = &forms.Provider{
			Name: o.Provider,
		}
		err = json.Unmarshal([]byte(o.Regions), &providers[i].Regions)
		if err != nil {
			return nil, err
		}
		providers[i].Regions = append(providers[i].Regions, OthersRegion)
	}
	providers[len(dbOutput)] = &forms.Provider{
		Name:    "PingCAP",
		Regions: []string{OthersRegion},
	}
	return providers, nil
}

func GetCostExplorerProjects(tenantID uint) ([]*forms.Project, error) {
	handler := centralhandler.GetCentralHandler()
	return handler.ListProjects(tenantID, true)
}

func GetCostExplorerClusters(tenantID uint) ([]*forms.Cluster, error) {
	if config.Get().GetBool(config.EnableUseMgmtGetClusterInfo) {
		mgmtClient, err := tidbmgmtsvchandler.Get()
		if err != nil {
			log.Error("fail to GetCostExplorerClusters get mgmt client error", zap.Uint("tenantID", tenantID), zap.Error(err))
			return nil, err
		}
		return mgmtClient.GetAllClustersByTenantID(context.Background(), uint64(tenantID))

	} else {
		return infrahandler.Get().ListClusters(tenantID)
	}

}

func GetCostExplorerCustomers(tenantID uint) ([]*forms.Customer, error) {
	handler := centralhandler.GetCentralHandler()
	return handler.ListPartnerCustomers(tenantID)
}
