package parser

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"math"
	"os"
	"path/filepath"
	"sort"
	"strings"

	"github.com/pingcap-inc/clinic/clinic_data_service/pkg/log"
	"github.com/pingcap-inc/clinic/clinic_data_service/pkg/prometheus"
	"go.uber.org/zap"
)

const (
	commitSql = "commit"
	useSql    = "use "
)

type Statement struct {
	Digest     string `json:"digest"`
	ExecCount  int    `json:"exec_count"`
	DigestText string `json:"digest_text"`
	SchemaName string `json:"schema_name"`
}

type StatementItem struct {
	Digest         string  `json:"digest"`
	TotalExecCount int     `json:"total_exec_count"`
	Sql            string  `json:"sql"`
	IsTopSql       bool    `json:"top_sql"`
	ToTalCpuTime   float64 `json:"total_cpu_time"`
	TopSqlCount    int     `json:"top_sql_count"`
	TopSqlModSmt   float64 `json:"top_sql_mod_stmt"`
}
type ParseResult struct {
	Day                 string          `json:"day"`
	TotalDigest         int             `json:"total_digest"`
	TotalExecCount      int             `json:"total_exec_count"`
	Top10TotalExecCount int             `json:"top10_total_exec_count"`
	Top10StatementItems []StatementItem `json:"top10_statement_items"`
}
type IsTopSqlResp struct {
	IsTopSql     bool
	TotalCpuTime float64
	TopSqlCount  int
}

func StatStatements(filename, clusterId, queryTimeStr, promSqlTime string) error {
	file, err := os.Open(filename)
	if err != nil {
		return err
	}
	defer file.Close()
	data, err := ioutil.ReadAll(file)
	if err != nil {
		return err
	}
	var stmts []Statement
	if err := json.Unmarshal(data, &stmts); err != nil {
		return err
	}

	dbMap := make(map[string]int)
	digestMap := make(map[string]*StatementItem)
	totalExecCount := 0
	// totalDB := 0
	for _, stmt := range stmts {
		totalExecCount += stmt.ExecCount
		if item, ok := digestMap[stmt.Digest]; ok {
			item.TotalExecCount += stmt.ExecCount
		} else {
			digestMap[stmt.Digest] = &StatementItem{
				Digest:         stmt.Digest,
				TotalExecCount: stmt.ExecCount,
				Sql:            stmt.DigestText,
			}
		}
		if item, ok := dbMap[stmt.SchemaName]; ok {
			item += stmt.ExecCount
		} else {
			dbMap[stmt.SchemaName] = stmt.ExecCount
		}
	}

	// a:= dbMap["dh_app_5072"]
	log.PlainInfo("dbmap", zap.Any("len", len(dbMap)))
	log.PlainInfo("dbmap", zap.Any("map", dbMap))

	var items []StatementItem
	for _, item := range digestMap {
		items = append(items, *item)
	}

	sort.Slice(items, func(i, j int) bool {
		return items[i].TotalExecCount > items[j].TotalExecCount
	})

	var top10Items []StatementItem
	top10TotalExecCount := 0
	numItems := 10
	if len(items) < 10 {
		numItems = len(items)
	}
	top10Items = items[:numItems]

	// for i := range top10Items {
	// 	item := &top10Items[i]
	// 	isTopResp, err := IsTopSql(clusterId, queryTimeStr, item.Digest, promSqlTime)
	// 	if err != nil {
	// 		// Log or handle error
	// 		panic(err)

	// 	}
	// 	mod := float64(isTopResp.TopSqlCount) / float64(item.TotalExecCount)
	// 	item.IsTopSql = isTopResp.IsTopSql
	// 	item.TopSqlCount = isTopResp.TopSqlCount
	// 	item.ToTalCpuTime = isTopResp.TotalCpuTime
	// 	item.TopSqlModSmt = math.Round(mod*100) /100
	// 	top10TotalExecCount += item.TotalExecCount

	// }

	day := strings.Split(filepath.Base(filename), "_")[2]
	parseResult := ParseResult{
		Day:                 day,
		TotalDigest:         len(digestMap),
		TotalExecCount:      totalExecCount,
		Top10StatementItems: top10Items,
		Top10TotalExecCount: top10TotalExecCount,
	}

	outputData, err := json.MarshalIndent(parseResult, "", "  ")
	if err != nil {
		return err
	}

	outputFilename := strings.Replace(filename, ".json", "_parsed.json", 1)
	err = ioutil.WriteFile(outputFilename, outputData, 0644)
	if err != nil {
		return fmt.Errorf("写入文件失败: %v", err)
	}

	return nil
}

func GetTotalCpu(clusterId string, queryTimeStr string, sqlDigest string, promSqlTime string) (float64, error) {
	sqlDigest = strings.ToUpper(sqlDigest)
	query := fmt.Sprintf(`sum(sum_over_time(topsql_cpu_time_ms{sql_digest="%s"}[%s]))`, sqlDigest, promSqlTime)
	return prometheus.GetOneValueByDay(clusterId, queryTimeStr, query)

}

func IsTopSql(clusterId string, queryTimeStr string, sqlDigest string, promSqlTime string) (*IsTopSqlResp, error) {

	sqlDigest = strings.ToUpper(sqlDigest)
	query := fmt.Sprintf(`sum(sum_over_time(topsql_cpu_time_ms{sql_digest="%s"}[%s]))`, sqlDigest, promSqlTime)
	val, err := prometheus.GetOneValueByTimeStr(clusterId, queryTimeStr, query)
	if err != nil {
		return nil, err
	}
	obj := &IsTopSqlResp{}
	if val == 0 {
		return obj, nil
	}

	totalcpuTime := val / 1000 / 60
	total := math.Round(totalcpuTime*100) / 100
	obj.IsTopSql = true
	obj.TotalCpuTime = total

	// get count
	query = fmt.Sprintf(`sum(sum_over_time(topsql_stmt_exec_count{instance_type="tidb",sql_digest="%s"}[%s]))`, sqlDigest, promSqlTime)
	count, err := prometheus.GetOneValueByTimeStr(clusterId, queryTimeStr, query)
	if err != nil {
		return nil, err
	}
	obj.TopSqlCount = int(count)
	return obj, nil
}
