package shared

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

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

// MetricsQueryRangeCase test case for shared cluster metrics query_range API
// For shared clusters, we directly call the dataserver metrics API
type MetricsQueryRangeCase struct {
	Query string // PromQL query
	Step  int    // Query step in seconds
}

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

func (c *MetricsQueryRangeCase) Name() string {
	return "MetricsQueryRange"
}

func (c *MetricsQueryRangeCase) Description() string {
	return "Verify metrics query_range API for shared cluster - direct dataserver call"
}

// 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 (c *MetricsQueryRangeCase) Run(ctx *framework.TestContext) *framework.CaseResult {
	result := framework.NewCaseResult()

	// For shared cluster, use metrics URL from config
	// If metricsURL is not configured, use the default pattern
	var metricsBaseURL string
	if ctx.Config.MetricsURL != "" {
		// Use configured metrics URL, append path parameters
		metricsBaseURL = fmt.Sprintf("%s/external/metrics/%s/tenant/%s/project/%s/application/%s/api/v1/",
			ctx.Config.MetricsURL,
			ctx.BizType,
			ctx.OrgID,
			ctx.ProjectID,
			ctx.ClusterID,
		)
	} else {
		panic("config error: MetricsURL is not configured")
	}

	// Build query parameters
	now := time.Now().Unix()
	start := now - 1830 // ~30 minutes ago
	end := now
	step := c.Step
	if step == 0 {
		step = 30 // default 30 seconds for shared cluster
	}

	query := c.Query
	if query == "" {
		// Default query: TiDB transaction rate
		query = `sum(rate(tidb_session_transaction_duration_seconds_count{scope=~"general"}[120s])) or vector(0)`
	}

	// Build request URL
	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)

	// Send request
	resp, err := ctx.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())

	// Check common response headers
	// For metrics API (dataserver), the CORS origin is "*" instead of specific origin
	checker := framework.NewCommonResponseChecker(result, ctx.Origin)
	checker.CheckCORSHeadersWithOptions(resp, framework.CORSCheckOptions{
		ExpectedOrigin:      "*",
		CredentialsOptional: false,
	})
	checker.CheckStatusCode(resp, 200)

	// 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
	}

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

	// 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))
	}

	// For shared cluster, result may be empty if no data, but query should succeed
	// We just verify the response structure is correct

	return result
}
