package dedicated

import (
	"encoding/json"
	"fmt"
	"strings"
	"time"

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

// setClinicHeaders sets the required headers for clinic API requests.
// It parses regionID (e.g., "aws-us-east-1") to extract provider and region,
// and sets X-Deploy-Type to "dedicated".
func setClinicHeaders(ctx *framework.TestContext) {
	// Parse regionID to extract provider and region
	// Format: "provider-region" e.g., "aws-us-east-1"
	provider := "aws"
	region := ""

	if ctx.RegionID != "" {
		parts := strings.SplitN(ctx.RegionID, "-", 2)
		if len(parts) >= 2 {
			provider = parts[0]
			region = parts[1]
		} else {
			region = ctx.RegionID
		}
	}

	// Set clinic-specific headers
	ctx.Client.SetHeader("X-Deploy-Type", "dedicated")
	ctx.Client.SetHeader("X-Provider", provider)
	if region != "" {
		ctx.Client.SetHeader("X-Region", region)
	}
}

// KeyVisualQueryCase test case for keyvisual config API
type KeyVisualQueryCase struct{}

// NewKeyVisualQueryCase creates a new KeyVisualQueryCase instance
func NewKeyVisualQueryCase() *KeyVisualQueryCase {
	return &KeyVisualQueryCase{}
}

func (c *KeyVisualQueryCase) Name() string {
	return "KeyVisualQuery"
}

func (c *KeyVisualQueryCase) Description() string {
	return "Verify keyvisual config API - /api/v1/orgs/{orgID}/projects/{projectID}/clusters/{clusterID}/ngm/keyvisual/config"
}

// KeyVisualConfigResponse keyvisual config response structure
type KeyVisualConfigResponse struct {
	AutoCollectionDisabled bool   `json:"auto_collection_disabled"`
	Policy                 string `json:"policy"`
	PolicyKVSeparator      string `json:"policy_kv_separator"`
}

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

	// Check if ClinicURL is configured
	if ctx.ClinicURL == "" {
		result.AddError("ClinicURL is not configured - set clinicURL in config or use --clinic-url flag")
		return result
	}

	// Set clinic-specific headers (X-Deploy-Type, X-Provider, X-Region)
	setClinicHeaders(ctx)

	// Build request URL
	// /api/v1/orgs/{orgID}/projects/{projectID}/clusters/{clusterID}/ngm/keyvisual/config
	url := fmt.Sprintf("%s/api/v1/orgs/%s/projects/%s/clusters/%s/ngm/keyvisual/config",
		ctx.ClinicURL,
		ctx.OrgID,
		ctx.ProjectID,
		ctx.ClusterID,
	)
	result.SetRequest("GET", url, nil)

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

	// 1. Check common response headers (CORS)
	// For clinic API: access-control-allow-origin is "*", access-control-allow-credentials can be absent
	checker := framework.NewCommonResponseChecker(result, ctx.Origin)
	if ctx.EnableNewPath {
		checker.CheckCORSHeaders(resp)
	} else {
		checker.CheckCORSHeadersWithOptions(resp, framework.CORSCheckOptions{
			ExpectedOrigin:      "*",
			CredentialsOptional: true,
		})
	}
	checker.CheckStatusCode(resp, 200)

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

	// 3. Verify auto_collection_disabled field exists (boolean type, so check is implicit in parsing)
	// We just verify the field was parsed successfully by checking the response structure

	// 4. Verify policy is not empty
	if msg := assertions.ShouldNotBeEmpty(respBody.Policy); msg != "" {
		result.AddError(fmt.Sprintf("policy should not be empty: %s", msg))
	}

	// 5. Verify policy_kv_separator field exists (can be empty string, but should exist in response)
	// Since it's a string field that can be empty, we verify the JSON was parsed correctly
	// The field exists if JSON parsing succeeded

	// Log the response values for debugging
	if ctx.Config.Verbose {
		fmt.Printf("      KeyVisual Config: auto_collection_disabled=%v, policy=%s, policy_kv_separator=%q\n",
			respBody.AutoCollectionDisabled, respBody.Policy, respBody.PolicyKVSeparator)
	}

	return result
}

// KeyVisualHeatmapsCase test case for keyvisual heatmaps API
type KeyVisualHeatmapsCase struct {
	Type string // heatmap type: written_bytes, read_bytes, etc.
}

func NewKeyVisualHeatmapsCase(heatmapType string) *KeyVisualHeatmapsCase {
	return &KeyVisualHeatmapsCase{
		Type: heatmapType,
	}
}

func (c *KeyVisualHeatmapsCase) Name() string {
	return "KeyVisualHeatmaps"
}

func (c *KeyVisualHeatmapsCase) Description() string {
	return "Verify keyvisual heatmaps API - /api/v1/orgs/{orgID}/projects/{projectID}/clusters/{clusterID}/ngm/keyvisual/heatmaps"
}

// KeyVisualHeatmapsResponse keyvisual heatmaps response structure
type KeyVisualHeatmapsResponse struct {
	Data     HeatmapData   `json:"data"`
	KeyAxis  []KeyAxisInfo `json:"keyAxis"`
	TimeAxis []int64       `json:"timeAxis"`
}

type HeatmapData struct {
	WrittenBytes [][]uint64 `json:"written_bytes,omitempty"`
	ReadBytes    [][]uint64 `json:"read_bytes,omitempty"`
}

type KeyAxisInfo struct {
	Key    string   `json:"key"`
	Labels []string `json:"labels"`
}

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

	// Check if ClinicURL is configured
	if ctx.ClinicURL == "" {
		result.AddError("ClinicURL is not configured - set clinicURL in config or use --clinic-url flag")
		return result
	}

	// Set clinic-specific headers (X-Deploy-Type, X-Provider, X-Region)
	setClinicHeaders(ctx)

	// Build query parameters
	now := time.Now().Unix()
	startTime := now - 3600 // 1 hour ago

	heatmapType := c.Type
	if heatmapType == "" {
		heatmapType = "written_bytes" // default type
	}

	// Build request URL
	// /api/v1/orgs/{orgID}/projects/{projectID}/clusters/{clusterID}/ngm/keyvisual/heatmaps?starttime={starttime}&type={type}
	url := fmt.Sprintf("%s/api/v1/orgs/%s/projects/%s/clusters/%s/ngm/keyvisual/heatmaps?starttime=%d&type=%s",
		ctx.ClinicURL,
		ctx.OrgID,
		ctx.ProjectID,
		ctx.ClusterID,
		startTime,
		heatmapType,
	)
	result.SetRequest("GET", url, nil)

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

	// 1. Check common response headers (CORS)
	// For clinic API: access-control-allow-origin is "*", access-control-allow-credentials can be absent
	checker := framework.NewCommonResponseChecker(result, ctx.Origin)
	if ctx.EnableNewPath {
		checker.CheckCORSHeaders(resp)
	} else {
		checker.CheckCORSHeadersWithOptions(resp, framework.CORSCheckOptions{
			ExpectedOrigin:      "*",
			CredentialsOptional: true,
		})
	}
	checker.CheckStatusCode(resp, 200)

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

	// 3. Verify data is not empty (check the field based on type)
	var dataValues [][]uint64
	switch heatmapType {
	case "written_bytes":
		dataValues = respBody.Data.WrittenBytes
	case "read_bytes":
		dataValues = respBody.Data.ReadBytes
	default:
		dataValues = respBody.Data.WrittenBytes
	}
	if msg := assertions.ShouldNotBeEmpty(dataValues); msg != "" {
		result.AddError(fmt.Sprintf("data.%s should not be empty: %s", heatmapType, msg))
	}

	// 4. Verify keyAxis is not empty
	if msg := assertions.ShouldNotBeEmpty(respBody.KeyAxis); msg != "" {
		result.AddError(fmt.Sprintf("keyAxis should not be empty: %s", msg))
	}

	// 5. Verify timeAxis is not empty
	if msg := assertions.ShouldNotBeEmpty(respBody.TimeAxis); msg != "" {
		result.AddError(fmt.Sprintf("timeAxis should not be empty: %s", msg))
	}

	// Log the response values for debugging
	if ctx.Config.Verbose {
		fmt.Printf("      KeyVisual Heatmaps: data rows=%d, keyAxis count=%d, timeAxis count=%d\n",
			len(dataValues), len(respBody.KeyAxis), len(respBody.TimeAxis))
	}

	return result
}
