package app

import (
	"sort"
	"strings"

	"go.signoz.io/signoz/pkg/query-service/constants"
	v3 "go.signoz.io/signoz/pkg/query-service/model/v3"
)

// applyMetricLimit applies limit to the metrics query results
func applyMetricLimit(results []*v3.Result, queryRangeParams *v3.QueryRangeParamsV3) {
	// apply limit if any for metrics
	// use the grouping set points to apply the limit

	for _, result := range results {
		builderQueries := queryRangeParams.CompositeQuery.BuilderQueries

		if builderQueries != nil && (builderQueries[result.QueryName].DataSource == v3.DataSourceMetrics) {
			limit := builderQueries[result.QueryName].Limit

			orderByList := builderQueries[result.QueryName].OrderBy
			{
				if len(orderByList) == 0 {
					// If no orderBy is specified, sort by value in descending order
					orderByList = []v3.OrderBy{{ColumnName: constants.SigNozOrderByValue, Order: "desc"}}
				}
				sort.SliceStable(result.Series, func(i, j int) bool {
					for _, orderBy := range orderByList {
						if orderBy.ColumnName == constants.SigNozOrderByValue {

							// For table type queries (we rely on the fact that one value for row), sort
							// based on final aggregation value
							if len(result.Series[i].Points) == 1 && len(result.Series[j].Points) == 1 {
								if orderBy.Order == "asc" {
									return result.Series[i].Points[0].Value < result.Series[j].Points[0].Value
								} else if orderBy.Order == "desc" {
									return result.Series[i].Points[0].Value > result.Series[j].Points[0].Value
								}
							}

							// For graph type queries, sort based on GroupingSetsPoint
							if result.Series[i].GroupingSetsPoint == nil || result.Series[j].GroupingSetsPoint == nil {
								// Handle nil GroupingSetsPoint, if needed
								// Here, we assume non-nil values are always less than nil values
								return result.Series[i].GroupingSetsPoint != nil
							}
							if orderBy.Order == "asc" {
								return result.Series[i].GroupingSetsPoint.Value < result.Series[j].GroupingSetsPoint.Value
							} else if orderBy.Order == "desc" {
								return result.Series[i].GroupingSetsPoint.Value > result.Series[j].GroupingSetsPoint.Value
							}
						} else {
							// Sort based on Labels map
							labelI, existsI := result.Series[i].Labels[orderBy.ColumnName]
							labelJ, existsJ := result.Series[j].Labels[orderBy.ColumnName]

							if !existsI || !existsJ {
								// Handle missing labels, if needed
								// Here, we assume non-existent labels are always less than existing ones
								return existsI
							}

							if orderBy.Order == "asc" {
								return strings.Compare(labelI, labelJ) < 0
							} else if orderBy.Order == "desc" {
								return strings.Compare(labelI, labelJ) > 0
							}
						}
					}
					// Preserve original order if no matching orderBy is found
					return i < j
				})

				if limit > 0 && len(result.Series) > int(limit) {
					result.Series = result.Series[:limit]
				}
			}
		}
	}
}
