// This file verifies replay dataset parsing, reference sources, fetching, and raw replay.
package evaluation

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

func TestDecodeReplayDatasetSpecRejectsUnknownFields(t *testing.T) {
	_, err := DecodeReplayDatasetSpec(strings.NewReader(`
version: v1alpha1
id: test-dataset
component: tidb
profile: tidb/horizontal-v1
unknown: true
`))
	if err == nil || !strings.Contains(err.Error(), "unknown") {
		t.Fatalf("DecodeReplayDatasetSpec() error = %v, want unknown field error", err)
	}
}

func TestBuildO11YDatasetExportSpecAppendsReferenceSources(t *testing.T) {
	spec := &ReplayDatasetSpec{
		Version:   ReplayVersionV1Alpha1,
		ID:        "test-dataset",
		Component: "tidb",
		Profile:   "tidb/test",
		Target:    O11YTargetSpec{ClusterID: "cluster-1", Vendor: "aws", Region: "us-west-2"},
		Window:    O11YQueryWindow{Start: time.Date(2026, 8, 1, 0, 0, 0, 0, time.UTC), End: time.Date(2026, 8, 2, 0, 0, 0, 0, time.UTC), StepSeconds: 60},
		Endpoints: map[O11YMetricSource]string{O11YMetricSourceApplication: "https://app.example.com/api/v1"},
		ReferenceSources: []O11YSourceSpec{{
			Type:    O11YMetricSourceInfrastructure,
			BaseURL: "https://infra.example.com/api/v1",
			Headers: map[string]string{"x-org-id": "org-1"},
			Queries: []O11YQuerySpec{{
				Name:          "hpa_desired_replicas",
				PromQL:        "hpa desired query",
				Type:          O11YQueryTypeRange,
				Required:      true,
				MaxGapSeconds: 120,
			}},
		}},
	}
	profile := &MetricsProfile{
		Version:   MetricsProfileVersionV1Alpha1,
		ID:        "tidb/test",
		Component: "tidb",
		Sources: []MetricsProfileSource{{
			Type: O11YMetricSourceApplication,
			Queries: []O11YQuerySpec{{
				Name:          "tidb_up_replicas",
				PromQL:        `query{cluster_id="${cluster_id}"}`,
				Type:          O11YQueryTypeRange,
				Required:      true,
				MaxGapSeconds: 120,
			}},
		}},
	}

	exportSpec, err := BuildO11YDatasetExportSpec(spec, profile)
	if err != nil {
		t.Fatalf("BuildO11YDatasetExportSpec(): %v", err)
	}
	if len(exportSpec.Sources) != 2 {
		t.Fatalf("sources = %d, want profile and reference sources", len(exportSpec.Sources))
	}
	reference := exportSpec.Sources[1]
	if reference.Queries[0].Name != "hpa_desired_replicas" || reference.Headers["x-org-id"] != "org-1" {
		t.Fatalf("reference source = %+v, want exact HPA source", reference)
	}
}

func TestFetchAndReplayO11YDataset(t *testing.T) {
	start := time.Date(2026, 8, 1, 0, 0, 0, 0, time.UTC)
	end := start.Add(10 * time.Minute)
	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if got := r.Header.Get("Authorization"); got != "Bearer "+testBearerToken {
			t.Errorf("Authorization = %q, want bearer token", got)
		}
		if got := r.URL.Query().Get("start"); got != fmt.Sprint(start.Unix()) {
			t.Errorf("start = %q, want %d", got, start.Unix())
		}
		if got := r.URL.Query().Get("end"); got != fmt.Sprint(end.Unix()) {
			t.Errorf("end = %q, want %d", got, end.Unix())
		}
		if got := r.Header.Get("x-org-id"); got != "org-1" {
			t.Errorf("x-org-id = %q, want org-1", got)
		}

		query := r.URL.Query().Get("query")
		switch query {
		case "replicas query":
			fmt.Fprintf(w, `{"status":"success","isPartial":false,"data":{"resultType":"matrix","result":[{"metric":{},"values":[[%d,"2"],[%d,"2"]]}]}}`, start.Unix(), end.Unix())
		case "cpu query":
			fmt.Fprintf(w, `{"status":"success","isPartial":false,"data":{"resultType":"matrix","result":[{"metric":{"instance":"a","drop":"x"},"values":[[%d,"1"],[%d,"2"]]},{"metric":{"instance":"b","drop":"x"},"values":[[%d,"2"],[%d,"3"]]}]}}`, start.Unix(), end.Unix(), start.Unix(), end.Unix())
		default:
			http.Error(w, "unexpected query", http.StatusBadRequest)
		}
	}))
	t.Cleanup(server.Close)

	spec := &O11YDatasetExportSpec{
		Version:   ReplayVersionV1Alpha1,
		ID:        "test-dataset",
		Component: "tidb",
		Target:    O11YTargetSpec{ClusterID: "cluster-1", Vendor: "aws", Region: "us-west-2"},
		Window:    O11YQueryWindow{Start: start, End: end, StepSeconds: 60},
		Sources: []O11YSourceSpec{{
			Type:    O11YMetricSourceApplication,
			BaseURL: server.URL + "/api/v1",
			Headers: map[string]string{"x-org-id": "org-1"},
			Queries: []O11YQuerySpec{
				{Name: "tidb_up_replicas", PromQL: "replicas query", Type: O11YQueryTypeRange, Required: true, KeepLabels: []string{}, MaxGapSeconds: 120},
				{Name: "tidb_cpu", PromQL: "cpu query", Type: O11YQueryTypeRange, Required: true, KeepLabels: []string{"instance"}, MaxGapSeconds: 120},
			},
		}},
	}
	archivePath := filepath.Join(t.TempDir(), "archive.json")
	cacheDir := filepath.Join(t.TempDir(), "cache")
	archive, err := FetchO11YDataset(context.Background(), spec, O11YDatasetExportOptions{
		Client:      server.Client(),
		BearerToken: testBearerToken,
		ArchivePath: archivePath,
		CacheDir:    cacheDir,
	})
	if err != nil {
		t.Fatalf("FetchO11YDataset(): %v", err)
	}
	if len(archive.Queries) != 2 {
		t.Fatalf("archive queries = %d, want 2", len(archive.Queries))
	}
	data, err := os.ReadFile(archivePath)
	if err != nil {
		t.Fatal(err)
	}
	decoded, err := DecodeO11YDatasetArchive(bytes.NewReader(data))
	if err != nil {
		t.Fatalf("DecodeO11YDatasetArchive(): %v", err)
	}
	dataset, err := ReplayO11YDatasetArchive(spec, decoded, digestBytes(data))
	if err != nil {
		t.Fatalf("ReplayO11YDatasetArchive(): %v", err)
	}
	if len(dataset.Metrics) != 3 {
		t.Fatalf("dataset series = %d, want 3", len(dataset.Metrics))
	}
	if got := dataset.Metrics[0].Labels; len(got) != 1 || got["instance"] != "a" {
		t.Fatalf("first cpu labels = %v, want instance a only", got)
	}
	if dataset.MaxAgeByMetric["tidb_cpu"] != 2*time.Minute {
		t.Fatalf("cpu max age = %s, want 2m", dataset.MaxAgeByMetric["tidb_cpu"])
	}

	decoded.Queries[0].RequestDigest = "changed"
	if _, err := ReplayO11YDatasetArchive(spec, decoded, digestBytes(data)); err == nil || !strings.Contains(err.Error(), "request digest") {
		t.Fatalf("ReplayO11YDatasetArchive() error = %v, want request digest mismatch", err)
	}
}
