package store

import (
	"errors"
	"testing"
)

// seedWorkloadForNotifications creates a minimal workload row so the FK
// constraint on workload_notifications is satisfied. Returns the new
// workload's ID for tests to reference.
func seedWorkloadForNotifications(t *testing.T, s *Store, name string) string {
	t.Helper()
	w, err := s.CreateWorkload(Workload{
		Kind:       string(WorkloadKindProject),
		Name:       name,
		SourceKind: "image",
	})
	if err != nil {
		t.Fatalf("seed workload: %v", err)
	}
	return w.ID
}

func TestCreateWorkloadNotification_RoundTrip(t *testing.T) {
	s := newTestStore(t)
	wlID := seedWorkloadForNotifications(t, s, "app1")

	created, err := s.CreateWorkloadNotification(WorkloadNotification{
		WorkloadID: wlID,
		Name:       "Slack alerts",
		URL:        "https://hooks.slack.test/x",
		Secret:     "shh",
		EventTypes: "deploy_failure,build_failure",
		Enabled:    true,
	})
	if err != nil {
		t.Fatalf("CreateWorkloadNotification: %v", err)
	}
	if created.ID == "" {
		t.Fatal("expected ID to be assigned")
	}

	got, err := s.GetWorkloadNotification(created.ID)
	if err != nil {
		t.Fatalf("Get: %v", err)
	}
	if got.URL != "https://hooks.slack.test/x" || got.Name != "Slack alerts" {
		t.Errorf("row mismatch: %+v", got)
	}
	if !got.Enabled {
		t.Error("expected Enabled=true")
	}
	if got.EventTypes != "deploy_failure,build_failure" {
		t.Errorf("event_types = %q", got.EventTypes)
	}
}

func TestCreateWorkloadNotification_RejectsMissingURL(t *testing.T) {
	s := newTestStore(t)
	wlID := seedWorkloadForNotifications(t, s, "app1")
	_, err := s.CreateWorkloadNotification(WorkloadNotification{
		WorkloadID: wlID,
		Name:       "broken",
		URL:        "",
	})
	if err == nil {
		t.Fatal("expected URL validation error")
	}
}

func TestListWorkloadNotifications_SortedByOrder(t *testing.T) {
	s := newTestStore(t)
	wlID := seedWorkloadForNotifications(t, s, "app1")

	// Insert out of order; ListWorkloadNotifications should return
	// them sorted by SortOrder ascending.
	_, _ = s.CreateWorkloadNotification(WorkloadNotification{
		WorkloadID: wlID, Name: "C", URL: "https://c.test", SortOrder: 30,
	})
	_, _ = s.CreateWorkloadNotification(WorkloadNotification{
		WorkloadID: wlID, Name: "A", URL: "https://a.test", SortOrder: 10,
	})
	_, _ = s.CreateWorkloadNotification(WorkloadNotification{
		WorkloadID: wlID, Name: "B", URL: "https://b.test", SortOrder: 20,
	})

	rows, err := s.ListWorkloadNotifications(wlID)
	if err != nil {
		t.Fatalf("list: %v", err)
	}
	if len(rows) != 3 {
		t.Fatalf("len = %d, want 3", len(rows))
	}
	if rows[0].Name != "A" || rows[1].Name != "B" || rows[2].Name != "C" {
		t.Errorf("sort order wrong: %q %q %q", rows[0].Name, rows[1].Name, rows[2].Name)
	}
}

func TestUpdateWorkloadNotification_PersistsChanges(t *testing.T) {
	s := newTestStore(t)
	wlID := seedWorkloadForNotifications(t, s, "app1")
	n, _ := s.CreateWorkloadNotification(WorkloadNotification{
		WorkloadID: wlID, Name: "old", URL: "https://old.test", Enabled: true,
	})
	n.Name = "new"
	n.URL = "https://new.test"
	n.Enabled = false
	n.EventTypes = "deploy_success"
	if err := s.UpdateWorkloadNotification(n); err != nil {
		t.Fatalf("update: %v", err)
	}
	got, _ := s.GetWorkloadNotification(n.ID)
	if got.Name != "new" || got.URL != "https://new.test" || got.Enabled {
		t.Errorf("update did not persist: %+v", got)
	}
}

func TestDeleteWorkloadNotification_ReturnsNotFoundForMissing(t *testing.T) {
	s := newTestStore(t)
	err := s.DeleteWorkloadNotification("nope")
	if !errors.Is(err, ErrNotFound) {
		t.Errorf("expected ErrNotFound, got %v", err)
	}
}

func TestDeleteWorkloadNotification_CascadesFromWorkload(t *testing.T) {
	s := newTestStore(t)
	wlID := seedWorkloadForNotifications(t, s, "app1")
	_, _ = s.CreateWorkloadNotification(WorkloadNotification{
		WorkloadID: wlID, Name: "x", URL: "https://x.test",
	})
	if err := s.DeleteWorkload(wlID); err != nil {
		t.Fatalf("delete workload: %v", err)
	}
	rows, err := s.ListWorkloadNotifications(wlID)
	if err != nil {
		t.Fatalf("list after cascade: %v", err)
	}
	if len(rows) != 0 {
		t.Errorf("expected cascade delete to remove rows, got %d", len(rows))
	}
}

func TestMatchesEventType_AllowList(t *testing.T) {
	cases := []struct {
		eventTypes string
		probe      string
		want       bool
	}{
		{"", "deploy_success", true},                          // empty = all
		{"deploy_success,deploy_failure", "deploy_success", true},
		{"deploy_success,deploy_failure", "build_failure", false},
		{"build_failure", "build_failure", true},
		{" deploy_success , build_failure ", "build_failure", true}, // whitespace tolerated
	}
	for _, c := range cases {
		n := WorkloadNotification{Enabled: true, EventTypes: c.eventTypes}
		got := n.MatchesEventType(c.probe)
		if got != c.want {
			t.Errorf("MatchesEventType(%q, %q) = %v, want %v", c.eventTypes, c.probe, got, c.want)
		}
	}
}

func TestMatchesEventType_DisabledNeverMatches(t *testing.T) {
	n := WorkloadNotification{Enabled: false, EventTypes: ""}
	if n.MatchesEventType("any") {
		t.Error("disabled row should never match")
	}
}
