// This file verifies experiment parsing, archive selection, and production Policy construction.
package evaluation

import (
	"strings"
	"testing"
	"time"

	"k8s.io/apimachinery/pkg/api/resource"

	decisionmetrics "github.com/tidbcloud/cluster-service-ng/internal/capacity-planner/pkg/metrics"
)

func TestDecodeReplayExperimentSpecRejectsUnknownFields(t *testing.T) {
	_, _, err := DecodeReplayExperimentSpec(strings.NewReader(`
version: v1alpha1
id: test
casesDir: cases
component: tidb
metricsProfile: tidb/horizontal-v1
target:
  clusterId: cluster-1
window:
  start: "2026-08-01T00:00:00Z"
  end: "2026-08-02T00:00:00Z"
dataStepSeconds: 60
evaluationIntervalSeconds: 300
observedReplicasMetric: replicas
policy:
  type: CostOptimized
  tierTemplates: templates.yaml
  metrics: []
unknown: true
`))
	if err == nil || !strings.Contains(err.Error(), "unknown") {
		t.Fatalf("DecodeReplayExperimentSpec() error = %v, want unknown field error", err)
	}
}

func TestBuildReplayPolicyUsesProductionCostOptimized(t *testing.T) {
	experiment, err := LoadReplayExperiment("experiments/tidb/cost-optimized-horizontal-v1.yaml")
	if err != nil {
		t.Fatalf("LoadReplayExperiment(): %v", err)
	}
	observed := &TiDBTierObservation{
		NodeSpec:      "4C16G",
		CPURequest:    resource.MustParse("3"),
		MemoryRequest: resource.MustParse("10350Mi"),
	}

	candidate, mappings, err := BuildReplayPolicy(experiment, observed)
	if err != nil {
		t.Fatalf("BuildReplayPolicy(): %v", err)
	}
	if len(mappings) != 2 || mappings[0] != (PolicyMetricMapping{SourceMetric: "tidb_cpu_cores_by_instance", PolicyMetric: "cpu"}) {
		t.Fatalf("mappings = %+v, want CPU and memory production mappings", mappings)
	}
	targetTier, replicas, err := candidate.Choose([]decisionmetrics.Metrics{
		{Series: []decisionmetrics.TimeSeries{{Samples: []decisionmetrics.Sample{{Value: 1}}}, {Samples: []decisionmetrics.Sample{{Value: 1}}}}},
		{Series: []decisionmetrics.TimeSeries{{Samples: []decisionmetrics.Sample{{Value: 1_000_000_000}}}, {Samples: []decisionmetrics.Sample{{Value: 1_000_000_000}}}}},
	})
	if err != nil {
		t.Fatalf("production Choose(): %v", err)
	}
	if targetTier.Name() != "historical-tidb-node-1" || replicas != 1 {
		t.Fatalf("production decision = %s/%d, want historical-tidb-node-1/1", targetTier.Name(), replicas)
	}
}

func TestBuildReplayPolicyRejectsUnconfiguredObservedTier(t *testing.T) {
	experiment, err := LoadReplayExperiment("experiments/tidb/cost-optimized-horizontal-v1.yaml")
	if err != nil {
		t.Fatalf("LoadReplayExperiment(): %v", err)
	}
	observed := &TiDBTierObservation{
		NodeSpec:      "32C64G",
		CPURequest:    resource.MustParse("31"),
		MemoryRequest: resource.MustParse("51667Mi"),
	}

	_, _, err = BuildReplayPolicy(experiment, observed)
	if err == nil || !strings.Contains(err.Error(), "no configured tier matches") {
		t.Fatalf("BuildReplayPolicy() error = %v, want unmatched tier", err)
	}
}

func TestSelectReplayDatasetUsesConfiguredTargetAndWindow(t *testing.T) {
	archiveStart := time.Date(2026, 8, 1, 0, 0, 0, 0, time.UTC)
	dataset := &ReplayDataset{
		Target: O11YTargetSpec{ClusterID: "cluster-1"},
		Window: O11YQueryWindow{Start: archiveStart, End: archiveStart.Add(7 * 24 * time.Hour), StepSeconds: 60},
	}
	experiment := &ReplayExperiment{Spec: ReplayExperimentSpec{
		Target: ReplayExperimentTarget{ClusterID: "cluster-1"},
		Window: ReplayExperimentWindow{Start: archiveStart.Add(24 * time.Hour), End: archiveStart.Add(48 * time.Hour)},
	}}

	selected, err := selectReplayDataset(experiment, dataset)
	if err != nil {
		t.Fatalf("selectReplayDataset(): %v", err)
	}
	if !selected.Window.Start.Equal(experiment.Spec.Window.Start) || !selected.Window.End.Equal(experiment.Spec.Window.End) {
		t.Fatalf("selected window = %s to %s, want experiment window", selected.Window.Start, selected.Window.End)
	}
	if !dataset.Window.Start.Equal(archiveStart) || !dataset.Window.End.Equal(archiveStart.Add(7*24*time.Hour)) {
		t.Fatalf("source dataset window was mutated: %+v", dataset.Window)
	}
}

func TestSelectReplayDatasetRejectsMismatchedArchive(t *testing.T) {
	archiveStart := time.Date(2026, 8, 1, 0, 0, 0, 0, time.UTC)
	dataset := &ReplayDataset{
		Target: O11YTargetSpec{ClusterID: "cluster-1"},
		Window: O11YQueryWindow{Start: archiveStart, End: archiveStart.Add(24 * time.Hour), StepSeconds: 60},
	}

	tests := []struct {
		name      string
		target    ReplayExperimentTarget
		window    ReplayExperimentWindow
		wantError string
	}{
		{
			name:      "cluster",
			target:    ReplayExperimentTarget{ClusterID: "cluster-2"},
			window:    ReplayExperimentWindow{Start: archiveStart, End: archiveStart.Add(time.Hour)},
			wantError: "clusterId",
		},
		{
			name:      "window",
			target:    ReplayExperimentTarget{ClusterID: "cluster-1"},
			window:    ReplayExperimentWindow{Start: archiveStart.Add(-time.Minute), End: archiveStart.Add(time.Hour)},
			wantError: "outside dataset window",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			experiment := &ReplayExperiment{Spec: ReplayExperimentSpec{Target: tt.target, Window: tt.window}}
			_, err := selectReplayDataset(experiment, dataset)
			if err == nil || !strings.Contains(err.Error(), tt.wantError) {
				t.Fatalf("selectReplayDataset() error = %v, want %q", err, tt.wantError)
			}
		})
	}
}
