package helpers

import (
	"testing"
	"time"

	"github.com/jfraeys/fetch_ml/internal/api/helpers"
)

func TestDBContext(t *testing.T) {
	ctx, cancel := helpers.DBContext(3 * time.Second)
	defer cancel()

	deadline, ok := ctx.Deadline()
	if !ok {
		t.Error("expected deadline to be set")
	}

	// Deadline should be within a reasonable time window
	now := time.Now()
	if deadline.Before(now) {
		t.Error("deadline is in the past")
	}
	if deadline.After(now.Add(4 * time.Second)) {
		t.Error("deadline is too far in the future")
	}

	// Context should not be cancelled
	select {
	case <-ctx.Done():
		t.Error("context should not be cancelled yet")
	default:
		// Good
	}
}

func TestDBContextShort(t *testing.T) {
	ctx, cancel := helpers.DBContextShort()
	defer cancel()

	deadline, ok := ctx.Deadline()
	if !ok {
		t.Error("expected deadline to be set")
	}

	now := time.Now()
	diff := deadline.Sub(now)

	// Should be around 3 seconds
	if diff < 2*time.Second || diff > 4*time.Second {
		t.Errorf("expected deadline ~3s, got %v", diff)
	}
}

func TestDBContextMedium(t *testing.T) {
	ctx, cancel := helpers.DBContextMedium()
	defer cancel()

	deadline, ok := ctx.Deadline()
	if !ok {
		t.Error("expected deadline to be set")
	}

	now := time.Now()
	diff := deadline.Sub(now)

	// Should be around 5 seconds
	if diff < 4*time.Second || diff > 6*time.Second {
		t.Errorf("expected deadline ~5s, got %v", diff)
	}
}

func TestDBContextLong(t *testing.T) {
	ctx, cancel := helpers.DBContextLong()
	defer cancel()

	deadline, ok := ctx.Deadline()
	if !ok {
		t.Error("expected deadline to be set")
	}

	now := time.Now()
	diff := deadline.Sub(now)

	// Should be around 10 seconds
	if diff < 9*time.Second || diff > 11*time.Second {
		t.Errorf("expected deadline ~10s, got %v", diff)
	}
}

func TestDBContextCancellation(t *testing.T) {
	ctx, cancel := helpers.DBContextShort()

	// Cancel immediately
	cancel()

	// Context should be cancelled
	select {
	case <-ctx.Done():
		// Good - context was cancelled
	default:
		t.Error("context should be cancelled after calling cancel()")
	}

	// Check error
	if ctx.Err() == nil {
		t.Error("expected non-nil error after cancellation")
	}
}

func TestStringSliceContains(t *testing.T) {
	tests := []struct {
		name  string
		slice []string
		item  string
		want  bool
	}{
		{
			name:  "contains",
			slice: []string{"a", "b", "c"},
			item:  "b",
			want:  true,
		},
		{
			name:  "not contains",
			slice: []string{"a", "b", "c"},
			item:  "d",
			want:  false,
		},
		{
			name:  "empty slice",
			slice: []string{},
			item:  "a",
			want:  false,
		},
		{
			name:  "nil slice",
			slice: nil,
			item:  "a",
			want:  false,
		},
		{
			name:  "empty string item",
			slice: []string{"a", "", "c"},
			item:  "",
			want:  true,
		},
		{
			name:  "case sensitive",
			slice: []string{"Apple", "Banana"},
			item:  "apple",
			want:  false,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			got := helpers.StringSliceContains(tt.slice, tt.item)
			if got != tt.want {
				t.Errorf("StringSliceContains() = %v, want %v", got, tt.want)
			}
		})
	}
}

func TestStringSliceFilter(t *testing.T) {
	tests := []struct {
		name      string
		slice     []string
		predicate func(string) bool
		want      []string
	}{
		{
			name:      "filter by prefix",
			slice:     []string{"apple", "banana", "apricot", "cherry"},
			predicate: func(s string) bool { return len(s) > 0 && s[0] == 'a' },
			want:      []string{"apple", "apricot"},
		},
		{
			name:      "filter empty strings",
			slice:     []string{"a", "", "b", "", "c"},
			predicate: func(s string) bool { return s != "" },
			want:      []string{"a", "b", "c"},
		},
		{
			name:      "all match",
			slice:     []string{"a", "b", "c"},
			predicate: func(s string) bool { return true },
			want:      []string{"a", "b", "c"},
		},
		{
			name:      "none match",
			slice:     []string{"a", "b", "c"},
			predicate: func(s string) bool { return false },
			want:      []string{},
		},
		{
			name:      "empty slice",
			slice:     []string{},
			predicate: func(s string) bool { return true },
			want:      []string{},
		},
		{
			name:      "nil slice",
			slice:     nil,
			predicate: func(s string) bool { return true },
			want:      []string{},
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			got := helpers.StringSliceFilter(tt.slice, tt.predicate)
			if len(got) != len(tt.want) {
				t.Errorf("StringSliceFilter() returned %d items, want %d", len(got), len(tt.want))
				return
			}
			for i := range got {
				if got[i] != tt.want[i] {
					t.Errorf("StringSliceFilter()[%d] = %q, want %q", i, got[i], tt.want[i])
				}
			}
		})
	}
}
