// This file verifies that point-in-time and replay archives remain complete and replayable.
package evaluation

import (
	"encoding/json"
	"os"
	"path/filepath"
	"strings"
	"testing"
)

func TestCommittedO11YArchivesCoverCatalog(t *testing.T) {
	casePaths, err := filepath.Glob(filepath.Join("cases", "tidb", PointInTimeCasesDirectory, "*.yaml"))
	if err != nil {
		t.Fatalf("glob committed cases: %v", err)
	}
	archivePaths, err := filepath.Glob(filepath.Join("cases", "tidb", PointInTimeCasesDirectory, "*.json"))
	if err != nil {
		t.Fatalf("glob committed archives: %v", err)
	}
	if len(casePaths) != 112 || len(archivePaths) != len(casePaths) {
		t.Fatalf("cases/archives = %d/%d, want 112/112", len(casePaths), len(archivePaths))
	}

	caseIDs := make(map[string]struct{}, len(casePaths))
	for _, path := range casePaths {
		file, openErr := os.Open(path)
		if openErr != nil {
			t.Fatalf("open case %s: %v", path, openErr)
		}
		spec, decodeErr := DecodeCaseSpec(file)
		closeErr := file.Close()
		if decodeErr != nil || closeErr != nil {
			t.Fatalf("decode/close case %s: %v / %v", path, decodeErr, closeErr)
		}
		caseIDs[spec.ID] = struct{}{}
	}

	responseCount := 0
	for _, path := range archivePaths {
		data, readErr := os.ReadFile(path)
		if readErr != nil {
			t.Fatalf("read archive %s: %v", path, readErr)
		}
		var archive O11YArchive
		if err := json.Unmarshal(data, &archive); err != nil {
			t.Fatalf("decode archive %s: %v", path, err)
		}
		if archive.Version != O11YExportVersionV1Alpha1 || archive.Component != "tidb" {
			t.Errorf("archive %s version/component = %q/%q", path, archive.Version, archive.Component)
		}
		if filepath.Base(path) != archive.CaseID+".json" {
			t.Errorf("archive %s caseId = %q", path, archive.CaseID)
		}
		if _, ok := caseIDs[archive.CaseID]; !ok {
			t.Errorf("archive %s has no matching case", path)
		} else {
			delete(caseIDs, archive.CaseID)
		}
		if len(archive.Queries) != 8 {
			t.Errorf("archive %s queries = %d, want 8", path, len(archive.Queries))
		}
		hasTierEvidence := false
		for i := range archive.Queries {
			query := &archive.Queries[i]
			if query.Name == tiDBGOMAXPROCSMetric {
				hasTierEvidence = true
			}
			if query.RequestDigest == "" || len(query.Response) == 0 || !json.Valid(query.Response) {
				t.Errorf("archive %s query %q lacks a valid response and request digest", path, query.Name)
			}
			responseCount++
		}
		if !hasTierEvidence {
			t.Errorf("archive %s lacks TiDB tier evidence", path)
		}
	}

	if len(caseIDs) != 0 {
		t.Errorf("cases without archives = %d", len(caseIDs))
	}
	if responseCount != 896 {
		t.Errorf("archived responses = %d, want 896", responseCount)
	}
}

func TestCommittedReplayDatasetIsSelfContained(t *testing.T) {
	const datasetPath = "cases/tidb/replays/tidb-continuous-20260801T000000Z-20260808T000000Z-a3ffdd609441.yaml"

	dataset, err := LoadReplayDataset(datasetPath, "cases")
	if err != nil {
		t.Fatalf("LoadReplayDataset(): %v", err)
	}
	if dataset.Target.ClusterID != "10041541748747923370" || dataset.Component != "tidb" {
		t.Fatalf("dataset target/component = %q/%q", dataset.Target.ClusterID, dataset.Component)
	}
	if len(dataset.Metrics) == 0 || len(dataset.MaxAgeByMetric) != 9 {
		t.Fatalf("dataset series/metrics = %d/%d, want non-empty/9", len(dataset.Metrics), len(dataset.MaxAgeByMetric))
	}
	if len(dataset.ArchiveDigest) != 64 || strings.Trim(dataset.ArchiveDigest, "0123456789abcdef") != "" {
		t.Fatalf("archive digest = %q, want SHA-256", dataset.ArchiveDigest)
	}
}

func TestCommittedPointInTimeCasesResolveOneHistoricalTiDBTier(t *testing.T) {
	catalog, err := LoadCatalog("cases")
	if err != nil {
		t.Fatalf("LoadCatalog(): %v", err)
	}

	counts := make(map[string]int)
	for i := range catalog.Cases {
		bundle := catalog.Cases[i]
		observed, err := ResolveTiDBTierAt(bundle.Input.Metrics, bundle.Input.DecisionAt, tiDBTierMaxAge)
		if err != nil {
			t.Fatalf("case %q tier: %v", bundle.ID, err)
		}
		counts[observed.NodeSpec]++
	}

	want := map[string]int{"4C8G": 19, "4C16G": 16, "8C16G": 15, "8C32G": 26, "16C64G": 36}
	if len(counts) != len(want) {
		t.Fatalf("resolved tier counts = %v, want %v", counts, want)
	}
	for nodeSpec, count := range want {
		if counts[nodeSpec] != count {
			t.Errorf("resolved %s cases = %d, want %d", nodeSpec, counts[nodeSpec], count)
		}
	}
}
