package service

import (
	"github.com/pingcap-inc/clinic/clinic_data_service/pkg/common"
)

const (
	TopX = 100
	// DiffCPUThreshold = 50.0
	// DiffCntThreshold = 50.0
)

type TopSqlService struct {
	commonDAO common.CommonDAO
}

func NewTopSqlService() *TopSqlService {
	return &TopSqlService{
		commonDAO: common.NewCommonDAO(),
	}
}

/*
// processDateRange handles the logic of iterating through a date range.
func (s *TopSqlService) processDateRange(ctx context.Context, clusterId, startDay, endDay string, dailyProcessor func(context.Context, string, string)) {
	layout := "20060102"
	startDate, err := time.Parse(layout, startDay)
	if err != nil {
		log.PlainError("parse startDay failed", zap.String("startDay", startDay), zap.Error(err))
		return
	}
	endDate, err := time.Parse(layout, endDay)
	if err != nil {
		log.PlainError("parse endDay failed", zap.String("endDay", endDay), zap.Error(err))
		return
	}

	if startDate.After(endDate) {
		log.PlainError("startDay cannot be after endDay", zap.String("startDay", startDay), zap.String("endDay", endDay))
		return
	}

	for d := startDate; !d.After(endDate); d = d.AddDate(0, 0, 1) {
		dayStr := d.Format(layout)
		log.PlainInfo("start to process for day", zap.String("day", dayStr))
		dailyProcessor(ctx, clusterId, dayStr)
	}
}

func (s *TopSqlService) SaveTopXCountSQL(ctx context.Context, clusterId string, startDay string, endDay string) {
	s.processDateRange(ctx, clusterId, startDay, endDay, s.saveTopXCountSQLForDay)
	log.PlainInfo("SaveTopXCountSQL done")
}

func (s *TopSqlService) SaveTopXCpuSQL(ctx context.Context, clusterId string, startDay string, endDay string) {
	s.processDateRange(ctx, clusterId, startDay, endDay, s.saveTopXCpuSQLForDay)
	log.PlainInfo("SaveTopXCpuSQL done")
}

func (s *TopSqlService) saveTopXCountSQLForDay(ctx context.Context, clusterId string, day string) {
	topCntResult, err := prometheus.GetTopXCountInfo(clusterId, day, TopX)
	if err != nil {
		log.PlainError("GetTopXCountInfo failed", zap.String("day", day), zap.Error(err))
		return
	}
	if len(topCntResult.TopSqls) == 0 {
		return
	}

	var yesterdayCntDigests []model.CntDigest
	yesterdayDigestMap, err := s.getYesterdayDigestMap(ctx, clusterId, day, &yesterdayCntDigests)
	if err != nil {
		return
	}

	var cntDigestArr []model.CntDigest
	for i, res := range topCntResult.TopSqls {
		digest := model.CntDigest{
			ClusterId:  clusterId,
			Day:        day,
			Top:        i + 1,
			Digest:     res.Digest,
			Sql:        res.Sql,
			Count:      res.Count,
			AvgLatency: res.AvgLatency,
			AvgCpu:     res.AvgCpu,
			MaxCpu:     res.MaxCpu,
			TotalCpu:   res.TotalCpu,
		}

		if prevDigest, ok := yesterdayDigestMap[digest.Digest]; ok {
			digest.IsNew = false
			if prev, ok := prevDigest.(model.CntDigest); ok {
				if prev.Count > 0 {
					digest.CntDiff = calculateDiff(float64(digest.Count), float64(prev.Count))
				}
				if prev.TotalCpu > 0 {
					digest.CpuDiff = calculateDiff(digest.TotalCpu, prev.TotalCpu)
				}
			}
		} else {
			digest.IsNew = true
		}
		cntDigestArr = append(cntDigestArr, digest)
	}

	if len(cntDigestArr) > 0 {
		s.replaceDigests(ctx, clusterId, day, &model.CntDigest{}, &cntDigestArr)
	}
}

func (s *TopSqlService) saveTopXCpuSQLForDay(ctx context.Context, clusterId string, day string) {
	topCpuResult, err := prometheus.GetTopXCPUInfo(clusterId, day, TopX)
	if err != nil {
		log.PlainError("GetTopXCPUInfo failed", zap.String("day", day), zap.Error(err))
		return
	}
	if len(topCpuResult.TopSqls) == 0 {
		return
	}

	var yesterdayCpuDigests []model.CpuDigest
	yesterdayDigestMap, err := s.getYesterdayDigestMap(ctx, clusterId, day, &yesterdayCpuDigests)
	if err != nil {
		return
	}

	var cpuDigestArr []model.CpuDigest
	for i, res := range topCpuResult.TopSqls {
		digest := model.CpuDigest{
			ClusterId:     clusterId,
			Day:           day,
			Top:           i + 1,
			Digest:        res.Digest,
			Sql:           res.Sql,
			Count:         res.Count,
			TotalCpu:      res.TotalCpu,
			AvgCpu:        res.AvgCpu,
			MaxCpu:        res.MaxCpu,
			AvgLatency:    res.AvgLatency,
			CpuDayPercent: res.CpuDayPercent,
		}

		if prevDigest, ok := yesterdayDigestMap[digest.Digest]; ok {
			digest.IsNew = false
			if prev, ok := prevDigest.(model.CpuDigest); ok {
				if prev.TotalCpu > 0 {
					digest.CpuDiff = calculateDiff(digest.TotalCpu, prev.TotalCpu)
				}
				if prev.Count > 0 {
					digest.CntDiff = calculateDiff(float64(digest.Count), float64(prev.Count))
				}
			}
		} else {
			digest.IsNew = true
		}
		cpuDigestArr = append(cpuDigestArr, digest)
	}

	if len(cpuDigestArr) > 0 {
		s.replaceDigests(ctx, clusterId, day, &model.CpuDigest{}, &cpuDigestArr)
	}
}

func (s *TopSqlService) getYesterdayDigestMap(ctx context.Context, clusterId, day string, digests any) (map[string]any, error) {
	yesterdayStr, err := utils.GetYesterdayStr(day)
	if err != nil {
		log.PlainError("parse day failed", zap.String("day", day), zap.Error(err))
		return nil, err
	}

	err = s.listDigests(ctx, clusterId, yesterdayStr, digests)
	if err != nil {
		return nil, err
	}

	digestMap := make(map[string]any)
	switch v := digests.(type) {
	case *[]model.CntDigest:
		for _, d := range *v {
			digestMap[d.Digest] = d
		}
	case *[]model.CpuDigest:
		for _, d := range *v {
			digestMap[d.Digest] = d
		}
	}
	return digestMap, nil
}

// listDigests is a generic function to list digests for a given day.
func (s *TopSqlService) listDigests(ctx context.Context, clusterId string, day string, digests any) error {
	params := common.ListParams{
		ModelFilters: map[string]any{
			"cluster_id": clusterId,
			"day":        day,
		},
	}
	err := s.commonDAO.ListEntities(ctx, params, digests)
	if err != nil {
		log.PlainError("listDigests failed", zap.Error(err), zap.String("day", day), zap.String("clusterId", clusterId))
		return err
	}
	return nil
}

// replaceDigests deletes the old digests for a day and creates the new ones.
func (s *TopSqlService) replaceDigests(ctx context.Context, clusterId string, day string, modelInstance any, newDigests any) {
	// 1. Delete old data
	params := common.ComFilter{
		ModelFilters: map[string]any{
			"cluster_id": clusterId,
			"day":        day,
		},
	}
	err := s.commonDAO.DeleteEntity(ctx, params, modelInstance)
	if err != nil {
		log.PlainError("DeleteEntities failed", zap.Error(err))
		// Continue to save new data even if deletion fails
	}
	// 2. Save to DB
	err = s.commonDAO.CreateEntity(ctx, newDigests)
	if err != nil {
		log.PlainError("CreateEntity failed", zap.Error(err))
	}
}

*/

// func calculateDiff(current, previous float64) *float64 {
// 	if previous > 0 {
// 		diff := (current - previous) / previous * 100.0
// 		roundedDiff := math.Round(diff*100) / 100
// 		return &roundedDiff
// 	}
// 	return nil
// }
