// This file verifies automatic replay dataset lookup, discovery, fetch, and local reuse.
package evaluation

import (
	"context"
	"fmt"
	"net/http"
	"net/http/httptest"
	"os"
	"path/filepath"
	"strings"
	"sync/atomic"
	"testing"
	"time"
)

func TestResolveReplayDatasetFetchesOnMissAndReusesLocalArchive(t *testing.T) {
	start := time.Date(2026, 8, 1, 0, 0, 0, 0, time.UTC)
	end := start.Add(10 * time.Minute)
	var requests atomic.Int32

	var server *httptest.Server
	server = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		requests.Add(1)
		if got := r.Header.Get("Authorization"); got != "Bearer "+testBearerToken && got != "Bearer clinic-token" {
			t.Errorf("Authorization = %q, want configured bearer token", got)
		}

		switch r.URL.Path {
		case "/clinic/data-proxy/api/v1/clusters/cluster-2":
			fmt.Fprint(w, `{"clusterProviderName":"aws","clusterRegionName":"us-west-2","deployType":"premium","parentID":"pool-2"}`)
		case "/global/api/v1/clusters/":
			if r.URL.Query().Get("applicationID") == "pool-2" {
				fmt.Fprintf(w, `{"code":0,"data":{"clusters":[{"applicationID":"pool-2","bizType":"prod-tidbcloud-nextgen","id":"app-uuid","internalReadUri":%q,"tenantID":"org-2","vendor":"aws","region":"us-west-2"}]}}`, server.URL+"/app/api/v1/")
				return
			}
			if r.URL.Query().Get("tenantID") == "org-2" {
				fmt.Fprintf(w, `{"code":0,"data":{"clusters":[{"applicationID":"dp-test","bizType":"prod-tidbcloud-nextgen","id":"infra-uuid","internalReadUri":%q,"tenantID":"org-2","vendor":"aws","region":"us-west-2"}]}}`, server.URL+"/infra/api/v1/")
				return
			}
			http.Error(w, "unexpected discovery request", http.StatusBadRequest)
		case "/app/api/v1/query_range":
			if got := r.URL.Query().Get("query"); got != `count(up{tidb_cluster_id="cluster-2",component="tidb"} == 1)` {
				t.Errorf("application query = %q", got)
			}
			writeTestRangeResponse(w, start, end, 1)
		case "/app/api/v1/query":
			if got := r.URL.Query().Get("query"); got != `up{tidb_cluster_id="cluster-2",component="tidb"} == 1` {
				t.Errorf("TiDB group discovery query = %q", got)
			}
			fmt.Fprint(w, `{"status":"success","isPartial":false,"data":{"resultType":"vector","result":[{"metric":{"instance":"db-group-2-tidb-abc"},"value":[1785543000,"1"]}]}}`)
		case "/infra/api/v1/query_range":
			if got := r.Header.Get("x-org-id"); got != "org-2" {
				t.Errorf("x-org-id = %q, want org-2", got)
			}
			if got := r.URL.Query().Get("query"); !strings.Contains(got, `namespace="tidbpool-2"`) || !strings.Contains(got, `horizontalpodautoscaler="tidbgroup-db-group-2"`) {
				t.Errorf("infrastructure query = %q", got)
			}
			writeTestRangeResponse(w, start, end, 1)
		default:
			http.Error(w, "unexpected path", http.StatusNotFound)
		}
	}))

	casesDir := t.TempDir()
	profileDir := filepath.Join(casesDir, "tidb", "metrics")
	if err := os.MkdirAll(profileDir, 0o755); err != nil {
		t.Fatal(err)
	}
	if err := os.WriteFile(filepath.Join(profileDir, "test.yaml"), []byte(`version: v1alpha1
id: tidb/test
component: tidb
sources:
  - type: application
    queries:
      - name: tidb_up_replicas
        promql: 'count(up{tidb_cluster_id="${cluster_id}",component="tidb"} == 1)'
        type: range
        required: true
        keepLabels: []
        maxGapSeconds: 120
`), 0o644); err != nil {
		t.Fatal(err)
	}

	experiment := &ReplayExperiment{
		CasesDir: casesDir,
		Spec: ReplayExperimentSpec{
			Component:                "tidb",
			MetricsProfile:           "tidb/test",
			Target:                   ReplayExperimentTarget{ClusterID: "cluster-2"},
			Window:                   ReplayExperimentWindow{Start: start, End: end},
			DataStepSeconds:          60,
			HPADesiredReplicasMetric: "hpa_desired_replicas",
		},
	}
	cacheDir := filepath.Join(t.TempDir(), "cache")
	options := ReplayDatasetResolveOptions{
		Client:             server.Client(),
		ClinicEndpoint:     server.URL + "/clinic",
		ClinicAPIKey:       "clinic-token",
		O11YGlobalEndpoint: server.URL + "/global",
		O11YBearerToken:    testBearerToken,
		CacheDir:           cacheDir,
	}

	dataset, fetched, err := ResolveReplayDataset(context.Background(), experiment, options)
	if err != nil {
		t.Fatalf("ResolveReplayDataset() first run: %v", err)
	}
	if !fetched || dataset.Target.ClusterID != "cluster-2" || !dataset.Window.Start.Equal(start) || !dataset.Window.End.Equal(end) {
		t.Fatalf("first resolution = fetched %v dataset %+v, want fetched cluster-2 window", fetched, dataset)
	}
	firstRequestCount := requests.Load()
	if firstRequestCount == 0 {
		t.Fatal("first resolution made no discovery or metrics requests")
	}
	yamlPaths, err := filepath.Glob(filepath.Join(casesDir, "tidb", ReplayDatasetsDirectory, "*.yaml"))
	if err != nil || len(yamlPaths) != 1 {
		t.Fatalf("dataset YAML paths = %v, error %v, want one", yamlPaths, err)
	}
	archivePath := strings.TrimSuffix(yamlPaths[0], ".yaml") + ".json"
	if _, err := os.Stat(archivePath); err != nil {
		t.Fatalf("adjacent dataset archive: %v", err)
	}

	if err := os.Remove(archivePath); err != nil {
		t.Fatal(err)
	}
	if err := os.RemoveAll(cacheDir); err != nil {
		t.Fatal(err)
	}
	options.ClinicAPIKey = ""
	requestCountBeforeRefetch := requests.Load()
	dataset, fetched, err = ResolveReplayDataset(context.Background(), experiment, options)
	if err != nil {
		t.Fatalf("ResolveReplayDataset() missing archive: %v", err)
	}
	if !fetched {
		t.Fatal("missing archive was not fetched")
	}
	if requests.Load() == requestCountBeforeRefetch {
		t.Fatal("missing archive made no metrics requests")
	}

	server.Close()
	options.O11YBearerToken = ""
	requestCountBeforeReuse := requests.Load()
	dataset, fetched, err = ResolveReplayDataset(context.Background(), experiment, options)
	if err != nil {
		t.Fatalf("ResolveReplayDataset() cached run: %v", err)
	}
	if fetched {
		t.Fatal("second resolution fetched instead of using the local archive")
	}
	if requests.Load() != requestCountBeforeReuse {
		t.Fatalf("local resolution request count = %d, want unchanged %d", requests.Load(), requestCountBeforeReuse)
	}
}

func writeTestRangeResponse(w http.ResponseWriter, start, end time.Time, value int) {
	fmt.Fprintf(w, `{"status":"success","isPartial":false,"data":{"resultType":"matrix","result":[{"metric":{},"values":[[%d,%q],[%d,%q]]}]}}`, start.Unix(), fmt.Sprint(value), end.Unix(), fmt.Sprint(value))
}
