package alerts

import (
	"testing"
	"time"
)

func newManagerWithQuietHoursSuppress(s QuietHoursSuppression) *Manager {
	m := NewManager()
	m.config.Schedule.QuietHours = QuietHours{
		Enabled:  true,
		Start:    "00:00",
		End:      "23:59",
		Timezone: "UTC",
		Days: map[string]bool{
			"monday":    true,
			"tuesday":   true,
			"wednesday": true,
			"thursday":  true,
			"friday":    true,
			"saturday":  true,
			"sunday":    true,
		},
		Suppress: s,
	}
	return m
}

func TestShouldSuppressNotificationQuietHours(t *testing.T) {
	t.Run("non-critical alerts suppressed by default", func(t *testing.T) {
		m := newManagerWithQuietHoursSuppress(QuietHoursSuppression{})
		alert := &Alert{ID: "warn", Type: "cpu", Level: AlertLevelWarning}
		suppressed, reason := m.shouldSuppressNotification(alert)
		if !suppressed || reason != "non-critical" {
			t.Fatalf("expected non-critical alert to be suppressed, got suppressed=%t reason=%q", suppressed, reason)
		}
	})

	t.Run("critical offline alerts suppressed when configured", func(t *testing.T) {
		m := newManagerWithQuietHoursSuppress(QuietHoursSuppression{Offline: true})
		alert := &Alert{ID: "offline", Type: "connectivity", Level: AlertLevelCritical}
		suppressed, reason := m.shouldSuppressNotification(alert)
		if !suppressed || reason != "offline" {
			t.Fatalf("expected offline alert suppression, got suppressed=%t reason=%q", suppressed, reason)
		}
	})

	t.Run("critical performance alerts require opt-in", func(t *testing.T) {
		m := newManagerWithQuietHoursSuppress(QuietHoursSuppression{})
		alert := &Alert{ID: "perf", Type: "cpu", Level: AlertLevelCritical}
		suppressed, reason := m.shouldSuppressNotification(alert)
		if suppressed {
			t.Fatalf("expected performance alert not to be suppressed, got reason=%q", reason)
		}
	})

	t.Run("critical performance alerts suppressed when enabled", func(t *testing.T) {
		m := newManagerWithQuietHoursSuppress(QuietHoursSuppression{Performance: true})
		alert := &Alert{ID: "perf-enabled", Type: "cpu", Level: AlertLevelCritical}
		suppressed, reason := m.shouldSuppressNotification(alert)
		if !suppressed || reason != "performance" {
			t.Fatalf("expected performance alert suppression, got suppressed=%t reason=%q", suppressed, reason)
		}
	})

	t.Run("critical storage alerts suppressed when enabled", func(t *testing.T) {
		m := newManagerWithQuietHoursSuppress(QuietHoursSuppression{Storage: true})
		alert := &Alert{ID: "storage", Type: "usage", Level: AlertLevelCritical}
		suppressed, reason := m.shouldSuppressNotification(alert)
		if !suppressed || reason != "storage" {
			t.Fatalf("expected storage alert suppression, got suppressed=%t reason=%q", suppressed, reason)
		}
	})
}

func TestIsInQuietHours(t *testing.T) {
	t.Parallel()

	t.Run("disabled returns false", func(t *testing.T) {
		t.Parallel()
		m := NewManager()
		m.mu.Lock()
		m.config.Schedule.QuietHours.Enabled = false
		m.mu.Unlock()

		m.mu.RLock()
		result := m.isInQuietHours()
		m.mu.RUnlock()

		if result {
			t.Errorf("isInQuietHours() = true, want false when disabled")
		}
	})

	t.Run("invalid timezone falls back to local", func(t *testing.T) {
		t.Parallel()
		m := NewManager()
		m.mu.Lock()
		m.config.Schedule.QuietHours = QuietHours{
			Enabled:  true,
			Start:    "00:00",
			End:      "23:59",
			Timezone: "Invalid/Timezone",
			Days: map[string]bool{
				"monday": true, "tuesday": true, "wednesday": true,
				"thursday": true, "friday": true, "saturday": true, "sunday": true,
			},
		}
		m.mu.Unlock()

		// Should not panic, should fall back to local time
		m.mu.RLock()
		result := m.isInQuietHours()
		m.mu.RUnlock()

		// With all days enabled and 00:00-23:59, it should be true
		if !result {
			t.Errorf("isInQuietHours() = false, want true (invalid timezone should fall back to local)")
		}
	})

	t.Run("day not enabled returns false", func(t *testing.T) {
		t.Parallel()
		m := NewManager()
		now := time.Now()
		currentDay := now.Format("Monday")

		m.mu.Lock()
		m.config.Schedule.QuietHours = QuietHours{
			Enabled:  true,
			Start:    "00:00",
			End:      "23:59",
			Timezone: "UTC",
			Days:     map[string]bool{}, // No days enabled
		}
		m.mu.Unlock()

		m.mu.RLock()
		result := m.isInQuietHours()
		m.mu.RUnlock()

		if result {
			t.Errorf("isInQuietHours() = true, want false (day %s not enabled)", currentDay)
		}
	})

	t.Run("invalid start time returns false", func(t *testing.T) {
		t.Parallel()
		m := NewManager()

		m.mu.Lock()
		m.config.Schedule.QuietHours = QuietHours{
			Enabled:  true,
			Start:    "invalid",
			End:      "23:59",
			Timezone: "UTC",
			Days: map[string]bool{
				"monday": true, "tuesday": true, "wednesday": true,
				"thursday": true, "friday": true, "saturday": true, "sunday": true,
			},
		}
		m.mu.Unlock()

		m.mu.RLock()
		result := m.isInQuietHours()
		m.mu.RUnlock()

		if result {
			t.Errorf("isInQuietHours() = true, want false (invalid start time)")
		}
	})

	t.Run("invalid end time returns false", func(t *testing.T) {
		t.Parallel()
		m := NewManager()

		m.mu.Lock()
		m.config.Schedule.QuietHours = QuietHours{
			Enabled:  true,
			Start:    "00:00",
			End:      "invalid",
			Timezone: "UTC",
			Days: map[string]bool{
				"monday": true, "tuesday": true, "wednesday": true,
				"thursday": true, "friday": true, "saturday": true, "sunday": true,
			},
		}
		m.mu.Unlock()

		m.mu.RLock()
		result := m.isInQuietHours()
		m.mu.RUnlock()

		if result {
			t.Errorf("isInQuietHours() = true, want false (invalid end time)")
		}
	})

	t.Run("overnight quiet hours spanning midnight", func(t *testing.T) {
		t.Parallel()
		m := NewManager()

		// Set up overnight quiet hours (22:00 to 06:00)
		m.mu.Lock()
		m.config.Schedule.QuietHours = QuietHours{
			Enabled:  true,
			Start:    "22:00",
			End:      "06:00", // End before start = overnight
			Timezone: "UTC",
			Days: map[string]bool{
				"monday": true, "tuesday": true, "wednesday": true,
				"thursday": true, "friday": true, "saturday": true, "sunday": true,
			},
		}
		m.mu.Unlock()

		// This tests the overnight branch - exact result depends on current time
		// but should not panic
		m.mu.RLock()
		_ = m.isInQuietHours()
		m.mu.RUnlock()
	})

	t.Run("normal daytime quiet hours", func(t *testing.T) {
		t.Parallel()
		m := NewManager()

		// Set up daytime quiet hours (09:00 to 17:00)
		m.mu.Lock()
		m.config.Schedule.QuietHours = QuietHours{
			Enabled:  true,
			Start:    "09:00",
			End:      "17:00",
			Timezone: "UTC",
			Days: map[string]bool{
				"monday": true, "tuesday": true, "wednesday": true,
				"thursday": true, "friday": true, "saturday": true, "sunday": true,
			},
		}
		m.mu.Unlock()

		// Should not panic, result depends on current time
		m.mu.RLock()
		_ = m.isInQuietHours()
		m.mu.RUnlock()
	})

	t.Run("outside quiet hours window", func(t *testing.T) {
		t.Parallel()
		m := NewManager()

		// Use a time window that's definitely not now (narrow window in far past/future time)
		// This creates a window from 03:00-03:01 which is unlikely to match current time
		m.mu.Lock()
		m.config.Schedule.QuietHours = QuietHours{
			Enabled:  true,
			Start:    "03:00",
			End:      "03:01", // 1 minute window
			Timezone: "UTC",
			Days: map[string]bool{
				"monday": true, "tuesday": true, "wednesday": true,
				"thursday": true, "friday": true, "saturday": true, "sunday": true,
			},
		}
		m.mu.Unlock()

		m.mu.RLock()
		result := m.isInQuietHours()
		m.mu.RUnlock()

		// Very narrow window, likely false but depends on exact test execution time
		// Main purpose is to exercise the "outside window" return false path
		_ = result
	})
}
