package metrics

import (
	"context"
	"fmt"
	"strings"
	"time"

	"github.com/pingcap-inc/clinic/clinic_data_service/pkg/common"
	"github.com/pingcap-inc/clinic/clinic_data_service/pkg/dto/metrics"
	"github.com/pingcap-inc/clinic/clinic_data_service/pkg/log"
	"github.com/pingcap-inc/clinic/clinic_data_service/pkg/model"
	"go.uber.org/zap"

	"github.com/pingcap-inc/clinic/clinic_data_service/pkg/thirdapi/feishu"

	confMetrics "github.com/pingcap-inc/clinic/clinic_data_service/pkg/config/metrics"
	"github.com/pingcap-inc/clinic/clinic_data_service/pkg/dto"
	"github.com/pingcap-inc/clinic/clinic_data_service/pkg/prometheus"
)

func GetMetricsResult(ctx context.Context, org dto.Org, tenant string) {
	resourceQuery, performanceQuery := getQueryConfig()

	var allResults []metrics.DashboardTable
	yesterday := time.Now().UTC().AddDate(0, 0, -1)
	projectInfos := filterClustersByTenant(org.Clusters, tenant)
	for _, projectInfo := range projectInfos {
		clusterTableData := getDashboardResult(ctx, org, resourceQuery, performanceQuery, projectInfo, yesterday)
		dashboardResults := filterDashboardResult(clusterTableData)
		allResults = append(allResults, dashboardResults...)
		if len(dashboardResults) > 0 && projectInfo.DashboardID != "" {
			feishu.NewReportWriter(org).WriteDashboardResult(dashboardResults, projectInfo.DashboardID, projectInfo.BlockID, projectInfo.ProjectName)
		}
	}
	feishu.NewReportWriter(org).CreateHeading1ForTime(ctx, yesterday.Format("2006-01-02"), projectInfos[0].DashboardID, projectInfos[0].BlockID)

	filteredResults := GetMetricsMessageResult(allResults)
	fmt.Println(filteredResults)
	if len(filteredResults) > 0 {
		messageResult := metrics.FeishuMessageForMetricsResult{
			Topic:           "Metrics",
			OrgName:         org.OrgName,
			Yesterday:       yesterday.Format("2006-01-02"),
			DashboardResult: filteredResults,
		}
		feishu.SendMessageCardForMetrics(ctx, messageResult, tenant)
	}

}

func getDashboardResult(ctx context.Context, org dto.Org, resourceQuery, performanceQuery [][]confMetrics.MetricsQueryConfig, projectInfo metrics.ProjectInfo, yesterday time.Time) []metrics.ClusterTableData {

	var clusterTableData []metrics.ClusterTableData

	for _, cluster := range projectInfo.Cluster {
		var resourceDetailData, performanceDetailData []metrics.DetailTableData
		for _, queries := range resourceQuery {
			result := getMetricsResult(ctx, org, queries, cluster.ClusterID, yesterday, "Resource")
			resourceDetailData = append(resourceDetailData, result...)
		}

		for _, queries := range performanceQuery {
			result := getMetricsResult(ctx, org, queries, cluster.ClusterID, yesterday, "Performance")
			performanceDetailData = append(performanceDetailData, result...)
		}

		if len(resourceDetailData) > 0 || len(performanceDetailData) > 0 {
			clusterTableData = append(clusterTableData, metrics.ClusterTableData{
				ClusterName:           cluster.Alias,
				ResourceDetailData:    resourceDetailData,
				PerformanceDetailData: performanceDetailData,
			})
		}
	}

	detailTableTotal := metrics.DetailTable{
		Time:             yesterday.Format("2006-01-02"),
		ClusterTableData: clusterTableData,
		DocumentId:       projectInfo.DocumentID,
	}
	feishu.NewReportWriter(org).WriteDetailResult(detailTableTotal)
	return clusterTableData
}

func getMetricsResult(ctx context.Context, org dto.Org, queries []confMetrics.MetricsQueryConfig, clusterId string, yesterday time.Time, rule string) []metrics.DetailTableData {

	cluster, err := common.GetCluster(ctx,clusterId)
	if err != nil {
		log.Error(ctx,"Failed to get cluster", zap.String("cluster", clusterId), zap.Error(err))
		return nil
	}
	if cluster==nil {
		log.Error(ctx,"Failed to get cluster", zap.String("cluster", clusterId), zap.Error(err))
		return nil
	}

	queryResults := prometheus.MetricsQueryBatch(ctx, *cluster, queries, yesterday)
	var diffResults []metrics.QueryPoints
	if rule == "Resource" {
		diffResults = getResourceDiffResult(ctx, queryResults)
	} else {
		diffResults = getPerformanceDiffResult(ctx, queryResults)
	}

	// Calculate time range: primary is from (yesterday-1 day 00:00) to (yesterday 23:59)
	startTime := time.Date(yesterday.Year(), yesterday.Month(), yesterday.Day()-1, 0, 0, 0, 0, time.UTC)
	endTime := time.Date(yesterday.Year(), yesterday.Month(), yesterday.Day(), 23, 59, 59, 0, time.UTC)

	// webHost和tidbVersion对于定时任务不需要，传空字符串
	webHost := ""
	tidbVersion := ""
	results := getResourceQueryResult(webHost, org.VarOrgID, diffResults, clusterId, startTime, endTime, rule, tidbVersion)

	var detailTableData []metrics.DetailTableData
	if len(results) > 0 {
		detailTableData = append(detailTableData, metrics.DetailTableData{
			RuleName:            rule,
			TableRowAggregation: results,
		})
	}

	return detailTableData
}

// getDocumentModuleByTenantAndProject gets DocumentModule from AppConfig based on tenant and projectName
func getDocumentModuleByTenantAndProject(tenant, projectName string) confMetrics.DocumentModule {
	tenantLower := strings.ToLower(tenant)

	// Get base DocumentModule for the tenant
	var baseModule confMetrics.DocumentModule
	switch tenantLower {
	case "wetech":
		baseModule = confMetrics.AppConfig.Feishu.DocumentID.Wetech
	case "bingo":
		baseModule = confMetrics.AppConfig.Feishu.DocumentID.Bingo
	case "ue":
		baseModule = confMetrics.AppConfig.Feishu.DocumentID.UE
	default:
		// Default to Wetech if tenant not found
		baseModule = confMetrics.AppConfig.Feishu.DocumentID.Wetech
	}

	return baseModule
}

// getDetailDocumentID gets detail document ID from DocumentModule based on tenant and projectName
func getDetailDocumentID(tenant, projectName string) string {
	tenantLower := strings.ToLower(tenant)
	projectNameLower := strings.ToLower(projectName)
	baseModule := getDocumentModuleByTenantAndProject(tenant, projectName)

	// For Wetech, if projectName is saas/order/wallet, get from Detail map
	if tenantLower == "wetech" {
		if detailID, exists := baseModule.Detail[projectNameLower]; exists && detailID != "" {
			return detailID
		}
		// For gogame or other projectNames, return dashboard
		return baseModule.Dashboard
	}

	// For non-Wetech tenants, use main detail if exists, otherwise use dashboard
	if detailID, exists := baseModule.Detail["main"]; exists && detailID != "" {
		return detailID
	}

	return baseModule.Dashboard
}

func filterClustersByTenant(clusters []dto.Cluster, tenant string) []metrics.ProjectInfo {
	results := make([]metrics.ProjectInfo, 0)

	if tenant != "Wetech" {
		// For non-Wetech tenants, return all clusters in a single ProjectInfo
		documentModule := getDocumentModuleByTenantAndProject(tenant, "main")
		detailDocumentID := getDetailDocumentID(tenant, "main")
		result := metrics.ProjectInfo{
			ProjectName: tenant,
			Cluster:     clusters,
			DocumentID:  detailDocumentID,
			DashboardID: documentModule.Dashboard,
			BlockID:     documentModule.BlockID,
		}
		results = append(results, result)
		return results
	}

	allowedAliases := []string{"saas", "order", "wallet", "gogame"}
	clustersByType := make(map[string][]dto.Cluster)

	// Group clusters by their alias type
	for _, cluster := range clusters {
		alias := strings.ToLower(cluster.Alias)
		// Exclude us-saas-nacos
		if alias == "us-saas-nacos" {
			continue
		}
		for _, allowedAlias := range allowedAliases {
			if strings.Contains(alias, allowedAlias) {
				clustersByType[allowedAlias] = append(clustersByType[allowedAlias], cluster)
				break
			}
		}
	}

	for _, allowedAlias := range allowedAliases {
		if clusters, exists := clustersByType[allowedAlias]; exists && len(clusters) > 0 {
			documentModule := getDocumentModuleByTenantAndProject(tenant, allowedAlias)
			detailDocumentID := getDetailDocumentID(tenant, allowedAlias)
			result := metrics.ProjectInfo{
				ProjectName: allowedAlias,
				Cluster:     clusters,
				DocumentID:  detailDocumentID,
				DashboardID: documentModule.Dashboard,
				BlockID:     documentModule.BlockID,
			}
			results = append(results, result)
		}
	}

	return results
}

func filterDashboardResult(result []metrics.ClusterTableData) []metrics.DashboardTable {
	if len(result) == 0 {
		return []metrics.DashboardTable{}
	}

	var dashboardResults []metrics.DashboardTable

	// Iterate through all DetailTableForCluster (clusters)
	for _, clusterData := range result {
		// Use map to group by QueryName, storing ResourceTableRow with the largest Threshold
		type RowWithValue struct {
			Row   metrics.TableRowAggregation
			Value float64
		}
		queryNameMap := make(map[string]RowWithValue)

		// Iterate through each cluster's ResourceDetailData (rules)
		for _, detailData := range clusterData.ResourceDetailData {
			// Iterate through each rule's TableRowAggregation
			for _, row := range detailData.TableRowAggregation {
				thresholdValue := getMaxThresholdValue(row.Threshold)

				// If this QueryName hasn't been recorded yet, or current Threshold is larger, then update
				if existing, exists := queryNameMap[row.QueryName]; !exists || thresholdValue > existing.Value {
					queryNameMap[row.QueryName] = RowWithValue{
						Row:   row,
						Value: thresholdValue,
					}
				}
			}
		}

		// Iterate through each cluster's PerformanceDetailData (rules)
		for _, detailData := range clusterData.PerformanceDetailData {
			// Iterate through each rule's TableRowAggregation
			for _, row := range detailData.TableRowAggregation {
				thresholdValue := getMaxThresholdValue(row.Threshold)

				// If this QueryName hasn't been recorded yet, or current Threshold is larger, then update
				if existing, exists := queryNameMap[row.QueryName]; !exists || thresholdValue > existing.Value {
					queryNameMap[row.QueryName] = RowWithValue{
						Row:   row,
						Value: thresholdValue,
					}
				}
			}
		}

		// Convert filtered results back to ResourceTableRow array
		var filteredRows []metrics.TableRowAggregation
		for _, rowWithValue := range queryNameMap {
			filteredRows = append(filteredRows, rowWithValue.Row)
		}

		// Build DashboardResult for each cluster
		if len(filteredRows) > 0 {
			dashboardResults = append(dashboardResults, metrics.DashboardTable{
				ClusterName:         clusterData.ClusterName,
				TableRowAggregation: filteredRows,
			})
		}
	}

	return dashboardResults
}

func getMaxThresholdValue(threshold string) float64 {
	if threshold == "" || threshold == "N/A" {
		return 0
	}

	// Split by newline to handle multi-line thresholds
	lines := strings.Split(threshold, "\n")

	var maxValue float64
	firstValue := true

	for _, line := range lines {
		line = strings.TrimSpace(line)
		if line == "" {
			continue
		}

		// Handle formats like "instance1: 10.2%"
		if strings.Contains(line, ":") {
			parts := strings.Split(line, ":")
			if len(parts) >= 2 {
				line = strings.TrimSpace(parts[1])
			}
		}

		// Remove % sign
		line = strings.TrimSuffix(line, "%")
		line = strings.TrimSpace(line)

		// Parse to float
		var value float64
		_, err := fmt.Sscanf(line, "%f", &value)
		if err == nil {
			if firstValue {
				maxValue = value
				firstValue = false
			} else if value > maxValue {
				maxValue = value
			}
		}
	}

	return maxValue
}

func GetMetricsMessageResult(messageResults []metrics.DashboardTable) []metrics.DashboardTable {
	var filteredResults []metrics.DashboardTable
	for _, result := range messageResults {
		var filteredRows []metrics.TableRowAggregation
		for _, row := range result.TableRowAggregation {
			// First filter: exclude certain metrics
			// Only include metrics that are NOT in the exclusion list
			if row.QueryName == "TiDB_CPU" || row.QueryName == "TiKV_CPU" ||
				row.QueryName == "TiDB_MEM" || row.QueryName == "TiKV_MEM" ||
				row.QueryName == "P99QueryLatency" || row.QueryName == "P999QueryLatency" ||
				row.QueryName == "CPS" || row.QueryName == "QPS" || row.QueryName == "DiskUsage" || row.QueryName == "WriteWaitLatency" {
				// Second filter: only include metrics with "Need Attention" status
				if row.Status == "Need Attention" {
					filteredRows = append(filteredRows, row)
				}
			}
		}

		// Only add to results when filtered rows are not empty
		if len(filteredRows) > 0 {
			filteredResult := metrics.DashboardTable{
				ClusterName:         result.ClusterName,
				TableRowAggregation: filteredRows,
			}
			filteredResults = append(filteredResults, filteredResult)
		}
	}
	return filteredResults
}

// GetSingleClusterMetricsResult 单集群 Metrics 巡检（定时任务使用）
// 复用现有的查询配置、差异计算、状态判定逻辑
func GetSingleClusterMetricsResult(ctx context.Context, cluster model.Cluster, yesterday time.Time) ([]metrics.TableRowAggregation, error) {
	resourceQuery, performanceQuery := getQueryConfig()

	var allResults []metrics.TableRowAggregation

	// Calculate time range: primary is from (yesterday-1 day 00:00) to (yesterday 23:59)
	startTime := time.Date(yesterday.Year(), yesterday.Month(), yesterday.Day()-1, 0, 0, 0, 0, time.UTC)
	endTime := time.Date(yesterday.Year(), yesterday.Month(), yesterday.Day(), 23, 59, 59, 0, time.UTC)

	// webHost和tidbVersion对于定时任务不需要，传空字符串（不会生成link）
	webHost := ""
	varOrgId := ""
	tidbVersion := ""

	// 执行资源类查询
	for _, queries := range resourceQuery {
		queryResults := prometheus.MetricsQueryBatch(ctx, cluster, queries, yesterday)
		if len(queryResults) == 0 {
			continue
		}
		diffResults := getResourceDiffResult(ctx, queryResults)
		results := getResourceQueryResult(webHost, varOrgId, diffResults, cluster.ClusterID, startTime, endTime, "Resource", tidbVersion)
		allResults = append(allResults, results...)
	}

	// 执行性能类查询
	for _, queries := range performanceQuery {
		queryResults := prometheus.MetricsQueryBatch(ctx, cluster, queries, yesterday)
		if len(queryResults) == 0 {
			continue
		}
		diffResults := getPerformanceDiffResult(ctx, queryResults)
		results := getResourceQueryResult(webHost, varOrgId, diffResults, cluster.ClusterID, startTime, endTime, "Performance", tidbVersion)
		allResults = append(allResults, results...)
	}

	return allResults, nil
}

// GetSingleClusterMetricsResultWithRange 单集群 Metrics 巡检（API 使用，支持自定义时间范围）
// Primary 为 startTime ~ endTime，Baseline 为 (startTime - 14天) ~ startTime
func GetSingleClusterMetricsResultWithRange(ctx context.Context, cluster model.Cluster, startTime, endTime time.Time, varOrgId, tidbVersion string) ([]metrics.TableRowAggregation, error) {
	resourceQuery, performanceQuery := getQueryConfig()

	// 读取 webHost 配置，设置默认值
	webHost := confMetrics.AppConfig.Clinic.WebHost
	if webHost == "" {
		webHost = "https://clinic.pingcap.com"
	}

	var allResults []metrics.TableRowAggregation

	// 执行资源类查询
	for _, queries := range resourceQuery {
		queryResults := prometheus.MetricsQueryBatchWithRange(ctx, cluster, queries, startTime, endTime)
		if len(queryResults) == 0 {
			continue
		}
		diffResults := getResourceDiffResult(ctx, queryResults)
		results := getResourceQueryResult(webHost, varOrgId, diffResults, cluster.ClusterID, startTime, endTime, "Resource", tidbVersion)
		allResults = append(allResults, results...)
	}

	// 执行性能类查询
	for _, queries := range performanceQuery {
		queryResults := prometheus.MetricsQueryBatchWithRange(ctx, cluster, queries, startTime, endTime)
		if len(queryResults) == 0 {
			continue
		}
		diffResults := getPerformanceDiffResult(ctx, queryResults)
		results := getResourceQueryResult(webHost, varOrgId, diffResults, cluster.ClusterID, startTime, endTime, "Performance", tidbVersion)
		allResults = append(allResults, results...)
	}

	return allResults, nil
}

func getQueryConfig() ([][]confMetrics.MetricsQueryConfig, [][]confMetrics.MetricsQueryConfig) {
	cpuQueries := confMetrics.AppConfig.GetCPUResource()
	memQueries := confMetrics.AppConfig.GetMEMResource()
	resourceQuery := [][]confMetrics.MetricsQueryConfig{cpuQueries, memQueries}
	latencyQueries := confMetrics.AppConfig.GetLatencyQueries()
	diskQueries := confMetrics.AppConfig.GetDISKResource()
	performanceQuery := [][]confMetrics.MetricsQueryConfig{latencyQueries, diskQueries}
	return resourceQuery, performanceQuery
}
