package api

import (
	"testing"
	"time"
)

func TestCalculateTrend(t *testing.T) {
	tests := []struct {
		name          string
		current       int64
		previous      int64
		wantDelta     *float64
		wantDirection string
	}{
		{
			name:          "zero previous, zero current - stable",
			current:       0,
			previous:      0,
			wantDelta:     nil,
			wantDirection: "stable",
		},
		{
			name:          "zero previous, positive current - new",
			current:       100,
			previous:      0,
			wantDelta:     nil,
			wantDirection: "new",
		},
		{
			name:          "100% increase - up",
			current:       200,
			previous:      100,
			wantDelta:     ptr(100.0),
			wantDirection: "up",
		},
		{
			name:          "50% decrease - down",
			current:       50,
			previous:      100,
			wantDelta:     ptr(-50.0),
			wantDirection: "down",
		},
		{
			name:          "0.3% increase - stable",
			current:       1003,
			previous:      1000,
			wantDelta:     ptr(0.3),
			wantDirection: "stable",
		},
		{
			name:          "0.5% increase - stable (boundary)",
			current:       1005,
			previous:      1000,
			wantDelta:     ptr(0.5),
			wantDirection: "stable",
		},
		{
			name:          "0.6% increase - up",
			current:       1006,
			previous:      1000,
			wantDelta:     ptr(0.6),
			wantDirection: "up",
		},
		{
			name:          "0.6% decrease - down",
			current:       994,
			previous:      1000,
			wantDelta:     ptr(-0.6),
			wantDirection: "down",
		},
		{
			name:          "no change - stable",
			current:       100,
			previous:      100,
			wantDelta:     ptr(0.0),
			wantDirection: "stable",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			got := CalculateTrend(tt.current, tt.previous)

			if tt.wantDelta == nil {
				if got.Delta != nil {
					t.Errorf("expected nil delta, got %v", *got.Delta)
				}
			} else {
				if got.Delta == nil {
					t.Errorf("expected delta %v, got nil", *tt.wantDelta)
				} else if *got.Delta != *tt.wantDelta {
					t.Errorf("expected delta %v, got %v", *tt.wantDelta, *got.Delta)
				}
			}

			if got.Direction != tt.wantDirection {
				t.Errorf("expected direction %q, got %q", tt.wantDirection, got.Direction)
			}
		})
	}
}

func TestCalculateTrendFloat(t *testing.T) {
	tests := []struct {
		name          string
		current       float64
		previous      float64
		wantDelta     *float64
		wantDirection string
	}{
		{
			name:          "zero previous, zero current - stable",
			current:       0,
			previous:      0,
			wantDelta:     nil,
			wantDirection: "stable",
		},
		{
			name:          "zero previous, positive current - new",
			current:       0.5,
			previous:      0,
			wantDelta:     nil,
			wantDirection: "new",
		},
		{
			name:          "50% increase - up",
			current:       0.15,
			previous:      0.10,
			wantDelta:     ptr(50.0),
			wantDirection: "up",
		},
		{
			name:          "33.3% decrease - down",
			current:       0.10,
			previous:      0.15,
			wantDelta:     ptr(-33.3),
			wantDirection: "down",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			got := CalculateTrendFloat(tt.current, tt.previous)

			if tt.wantDelta == nil {
				if got.Delta != nil {
					t.Errorf("expected nil delta, got %v", *got.Delta)
				}
			} else {
				if got.Delta == nil {
					t.Errorf("expected delta %v, got nil", *tt.wantDelta)
				} else if *got.Delta != *tt.wantDelta {
					t.Errorf("expected delta %v, got %v", *tt.wantDelta, *got.Delta)
				}
			}

			if got.Direction != tt.wantDirection {
				t.Errorf("expected direction %q, got %q", tt.wantDirection, got.Direction)
			}
		})
	}
}

func TestCalculateTrendFloatWithBaseline(t *testing.T) {
	tests := []struct {
		name          string
		current       float64
		previous      float64
		hasBaseline   bool
		wantDelta     *float64
		wantDirection string
	}{
		{
			name:          "no baseline, zero current",
			current:       0,
			previous:      0,
			hasBaseline:   false,
			wantDelta:     nil,
			wantDirection: "stable",
		},
		{
			name:          "no baseline, positive current",
			current:       0.2,
			previous:      0,
			hasBaseline:   false,
			wantDelta:     nil,
			wantDirection: "new",
		},
		{
			name:          "baseline present, zero previous and current",
			current:       0,
			previous:      0,
			hasBaseline:   true,
			wantDelta:     nil,
			wantDirection: "stable",
		},
		{
			name:          "baseline present, zero previous to positive",
			current:       0.2,
			previous:      0,
			hasBaseline:   true,
			wantDelta:     nil,
			wantDirection: "up",
		},
		{
			name:          "baseline present, increase",
			current:       0.15,
			previous:      0.10,
			hasBaseline:   true,
			wantDelta:     ptr(50.0),
			wantDirection: "up",
		},
		{
			name:          "baseline present, no change",
			current:       0.10,
			previous:      0.10,
			hasBaseline:   true,
			wantDelta:     ptr(0.0),
			wantDirection: "stable",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			got := CalculateTrendFloatWithBaseline(tt.current, tt.previous, tt.hasBaseline)

			if tt.wantDelta == nil {
				if got.Delta != nil {
					t.Errorf("expected nil delta, got %v", *got.Delta)
				}
			} else {
				if got.Delta == nil {
					t.Errorf("expected delta %v, got nil", *tt.wantDelta)
				} else if *got.Delta != *tt.wantDelta {
					t.Errorf("expected delta %v, got %v", *tt.wantDelta, *got.Delta)
				}
			}

			if got.Direction != tt.wantDirection {
				t.Errorf("expected direction %q, got %q", tt.wantDirection, got.Direction)
			}
		})
	}
}

func TestPreviousPeriodWindow(t *testing.T) {
	// These tests verify the structure of the period calculation
	// Not testing exact timestamps since they depend on current time

	tests := []struct {
		period       string
		expectZero   bool
		expectWindow bool
	}{
		{"today", false, true},
		{"last7d", false, true},
		{"last30d", false, true},
		{"week", false, true},
		{"month", false, true},
		{"all", true, false},
		{"invalid", true, false},
	}

	for _, tt := range tests {
		t.Run(tt.period, func(t *testing.T) {
			start, end := previousPeriodWindow(tt.period)

			if tt.expectZero {
				if !start.IsZero() || !end.IsZero() {
					t.Errorf("expected zero times for period %q", tt.period)
				}
			} else {
				if start.IsZero() || end.IsZero() {
					t.Errorf("expected non-zero times for period %q", tt.period)
				}
				if !start.Before(end) {
					t.Errorf("expected start < end for period %q", tt.period)
				}
			}
		})
	}
}

func TestPreviousPeriodWindowToday(t *testing.T) {
	start, end := previousPeriodWindow("today")

	// Previous period for "today" should be "yesterday"
	now := time.Now().UTC()
	startOfToday := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, time.UTC)
	yesterday := startOfToday.AddDate(0, 0, -1)

	if !start.Equal(yesterday) {
		t.Errorf("expected start to be yesterday (%v), got %v", yesterday, start)
	}
	if !end.Equal(startOfToday) {
		t.Errorf("expected end to be start of today (%v), got %v", startOfToday, end)
	}
}

func TestPreviousPeriodWindowLast7d(t *testing.T) {
	start, end := previousPeriodWindow("last7d")

	now := time.Now().UTC()
	expected14DaysAgo := now.AddDate(0, 0, -14)
	expected7DaysAgo := now.AddDate(0, 0, -7)

	// Allow 1 second tolerance for test execution time
	if start.Sub(expected14DaysAgo).Abs() > time.Second {
		t.Errorf("expected start ~14 days ago, got %v (expected %v)", start, expected14DaysAgo)
	}
	if end.Sub(expected7DaysAgo).Abs() > time.Second {
		t.Errorf("expected end ~7 days ago, got %v (expected %v)", end, expected7DaysAgo)
	}
}

func ptr(v float64) *float64 {
	return &v
}
