package services

import (
	"slices"
	"testing"
)

// Pure-Go tests for the SystemView registry. Each system view's specs
// must self-validate; the slugs must be reserved.

func TestSystemViews_AllValidate(t *testing.T) {
	for _, sv := range AllSystemViews() {
		t.Run(sv.Slug, func(t *testing.T) {
			if err := sv.Filter.Validate(); err != nil {
				t.Errorf("%s filter spec invalid: %v", sv.Slug, err)
			}
			if err := sv.Render.Validate(); err != nil {
				t.Errorf("%s render spec invalid: %v", sv.Slug, err)
			}
		})
	}
}

func TestSystemViews_SlugsReserved(t *testing.T) {
	for _, sv := range AllSystemViews() {
		t.Run(sv.Slug, func(t *testing.T) {
			if !IsReservedUserViewSlug(sv.Slug) {
				t.Errorf("system slug %q must be reserved against user_views", sv.Slug)
			}
		})
	}
}

func TestReservedSlugs_CaseFolded(t *testing.T) {
	if !IsReservedUserViewSlug("Dashboard") {
		t.Error("reserved-slug check must be case-insensitive")
	}
	if !IsReservedUserViewSlug("INBOX") {
		t.Error("reserved-slug check must be case-insensitive")
	}
}

func TestReservedSlugs_NonReservedAccepted(t *testing.T) {
	cases := []string{"freitag-stand", "approval-pending-mine", "siemens", "my-view"}
	for _, slug := range cases {
		if IsReservedUserViewSlug(slug) {
			t.Errorf("user-friendly slug %q must not be reserved", slug)
		}
	}
}

// ----------------------------------------------------------------------
// InboxSystemView shape — t-paliad-249
// ----------------------------------------------------------------------

func TestInboxSystemView_Sources(t *testing.T) {
	sv := InboxSystemView()
	if !slices.Contains(sv.Filter.Sources, SourceApprovalRequest) {
		t.Errorf("InboxSystemView must include SourceApprovalRequest, got %v", sv.Filter.Sources)
	}
	if !slices.Contains(sv.Filter.Sources, SourceProjectEvent) {
		t.Errorf("InboxSystemView must include SourceProjectEvent, got %v", sv.Filter.Sources)
	}
}

func TestInboxSystemView_DefaultsToPast30d(t *testing.T) {
	sv := InboxSystemView()
	if sv.Filter.Time.Horizon != HorizonPast30d {
		t.Errorf("default horizon must be past_30d, got %q", sv.Filter.Time.Horizon)
	}
}

func TestInboxSystemView_RowActionInbox(t *testing.T) {
	sv := InboxSystemView()
	if sv.Render.List == nil {
		t.Fatal("InboxSystemView must define a list config")
	}
	if sv.Render.List.RowAction != RowActionInbox {
		t.Errorf("row_action must be inbox, got %q", sv.Render.List.RowAction)
	}
}

func TestInboxSystemView_CuratedProjectEventKinds(t *testing.T) {
	sv := InboxSystemView()
	if sv.Filter.Predicates == nil || sv.Filter.Predicates.ProjectEvent == nil {
		t.Fatal("InboxSystemView must narrow project_event predicates")
	}
	got := sv.Filter.Predicates.ProjectEvent.EventTypes
	if len(got) != len(InboxProjectEventKinds) {
		t.Errorf("expected %d curated kinds, got %d", len(InboxProjectEventKinds), len(got))
	}
	for _, k := range got {
		if slices.Contains([]string{"status_changed", "project_created"}, k) {
			t.Errorf("inbox must NOT include noisy kind %q", k)
		}
		// No *_approval_* audit duplicates either — view_service dedups
		// at query time but the curated list shouldn't carry them.
		if isApprovalAuditKind(k) {
			t.Errorf("inbox curated list must not include audit-dup %q", k)
		}
	}
}

func TestInboxSystemView_NewestFirst(t *testing.T) {
	sv := InboxSystemView()
	if sv.Render.List == nil || sv.Render.List.Sort != SortDateDesc {
		t.Errorf("inbox must sort newest-first by default, got %q", sv.Render.List.Sort)
	}
}
