package dedicated

import (
	"encoding/json"
	"fmt"
	"net/url"
	"time"

	"github.com/smarty/assertions"
	"github.com/tidbcloud/observability/test/webconsolee2e/framework"
)

// MetricsQueryRangeNewPathCase test case for metrics query_range API using new path
type MetricsQueryRangeNewPathCase struct {
	Query string // PromQL query
	Step  int    // Query step in seconds
}

// MetricsQueryResponse metrics query response structure
type MetricsQueryResponse struct {
	Status    string       `json:"status"`
	IsPartial bool         `json:"isPartial"`
	Data      MetricsData  `json:"data"`
	Stats     MetricsStats `json:"stats"`
}

type MetricsData struct {
	ResultType string         `json:"resultType"`
	Result     []MetricResult `json:"result"`
}

type MetricResult struct {
	Metric map[string]string `json:"metric"`
	Values [][]interface{}   `json:"values"`
}

type MetricsStats struct {
	SeriesFetched     string `json:"seriesFetched"`
	ExecutionTimeMsec int    `json:"executionTimeMsec"`
}

func NewMetricsQueryRangeNewPathCase(query string, step int) *MetricsQueryRangeNewPathCase {
	return &MetricsQueryRangeNewPathCase{
		Query: query,
		Step:  step,
	}
}

func (c *MetricsQueryRangeNewPathCase) Name() string {
	return "MetricsQueryRangeNewPath"
}

func (c *MetricsQueryRangeNewPathCase) Description() string {
	return "Verify metrics query_range API (gateway) - depends on GetClusterRouteNewPath to get metricsOpenReadUri"
}

func (c *MetricsQueryRangeNewPathCase) Run(ctx *framework.TestContext) *framework.CaseResult {
	result := framework.NewCaseResult()

	// 1. Get metricsOpenReadUri from shared data (set by GetClusterRouteNewPathCase)
	metricsBaseURL := ctx.GetSharedString(KeyNewPathMetricsOpenReadUri)
	if metricsBaseURL == "" {
		result.AddError("newPathMetricsOpenReadUri not found in shared data - ensure GetClusterRouteNewPathCase runs first")
		return result
	}

	// 2. Build query parameters
	now := time.Now().Unix()
	start := now - 1800 // 30 minutes ago
	end := now
	step := c.Step
	if step == 0 {
		step = 120 // default 2 minutes
	}

	query := c.Query
	if query == "" {
		// Default query: TiDB memory usage
		query = "avg(node_memory_MemTotal_bytes{component=\"tidb\"} - node_memory_MemAvailable_bytes{component=\"tidb\"})"
	}

	// 3. Build request URL
	// metricsBaseURL already ends with /api/v1/
	requestURL := fmt.Sprintf("%squery_range?start=%d&end=%d&step=%d&query=%s",
		metricsBaseURL,
		start,
		end,
		step,
		url.QueryEscape(query),
	)
	result.SetRequest("GET", requestURL, nil)

	// 4. Send request with newpath-specific headers
	client := ctx.GetNewPathClient()
	resp, err := client.Get(requestURL)
	if err != nil {
		result.AddError(fmt.Sprintf("Request failed: %v", err))
		return result
	}

	result.SetRequestWithHeaders(result.Request.Method, result.Request.URL, resp.RequestHeaders, result.Request.Body)
	result.SetResponseWithHeaders(resp.StatusCode, resp.GetResponseHeaders(), resp.String())

	// 5. Check common response headers
	checker := framework.NewCommonResponseChecker(result, ctx.Origin)
	checker.CheckCORSHeaders(resp)
	checker.CheckStatusCode(resp, 200)

	// 6. Parse response body
	var respBody MetricsQueryResponse
	if err := json.Unmarshal(resp.Body, &respBody); err != nil {
		result.AddError(fmt.Sprintf("Failed to parse response JSON: %v", err))
		return result
	}

	// 7. Verify status is "success"
	if msg := assertions.ShouldEqual(respBody.Status, "success"); msg != "" {
		result.AddError(fmt.Sprintf("Response status should be 'success': %s", msg))
	}

	// 8. Verify data.resultType is "matrix"
	if msg := assertions.ShouldEqual(respBody.Data.ResultType, "matrix"); msg != "" {
		result.AddError(fmt.Sprintf("data.resultType should be 'matrix': %s", msg))
	}

	// 9. Verify data.result is not empty (has values)
	if msg := assertions.ShouldNotBeEmpty(respBody.Data.Result); msg != "" {
		result.AddError(fmt.Sprintf("data.result should not be empty: %s", msg))
	}

	// 10. If result exists, verify it has values
	if len(respBody.Data.Result) > 0 {
		if msg := assertions.ShouldNotBeEmpty(respBody.Data.Result[0].Values); msg != "" {
			result.AddError(fmt.Sprintf("data.result[0].values should not be empty: %s", msg))
		}
	}

	return result
}
