package service

// import (
// 	"context"
// 	"fmt"
// 	"math"
// 	"sort"
// 	"strings"

// 	"github.com/duke-git/lancet/v2/slice"
// 	"github.com/pingcap-inc/clinic/clinic_data_service/pkg/common"
// 	"github.com/pingcap-inc/clinic/clinic_data_service/pkg/config"
// 	"github.com/pingcap-inc/clinic/clinic_data_service/pkg/dto"
// 	"github.com/pingcap-inc/clinic/clinic_data_service/pkg/log"
// 	"github.com/pingcap-inc/clinic/clinic_data_service/pkg/model"
// 	"github.com/pingcap-inc/clinic/clinic_data_service/pkg/prometheus"
// 	"github.com/pingcap-inc/clinic/clinic_data_service/pkg/thirdapi/feishu"
// 	"github.com/pingcap-inc/clinic/clinic_data_service/pkg/utils"
// 	"go.uber.org/zap"
// )

// const (
// 	Top20            = 50
// 	DiffCntThreshold = 50.0
// 	DiffCpuThreshold = 50.0
// 	TagNameCpuGT50   = "cpu_gt_50"
// 	TagNameCntGT50   = "cnt_gt_50"
// 	TagNameNewTopSql = "new_topsql"
// )

// var (
// 	tagCpuGT50   = dto.TopSqlTag{Name: TagNameCpuGT50, Desc: "cpuDiff>50%"}
// 	tagCntGT50   = dto.TopSqlTag{Name: TagNameCntGT50, Desc: "cntDiff>50%"}
// 	tagNewTopSql = dto.TopSqlTag{Name: TagNameNewTopSql, Desc: "new top sql"}
// )

// type TopSqlTop20Service struct {
// 	Day         string
// 	commonDAO   common.CommonDAO
// 	clusterData map[string]model.ClusterData
// }

// func NewTopSqlTop20Service(day ...string) *TopSqlTop20Service {
// 	s := &TopSqlTop20Service{
// 		commonDAO:   common.NewCommonDAO(),
// 		clusterData: make(map[string]model.ClusterData),
// 	}
// 	if len(day) > 0 {
// 		s.Day = day[0]
// 	}
// 	return s
// }

// func (s *TopSqlTop20Service) GenerateOrgTop20(ctx context.Context) {
// 	orgs := config.OrgsConfig.Orgs
// 	for _, org := range orgs {
// 		s.processOrg(ctx, org)
// 	}
// }

// func (s *TopSqlTop20Service) processOrg(ctx context.Context, org dto.Org) {
// 	log.PlainInfo("start to process org", zap.String("orgId", org.OrgID))

// 	// 1. get top 20 digests for each cluster and save to db
// 	for _, cluster := range org.Clusters {
// 		s.saveTop20Digests(ctx, cluster.ClusterID)
// 	}

// 	// 2. get all digests for the org and calculate total count and cpu
// 	mergedDigests, err := s.getAndMergeTopDigests(ctx, org)
// 	if err != nil {
// 		log.PlainError("getAndMergeTopDigests failed", zap.String("orgId", org.OrgID), zap.Error(err))
// 		return
// 	}

// 	// 3. calculate diff and save to db
// 	err = s.calculateAndSaveTopDigests(ctx, org.OrgID, mergedDigests)
// 	if err != nil {
// 		log.PlainError("calculateAndSaveTopDigests failed", zap.String("orgId", org.OrgID), zap.Error(err))
// 		return
// 	}

// 	// // 4. generate feishu report
// 	// err = s.GenerateFeishuReport(ctx, org)
// 	// if err != nil {
// 	// 	log.PlainError("generateFeishuReport failed", zap.String("orgId", org.OrgID), zap.Error(err))
// 	// 	return
// 	// }
// 	// log.PlainInfo("finish to process org", zap.String("orgId", org.OrgID))
// }

// func (s *TopSqlTop20Service) saveTop20Digests(ctx context.Context, clusterId string) {
// 	orgId := config.GetOrgByClusterID(clusterId).OrgID
// 	// 1.get cluster data
// 	clusterData, err := s.getClusterData(ctx, clusterId)
// 	if err != nil {
// 		log.PlainError("getClusterData failed", zap.String("clusterId", clusterId), zap.Error(err))
// 		return
// 	}
// 	// 2. get top 20 count sql
// 	topCntResult, err := prometheus.GetTopXCountInfo2(clusterId, s.Day, Top20)
// 	if err != nil {
// 		log.PlainError("GetTopXCountInfo failed", zap.String("day", s.Day), zap.Error(err))
// 	}
// 	if len(topCntResult.TopSqls) > 0 {
// 		var digests []model.Digest
// 		for i, item := range topCntResult.TopSqls {
// 			digests = append(digests, model.Digest{
// 				Day:       s.Day,
// 				ClusterId: clusterId,
// 				OrgId:     orgId,
// 				Source:    "count",
// 				Digest:    item.Digest,
// 				Sql:       item.Sql,
// 				Top:       i + 1,
// 				Count:     item.Count,
// 				Cpu:       item.TotalCpu,
// 				CpuPer:    math.Round(item.TotalCpu*100*1000/clusterData.Cpu) / 1000,
// 			})
// 		}
// 		s.replaceDigests(ctx, clusterId, "count", &digests)
// 	}

// 	// 3. get top 20 cpu sql
// 	topCpuResult, err := prometheus.GetTopXCPUInfo2(clusterId, s.Day, Top20)
// 	if err != nil {
// 		log.PlainError("GetTopXCPUInfo failed", zap.String("day", s.Day), zap.Error(err))
// 		return
// 	}
// 	if len(topCpuResult.TopSqls) > 0 {
// 		var digests []model.Digest
// 		for i, item := range topCpuResult.TopSqls {
// 			digests = append(digests, model.Digest{
// 				Day:       s.Day,
// 				ClusterId: clusterId,
// 				OrgId:     orgId,
// 				Source:    "cpu",
// 				Digest:    item.Digest,
// 				Sql:       item.Sql,
// 				Top:       i + 1,
// 				Count:     item.Count,
// 				Cpu:       item.TotalCpu,
// 				CpuPer:    math.Round(item.TotalCpu*100*1000/clusterData.Cpu) / 1000,
// 			})
// 		}
// 		s.replaceDigests(ctx, clusterId, "cpu", &digests)
// 	}
// }

// func (s *TopSqlTop20Service) getClusterData(ctx context.Context, clusterId string) (*model.ClusterData, error) {
// 	orgId := config.GetOrgByClusterID(clusterId).OrgID
// 	// 1. query cluster cpu
// 	queryTime := prometheus.GetDayQueryTime(s.Day)
// 	interval := "1d"
// 	allCpuDay, err := prometheus.GetAllSQLTotalCPUTime(clusterId, interval, queryTime, prometheus.CpuTimeUnitHour)
// 	if err != nil {
// 		log.PlainError("GetAllSQLTotalCPUTime failed", zap.String("clusterId", clusterId), zap.Error(err))
// 		return nil, err
// 	}
// 	latencyDay, err := prometheus.GetClusterLatency(clusterId, 0.999, queryTime, interval)
// 	if err != nil {
// 		log.PlainError("GetLatency failed", zap.String("clusterId", clusterId), zap.Error(err))
// 		return nil, err
// 	}

// 	clusterData := &model.ClusterData{
// 		Day:       s.Day,
// 		OrgId:     orgId,
// 		ClusterId: clusterId,
// 		Cpu:       math.Round(allCpuDay),
// 		Latency:   math.Round(latencyDay),
// 	}
// 	// 2. save cluster data
// 	// delete old data
// 	params := common.ComFilter{
// 		ModelFilters: model.ClusterData{
// 			Day:       s.Day,
// 			OrgId:     orgId,
// 			ClusterId: clusterId,
// 		},
// 	}
// 	err = s.commonDAO.DeleteEntity(ctx, params, &model.ClusterData{})
// 	if err != nil {
// 		log.PlainError("delete cluster data failed", zap.String("clusterId", clusterId), zap.Error(err))
// 		return nil, err
// 	}
// 	err = s.commonDAO.CreateEntity(ctx, clusterData)
// 	if err != nil {
// 		log.PlainError("create cluster data failed", zap.String("clusterId", clusterId), zap.Error(err))
// 		return nil, err
// 	}
// 	s.clusterData[clusterId] = *clusterData
// 	return clusterData, nil
// }

// func (s *TopSqlTop20Service) getAndMergeTopDigests(ctx context.Context, org dto.Org) (map[string]*model.TopDigest, error) {
// 	var clusterIDs []string
// 	for _, cluster := range org.Clusters {
// 		clusterIDs = append(clusterIDs, cluster.ClusterID)
// 	}

// 	var digests []model.Digest
// 	params := common.ListParams{
// 		AdvancedFilters: map[string]any{
// 			"cluster_id IN (?)": clusterIDs,
// 			"day = ?":           s.Day,
// 			"org_id = ?":        org.OrgID,
// 		},
// 		Orders: []string{"count DESC", "cpu DESC"},
// 	}
// 	err := s.commonDAO.ListEntities(ctx, params, &digests)
// 	if err != nil {
// 		return nil, err
// 	}

// 	mergedDigests := make(map[string]*model.TopDigest)
// 	for _, d := range digests {
// 		mapKey := d.ClusterId + d.Digest
// 		if _, ok := mergedDigests[mapKey]; !ok {
// 			mergedDigests[mapKey] = &model.TopDigest{
// 				Day:       s.Day,
// 				ClusterId: d.ClusterId,
// 				OrgId:     d.OrgId,
// 				Digest:    d.Digest,
// 				Sql:       d.Sql,
// 				Count:     d.Count,
// 				Cpu:       d.Cpu,
// 				CpuPer:    d.CpuPer,
// 			}
// 		}
// 		// mergedDigests[d.Digest].Count += d.Count
// 		// mergedDigests[d.Digest].TotalCpu += d.TotalCpu
// 		// if !strings.Contains(mergedDigests[d.Digest].ClusterArr, d.ClusterId) {
// 		// 	if mergedDigests[d.Digest].ClusterArr != "" {
// 		// 		mergedDigests[d.Digest].ClusterArr += ","
// 		// 	}
// 		// 	mergedDigests[d.Digest].ClusterArr += d.ClusterId
// 		// }
// 	}

// 	return mergedDigests, nil
// }

// func (s *TopSqlTop20Service) calculateAndSaveTopDigests(ctx context.Context, orgId string, mergedDigests map[string]*model.TopDigest) error {
// 	yesterdayDigestMap, err := s.getYesterdayTopDigestMap(ctx, orgId, s.Day)
// 	if err != nil {
// 		return err
// 	}

// 	var topDigests []model.TopDigest
// 	for _, digest := range mergedDigests {
// 		mapKey := digest.ClusterId + digest.Digest
// 		if prevDigest, ok := yesterdayDigestMap[mapKey]; ok {
// 			digest.IsNew = false
// 			if prevDigest.Count > 0 {
// 				digest.CntDiff = utils.CalculateDiff(digest.Count, prevDigest.Count)
// 			}
// 			if prevDigest.Cpu > 0 {
// 				digest.CpuDiff = utils.CalculateDiff(digest.Cpu, prevDigest.Cpu)
// 			}
// 		} else {
// 			digest.IsNew = true
// 		}
// 		topDigests = append(topDigests, *digest)
// 	}

// 	if len(topDigests) > 0 {
// 		// delete old data
// 		delParams := common.ComFilter{
// 			ModelFilters: map[string]any{
// 				"day":    s.Day,
// 				"org_id": orgId,
// 			},
// 		}
// 		err := s.commonDAO.DeleteEntity(ctx, delParams, &model.TopDigest{})
// 		if err != nil {
// 			log.PlainError("delete top_digests failed", zap.Error(err))
// 		}

// 		// create new data
// 		err = s.commonDAO.CreateEntity(ctx, &topDigests)
// 		if err != nil {
// 			return err
// 		}
// 	}

// 	return nil
// }

// func (s *TopSqlTop20Service) GenerateFeishuReport(ctx context.Context, org dto.Org) error {
// 	var topDigests []model.TopDigest
// 	params := common.ListParams{
// 		ModelFilters: model.TopDigest{
// 			Day:   s.Day,
// 			OrgId: org.OrgID,
// 		},
// 		// OrFilters: map[string]any{
// 		// 	"is_new = ?":   true,
// 		// 	"cnt_diff > ?": DiffCntThreshold,
// 		// 	"cpu_diff > ?": DiffCpuThreshold,
// 		// },
// 		Orders: []string{"is_new desc", "cpu_diff desc", "cnt_diff desc"},
// 	}
// 	err := s.commonDAO.ListEntities(ctx, params, &topDigests)
// 	if err != nil {
// 		return err
// 	}

// 	if len(topDigests) == 0 {
// 		log.PlainInfo("no data for feishu report", zap.String("orgId", org.OrgID))
// 		return nil
// 	}

// 	// conver to feishu report dto
// 	var topSqlReport dto.TopSqlReport
// 	topSqlReport, err = s.bulidTopSqlReport(org, topDigests)
// 	if err != nil {
// 		return err
// 	}

// 	reportWriter := feishu.NewReportWriter(org)

// 	reportWriter.WriteTop20ToFeishu(topSqlReport)
// 	log.PlainInfo("Feishu report data prepared", zap.Int("rows", len(topDigests)))

// 	return nil
// }

// func (s *TopSqlTop20Service) getYesterdayTopDigestMap(ctx context.Context, orgId, day string) (map[string]model.TopDigest, error) {
// 	yesterdayStr, err := utils.GetYesterdayStr(day)
// 	if err != nil {
// 		log.PlainError("parse day failed", zap.String("day", day), zap.Error(err))
// 		return nil, err
// 	}

// 	var digests []model.TopDigest
// 	params := common.ListParams{
// 		ModelFilters: map[string]any{
// 			"day":    yesterdayStr,
// 			"org_id": orgId,
// 		},
// 	}
// 	err = s.commonDAO.ListEntities(ctx, params, &digests)
// 	if err != nil {
// 		return nil, err
// 	}

// 	digestMap := make(map[string]model.TopDigest)
// 	for _, d := range digests {
// 		mapKey := d.ClusterId + d.Digest
// 		digestMap[mapKey] = d
// 	}
// 	return digestMap, nil
// }

// func (s *TopSqlTop20Service) replaceDigests(ctx context.Context, clusterId string, source string, newDigests *[]model.Digest) {
// 	params := common.ComFilter{
// 		ModelFilters: map[string]any{
// 			"cluster_id": clusterId,
// 			"day":        s.Day,
// 			"source":     source,
// 		},
// 	}
// 	err := s.commonDAO.DeleteEntity(ctx, params, &model.Digest{})
// 	if err != nil {
// 		log.PlainError("DeleteEntities failed", zap.Error(err))
// 	}

// 	err = s.commonDAO.CreateEntity(ctx, newDigests)
// 	if err != nil {
// 		log.PlainError("CreateEntity failed", zap.Error(err))
// 	}
// }

// // This is a helper function to sort map keys for consistent report generation
// func sortedKeys(m map[string]any) []string {
// 	keys := make([]string, 0, len(m))
// 	for k := range m {
// 		keys = append(keys, k)
// 	}
// 	sort.Strings(keys)
// 	return keys
// }

// // bulidTopSqlReport assembles the TopSqlReport data (summary and rows) for Feishu writing
// func (s *TopSqlTop20Service) bulidTopSqlReport(org dto.Org, topDigests []model.TopDigest) (dto.TopSqlReport, error) {
// 	report := dto.TopSqlReport{
// 		Summary: dto.TopSqlSummary{
// 			Day:     s.Day,
// 			OrgId:   org.OrgID,
// 			OrgName: org.OrgName,
// 			Total:   len(topDigests),
// 		},
// 	}
// 	if len(topDigests) == 0 {
// 		return report, nil
// 	}
// 	// 1. add all digests to report.SkipDigests
// 	// var sourceDigests []dto.TopSqlDigest
// 	// for _, item := range topDigests {
// 	// 	var cntDiffText, cpuDiffText string
// 	// 	if item.CntDiff != nil {
// 	// 		cntDiffText = fmt.Sprintf("%.2f%%", *item.CntDiff)
// 	// 	}
// 	// 	if item.CpuDiff != nil {
// 	// 		cpuDiffText = fmt.Sprintf("%.2f%%", *item.CpuDiff)
// 	// 	}
// 	// 	sourceDigests = append(sourceDigests, dto.TopSqlDigest{
// 	// 		Digest:     item.Digest,
// 	// 		CntDiffText:    cntDiffText,
// 	// 		CpuDiffText:    cpuDiffText,
// 	// 	})
// 	// }

// 	var topSqlDigests []dto.TopSqlDigest

// 	// Assemble digest rows
// 	for _, item := range topDigests {
// 		tags := make(map[string]dto.TopSqlTag)

// 		var cntDiffText, cpuDiffText string
// 		// a. check cnt diff
// 		if item.CntDiff != nil {
// 			if *item.CntDiff > DiffCntThreshold {
// 				// reason = "count diff>50%"
// 				// totalCntDiff++
// 				report.Summary.TotalCntDiff++
// 				tags[TagNameCntGT50] = getTag(TagNameCntGT50, item.CntDiff)
// 			}
// 			cntDiffText = fmt.Sprintf("%.2f%%", *item.CntDiff)
// 		}

// 		// b. check cpu diff
// 		if item.CpuDiff != nil {
// 			if *item.CpuDiff > DiffCpuThreshold {
// 				// reason = "cpu diff>50%"
// 				// totalCpuDiff++
// 				report.Summary.TotalCpuDiff++
// 				tags[TagNameCpuGT50] = getTag(TagNameCpuGT50, item.CpuDiff)
// 			}
// 			cpuDiffText = fmt.Sprintf("%.2f%%", *item.CpuDiff)
// 		}

// 		// c. check is new
// 		if item.IsNew {
// 			// reason = "new sql"
// 			// totalNew++
// 			report.Summary.TotalNew++
// 			tags[TagNameNewTopSql] = getTag(TagNameNewTopSql, item.IsNew)
// 		}

// 		// Build cluster alias string
// 		var clusterAliasArr []string
// 		for _, clusterId := range strings.Split(item.ClusterId, ",") {
// 			clusterId = strings.TrimSpace(clusterId)
// 			cluster := config.GetClusterByID(clusterId)
// 			if cluster != nil {
// 				alias := cluster.Alias
// 				if alias == "" {
// 					alias = cluster.ClusterName
// 				}
// 				clusterAliasArr = append(clusterAliasArr, alias)
// 			}
// 		}
// 		sort.Strings(clusterAliasArr)
// 		clusterAliasStr := strings.Join(clusterAliasArr, " , ")

// 		deployType := "dedicated"

// 		// isEven := func(i int, obj MaxCountClusterIdArr) bool {
// 		// 	return obj.Digest == res.Digest
// 		// }

// 		maxCountClusterId := strings.Split(item.ClusterId, ",")[0]
// 		cluster := config.GetClusterByID(maxCountClusterId)
// 		maxCountClusterName := cluster.ClusterName
// 		day := s.Day
// 		// List link
// 		listLink := fmt.Sprintf("%s?provider=%s&region=%s&orgName=%s&orgId=%s&projectId=%s&clusterId=%s&clusterName=%s&deployType=%s#/slow_query?show_internal=true&digest=%s&from=%s&row=0&to=%s",
// 			config.AppConfig.Clinic.BaseUrl,
// 			cluster.Provider,
// 			cluster.Region,
// 			org.OrgName,
// 			org.OrgID,
// 			cluster.ProjectId,
// 			maxCountClusterId,
// 			maxCountClusterName,
// 			deployType,
// 			item.Digest,
// 			config.GetDynamicTimeFrom(day),
// 			config.GetDynamicTimeTo2(day))

// 		topSqlDigests = append(topSqlDigests, dto.TopSqlDigest{
// 			// Reason:          reason,
// 			Digest:          item.Digest,
// 			ClusterArr:      item.ClusterId,
// 			Sql:             item.Sql,
// 			Count:           item.Count,
// 			TotalCpu:        item.Cpu,
// 			IsNew:           item.IsNew,
// 			ClusterAliasStr: clusterAliasStr,
// 			CntDiffText:     cntDiffText,
// 			CpuDiffText:     cpuDiffText,
// 			ListLink:        listLink,
// 			Tags:            tags,
// 			Reason:          getReason(tags),
// 		})
// 	}

// 	// 2. get seed digest
// 	isSeedDigest := func(i int, obj dto.TopSqlDigest) bool {
// 		for k := range obj.Tags {
// 			if k == TagNameNewTopSql || k == TagNameCntGT50 || k == TagNameCpuGT50 {
// 				return true
// 			}
// 		}
// 		return false
// 	}

// 	seedDigest, skipDigests := slice.GroupBy(topSqlDigests, isSeedDigest)

// 	_ = slice.SortByField(seedDigest, "Count", "desc")
// 	_ = slice.SortByField(skipDigests, "Count", "desc")

// 	report.SeedDigests = seedDigest
// 	report.SkipDigests = skipDigests

// 	return report, nil
// }

// func checkCpuDiff(report *dto.TopSqlReport, topDigests []model.TopDigest) error {
// 	var sourceDigests []dto.TopSqlDigest
// 	for _, item := range topDigests {
// 		var cpuDiffText string
// 		if item.CpuDiff != nil {
// 			cpuDiffText = fmt.Sprintf("%.2f%%", *item.CpuDiff)
// 		}
// 		sourceDigests = append(sourceDigests, dto.TopSqlDigest{
// 			Digest:      item.Digest,
// 			CpuDiffText: cpuDiffText,
// 		})
// 	}
// 	return nil
// }

// // func checkCntDiff(report *dto.TopSqlReport,topDigests []model.TopDigest) error {
// // 	return nil
// // }

// // func checkIsNew(report *dto.TopSqlReport,topDigests []model.TopDigest) error {
// // 	return nil
// // }

// // func skipFilter(report *dto.TopSqlReport) error {
// // 	return nil
// // }
// // func AndFilter(report *dto.TopSqlReport) error {
// // 	return nil
// // }

// func getTag(tagName string, value any) dto.TopSqlTag {
// 	tag := dto.TopSqlTag{Name: tagName, Value: value}
// 	desc := ""
// 	switch tagName {
// 	case TagNameCpuGT50:
// 		desc = tagCpuGT50.Desc
// 	case TagNameCntGT50:
// 		desc = tagCntGT50.Desc
// 	case TagNameNewTopSql:
// 		desc = tagNewTopSql.Desc
// 	default:
// 		tag.Desc = tagName
// 	}
// 	tag.Desc = desc
// 	return tag
// }

// func getReason(tags map[string]dto.TopSqlTag) string {
// 	var descs []string
// 	for _, v := range tags {
// 		descs = append(descs, v.Desc)
// 	}
// 	return strings.Join(descs, " && ")
// }
//
