package store

import (
	"strings"
	"testing"
)

func TestCreateMetricAlertRule_Validates(t *testing.T) {
	s := newTestStore(t)
	cases := []struct {
		name    string
		in      MetricAlertRule
		wantErr string
	}{
		{
			name:    "missing name",
			in:      MetricAlertRule{Metric: MetricCPUPercent, Comparator: MetricComparatorGT},
			wantErr: "name is required",
		},
		{
			name:    "bad metric",
			in:      MetricAlertRule{Name: "n", Metric: "load_avg", Comparator: MetricComparatorGT},
			wantErr: "invalid metric",
		},
		{
			name:    "bad comparator",
			in:      MetricAlertRule{Name: "n", Metric: MetricCPUPercent, Comparator: "eq"},
			wantErr: "invalid comparator",
		},
		{
			name:    "bad severity",
			in:      MetricAlertRule{Name: "n", Metric: MetricCPUPercent, Comparator: MetricComparatorGT, Severity: "loud"},
			wantErr: "invalid severity",
		},
		{
			name:    "negative cooldown",
			in:      MetricAlertRule{Name: "n", Metric: MetricCPUPercent, Comparator: MetricComparatorGT, CooldownSeconds: -1},
			wantErr: "cooldown_seconds must be",
		},
	}
	for _, c := range cases {
		t.Run(c.name, func(t *testing.T) {
			_, err := s.CreateMetricAlertRule(c.in)
			if err == nil {
				t.Fatalf("expected error containing %q, got nil", c.wantErr)
			}
			if !strings.Contains(err.Error(), c.wantErr) {
				t.Fatalf("error mismatch: got %q want substring %q", err.Error(), c.wantErr)
			}
		})
	}
}

func TestCreateAndGetMetricAlertRule(t *testing.T) {
	s := newTestStore(t)
	r, err := s.CreateMetricAlertRule(MetricAlertRule{
		Name: "cpu-hot", Metric: MetricCPUPercent, Comparator: MetricComparatorGT,
		Threshold: 80, Severity: "warn", CooldownSeconds: 300, Enabled: true,
	})
	if err != nil {
		t.Fatalf("create: %v", err)
	}
	if r.ID == 0 {
		t.Fatal("id should be set")
	}
	got, err := s.GetMetricAlertRule(r.ID)
	if err != nil {
		t.Fatalf("get: %v", err)
	}
	if got.Metric != MetricCPUPercent || got.Comparator != MetricComparatorGT {
		t.Errorf("metric/comparator mismatch: %q %q", got.Metric, got.Comparator)
	}
	if got.Threshold != 80 {
		t.Errorf("threshold mismatch: %v", got.Threshold)
	}
	if !got.Enabled {
		t.Error("enabled lost on round-trip")
	}
}

func TestGetMetricAlertRule_NotFound(t *testing.T) {
	s := newTestStore(t)
	if _, err := s.GetMetricAlertRule(999); err == nil {
		t.Fatal("expected ErrNotFound for missing rule")
	}
}

func TestListMetricAlertRulesByWorkload(t *testing.T) {
	s := newTestStore(t)
	_, _ = s.CreateMetricAlertRule(MetricAlertRule{
		Name: "global", Metric: MetricCPUPercent, Comparator: MetricComparatorGT,
		Threshold: 90, Severity: "warn", Enabled: true,
	})
	_, _ = s.CreateMetricAlertRule(MetricAlertRule{
		Name: "w1-mem", WorkloadID: "w1", Metric: MetricMemoryPercent, Comparator: MetricComparatorGT,
		Threshold: 85, Severity: "error", Enabled: true,
	})
	_, _ = s.CreateMetricAlertRule(MetricAlertRule{
		Name: "w2-mem", WorkloadID: "w2", Metric: MetricMemoryBytes, Comparator: MetricComparatorGT,
		Threshold: 1000, Severity: "info", Enabled: true,
	})

	w1, err := s.ListMetricAlertRulesByWorkload("w1")
	if err != nil {
		t.Fatalf("by workload: %v", err)
	}
	// w1 sees its own rule + the global, but NOT w2's rule.
	if len(w1) != 2 {
		t.Fatalf("w1 should see 2 rules (own + global), got %d", len(w1))
	}
	for _, r := range w1 {
		if r.WorkloadID == "w2" {
			t.Errorf("w1 should not see w2's rule")
		}
	}
}

func TestUpdateMetricAlertRule(t *testing.T) {
	s := newTestStore(t)
	r, _ := s.CreateMetricAlertRule(MetricAlertRule{
		Name: "n", Metric: MetricCPUPercent, Comparator: MetricComparatorGT,
		Threshold: 80, Severity: "warn", Enabled: true,
	})
	r.Threshold = 95
	r.Comparator = MetricComparatorLT
	r.Enabled = false
	got, err := s.UpdateMetricAlertRule(r)
	if err != nil {
		t.Fatalf("update: %v", err)
	}
	if got.Threshold != 95 {
		t.Errorf("threshold not updated: %v", got.Threshold)
	}
	if got.Comparator != MetricComparatorLT {
		t.Errorf("comparator not updated: %q", got.Comparator)
	}
	if got.Enabled {
		t.Error("enabled=false not applied")
	}
}

func TestUpdateMetricAlertRule_NotFound(t *testing.T) {
	s := newTestStore(t)
	_, err := s.UpdateMetricAlertRule(MetricAlertRule{
		ID: 999, Name: "n", Metric: MetricCPUPercent, Comparator: MetricComparatorGT,
	})
	if err == nil {
		t.Fatal("expected ErrNotFound updating missing rule")
	}
}

func TestDeleteMetricAlertRule(t *testing.T) {
	s := newTestStore(t)
	r, _ := s.CreateMetricAlertRule(MetricAlertRule{
		Name: "n", Metric: MetricCPUPercent, Comparator: MetricComparatorGT,
		Threshold: 80, Severity: "warn", Enabled: true,
	})
	if err := s.DeleteMetricAlertRule(r.ID); err != nil {
		t.Fatalf("delete: %v", err)
	}
	if _, err := s.GetMetricAlertRule(r.ID); err == nil {
		t.Error("rule should be gone after delete")
	}
	if err := s.DeleteMetricAlertRule(r.ID); err == nil {
		t.Error("expected ErrNotFound deleting already-deleted rule")
	}
}
