package sqlstatement

import (
	"context"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"net/url"
	"sort"
	"time"

	"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/log"
	"github.com/pingcap-inc/clinic/clinic_data_service/pkg/service"
	"go.uber.org/zap"
)

// StatementItem represents a single statement item from the API response.
type StatementItem struct {
	ClusterId   string `json:"cluster_id"`
	ClusterName string `json:"cluster_alias"`
	Digest      string `json:"digest"`
	SchemaName  string `json:"schema_name"`
	ExecCount   int64  `json:"exec_count"`
	SumLatency  int64  `json:"sum_latency"`
	AvgLatency  int64  `json:"avg_latency"`
	PlanCount   int64  `json:"plan_count"`
	IndexNames  string `json:"index_names"`
}

type StatementParseBean struct {
	SchemaName     string
	TotalExecCount int64
	Precent        string
	// PrecentStr string
}

type StatementListResp struct {
	ClusterName    string
	ClusterId      string
	Day            string
	TotalExecCount int64
	TotalDB        int64
	Top            []StatementParseBean
	// DB []StatementParseBean
}

// FetchStatementsAndCountSchema fetches statements from the clinic API and counts executions per schema_name.
// 支持多个 ClusterId 并返回所有结果。
func FetchStatementsAndCountSchema(cs []service.ClusterInfo, cookie string, top int) ([]StatementListResp, error) {
	var results []StatementListResp
	var allErrs []error
	for _, cluster := range cs {

		beginTime := cluster.TimeRange.Start.Unix()
		endTime := cluster.TimeRange.End.Unix()

		csrfToken := "SilsT2QoDDBDZiIQ"
		stmts, err := statementListRange(cluster.ClusterId, beginTime, endTime, cookie, csrfToken)

		if err != nil {
			log.PlainError("failed to get statement list", zap.String("clusterId", cluster.ClusterId), zap.Error(err))
			allErrs = append(allErrs, err)
			continue
		}
		dao := common.NewCommonDAO()
		err = dao.CreateEntity(context.Background(), &stmts)
		if err != nil {
			log.PlainError("failed to create statement items", zap.String("clusterId", cluster.ClusterId), zap.Error(err))
			allErrs = append(allErrs, err)
			continue
		}

		dbMap := make(map[string]*StatementParseBean)
		totalExecCount := int64(0)
		for _, stmt := range stmts {
			totalExecCount += stmt.ExecCount
			if item, ok := dbMap[stmt.SchemaName]; ok {
				item.TotalExecCount += stmt.ExecCount
			} else {
				dbMap[stmt.SchemaName] = &StatementParseBean{
					SchemaName:     stmt.SchemaName,
					TotalExecCount: stmt.ExecCount,
				}
			}
		}
		var items []StatementParseBean
		for _, item := range dbMap {
			items = append(items, *item)
		}
		sort.Slice(items, func(i, j int) bool {
			return items[i].TotalExecCount > items[j].TotalExecCount
		})
		for i := range items {
			if totalExecCount > 0 {
				percent := float64(items[i].TotalExecCount) / float64(totalExecCount) * 100
				items[i].Precent = fmt.Sprintf("%.2f%%", percent)
			} else {
				items[i].Precent = "0.00%"
			}
		}
		// Top 统计逻辑
		var topN []StatementParseBean
		if len(items) > top {
			topN = append(topN, items[:top]...)
			other := StatementParseBean{SchemaName: "other"}
			for _, item := range items[top:] {
				other.TotalExecCount += item.TotalExecCount
			}
			if totalExecCount > 0 {
				percent := float64(other.TotalExecCount) / float64(totalExecCount) * 100
				other.Precent = fmt.Sprintf("%.2f%%", percent)
			} else {
				other.Precent = "0.00%"
			}
			topN = append(topN, other)
		} else {
			topN = append(topN, items...)
		}
		cluster := config.GetClusterByID(cluster.ClusterId)
		resp := StatementListResp{
			ClusterName: cluster.Alias,
			ClusterId:   cluster.ClusterID,
			// Day:            day,
			TotalExecCount: totalExecCount,
			TotalDB:        int64(len(items)),
			Top:            topN,
		}

		results = append(results, resp)
	}

	// filename := fmt.Sprintf("./output/statements/%s_top%d.json", day, top)
	// err := utils.WriteFile(filename, results)
	// if err != nil {
	// 	log.PlainError("failed to write StatementListResp to file", zap.String("day", day), zap.Error(err))
	// }
	// if len(allErrs) > 0 {
	// 	return results, fmt.Errorf("some clusters failed: %v", allErrs)
	// }
	// log.PlainInfo("Successfully fetched and counted statements for all clusters.", zap.Int("count", len(results)))
	return results, nil
}

func StatementLlist(clusterId, day, cookie string) ([]StatementItem, error) {
	csrfToken := "vWXqa7laee3vDI0R"
	location, err := time.LoadLocation("Asia/Shanghai")
	if err != nil {
		log.PlainError("failed to load location Asia/Shanghai", zap.Error(err))
		return nil, err
	}
	// Parse the day string
	t, err := time.ParseInLocation("2006-01-02", day, location)
	if err != nil {
		log.PlainError("failed to parse day string", zap.String("day", day), zap.Error(err))
		return nil, err
	}
	// 切分一天为两段
	beginTime := t.Unix()
	midTime := t.Add(12 * time.Hour).Unix()
	endTime := t.Add(24*time.Hour - time.Second).Unix()
	// 查询前半天
	items1, err1 := statementListRange(clusterId, beginTime, midTime-1, cookie, csrfToken)
	if err1 != nil {
		return nil, err1
	}
	// 查询后半天
	items2, err2 := statementListRange(clusterId, midTime, endTime, cookie, csrfToken)
	if err2 != nil {
		return nil, err2
	}
	// 合并结果
	return append(items1, items2...), nil
}

// 新增辅助函数，支持时间范围查询
func statementListRange(clusterId string, beginTime, endTime int64, cookie, csrfToken string) ([]StatementItem, error) {
	apiURL := "https://clinic.pingcap.com/ngm/api/v1/statements/list"
	params := url.Values{}
	params.Set("begin_time", fmt.Sprintf("%d", beginTime))
	params.Set("end_time", fmt.Sprintf("%d", endTime))
	params.Set("fields", "digest_text,sum_latency,avg_latency,max_latency,min_latency,exec_count,plan_count,digest,index_names")
	params.Set("text", "")
	fullURL := fmt.Sprintf("%s?%s", apiURL, params.Encode())
	log.PlainInfo(fullURL)
	req, err := http.NewRequest("GET", fullURL, nil)
	if err != nil {
		log.PlainError("failed to create new http request", zap.Error(err))
		return nil, err
	}
	cluster := config.GetClusterByID(clusterId)
	if cluster == nil {
		log.PlainError("failed to get cluster by id", zap.String("clusterId", clusterId))
		return nil, fmt.Errorf("failed to get cluster by id")
	}
	org := config.GetOrgByClusterID(clusterId)
	req.Header.Set("Content-Type", "application/json")
	req.Header.Set("Accept", "application/json, text/plain, */*")
	req.Header.Set("User-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36")
	req.Header.Set("x-cluster-id", clusterId)
	req.Header.Set("x-csrf-token", csrfToken)
	req.Header.Set("x-deploy-type", "dedicated")
	req.Header.Set("x-org-id", org.OrgID)
	req.Header.Set("x-project-id", cluster.ProjectId)
	req.Header.Set("x-provider", cluster.Provider)
	req.Header.Set("x-region", cluster.Region)
	req.Header.Set("Cookie", cookie)
	client := &http.Client{}
	resp, err := client.Do(req)
	if err != nil {
		log.PlainError("failed to send http request", zap.Error(err))
		return nil, err
	}
	defer resp.Body.Close()
	if resp.StatusCode != http.StatusOK {
		body, _ := io.ReadAll(resp.Body)
		err := fmt.Errorf("API request failed with status: %s, body: %s", resp.Status, string(body))
		log.PlainError("API request failed", zap.String("status", resp.Status), zap.String("body", string(body)))
		return nil, err
	}
	body, err := io.ReadAll(resp.Body)
	if err != nil {
		log.PlainError("failed to read response body", zap.Error(err))
		return nil, err
	}
	var statements []StatementItem
	if err := json.Unmarshal(body, &statements); err != nil {
		log.PlainError("failed to unmarshal response body", zap.Error(err))
		return nil, err
	}
	for i, _ := range statements {
		statements[i].ClusterId = clusterId
		statements[i].ClusterName = cluster.Alias
	}
	log.PlainInfo("Successfully fetched and counted statements.", zap.Any("statements", len(statements)))
	return statements, nil
}
