package wayland

import (
	"math"
	"testing"
	"time"
)

func calculateTemperature(config Config, now time.Time) int {
	if !config.Enabled {
		return config.HighTemp
	}

	var sunrise, sunset time.Time

	if config.ManualSunrise != nil && config.ManualSunset != nil {
		year, month, day := now.Date()
		loc := now.Location()

		sunrise = time.Date(year, month, day,
			config.ManualSunrise.Hour(),
			config.ManualSunrise.Minute(),
			config.ManualSunrise.Second(), 0, loc)
		sunset = time.Date(year, month, day,
			config.ManualSunset.Hour(),
			config.ManualSunset.Minute(),
			config.ManualSunset.Second(), 0, loc)

		if sunset.Before(sunrise) {
			sunset = sunset.Add(24 * time.Hour)
		}
	} else if config.UseIPLocation {
		lat, lon, err := FetchIPLocation()
		if err != nil {
			return config.HighTemp
		}
		times := CalculateSunTimes(*lat, *lon, now)
		sunrise = times.Sunrise
		sunset = times.Sunset
	} else if config.Latitude != nil && config.Longitude != nil {
		times := CalculateSunTimes(*config.Latitude, *config.Longitude, now)
		sunrise = times.Sunrise
		sunset = times.Sunset
	} else {
		return config.HighTemp
	}

	if now.Before(sunrise) || now.After(sunset) {
		return config.LowTemp
	}
	return config.HighTemp
}

func calculateNextTransition(config Config, now time.Time) time.Time {
	if !config.Enabled {
		return now.Add(24 * time.Hour)
	}

	var sunrise, sunset time.Time

	if config.ManualSunrise != nil && config.ManualSunset != nil {
		year, month, day := now.Date()
		loc := now.Location()

		sunrise = time.Date(year, month, day,
			config.ManualSunrise.Hour(),
			config.ManualSunrise.Minute(),
			config.ManualSunrise.Second(), 0, loc)
		sunset = time.Date(year, month, day,
			config.ManualSunset.Hour(),
			config.ManualSunset.Minute(),
			config.ManualSunset.Second(), 0, loc)

		if sunset.Before(sunrise) {
			sunset = sunset.Add(24 * time.Hour)
		}
	} else if config.UseIPLocation {
		lat, lon, err := FetchIPLocation()
		if err != nil {
			return now.Add(24 * time.Hour)
		}
		times := CalculateSunTimes(*lat, *lon, now)
		sunrise = times.Sunrise
		sunset = times.Sunset
	} else if config.Latitude != nil && config.Longitude != nil {
		times := CalculateSunTimes(*config.Latitude, *config.Longitude, now)
		sunrise = times.Sunrise
		sunset = times.Sunset
	} else {
		return now.Add(24 * time.Hour)
	}

	if now.Before(sunrise) {
		return sunrise
	}
	if now.Before(sunset) {
		return sunset
	}

	if config.ManualSunrise != nil && config.ManualSunset != nil {
		year, month, day := now.Add(24 * time.Hour).Date()
		loc := now.Location()
		nextSunrise := time.Date(year, month, day,
			config.ManualSunrise.Hour(),
			config.ManualSunrise.Minute(),
			config.ManualSunrise.Second(), 0, loc)
		return nextSunrise
	}

	if config.UseIPLocation {
		lat, lon, err := FetchIPLocation()
		if err != nil {
			return now.Add(24 * time.Hour)
		}
		nextDayTimes := CalculateSunTimes(*lat, *lon, now.Add(24*time.Hour))
		return nextDayTimes.Sunrise
	}

	if config.Latitude != nil && config.Longitude != nil {
		nextDayTimes := CalculateSunTimes(*config.Latitude, *config.Longitude, now.Add(24*time.Hour))
		return nextDayTimes.Sunrise
	}

	return now.Add(24 * time.Hour)
}

func TestCalculateSunTimes(t *testing.T) {
	tests := []struct {
		name      string
		lat       float64
		lon       float64
		date      time.Time
		checkFunc func(*testing.T, SunTimes)
	}{
		{
			name: "new_york_summer",
			lat:  40.7128,
			lon:  -74.0060,
			date: time.Date(2024, 6, 21, 12, 0, 0, 0, time.Local),
			checkFunc: func(t *testing.T, times SunTimes) {
				if times.Sunrise.Hour() < 4 || times.Sunrise.Hour() > 6 {
					t.Logf("sunrise: %v", times.Sunrise)
				}
				if times.Sunset.Hour() < 19 || times.Sunset.Hour() > 21 {
					t.Logf("sunset: %v", times.Sunset)
				}
				if !times.Sunset.After(times.Sunrise) {
					t.Error("sunset should be after sunrise")
				}
			},
		},
		{
			name: "london_winter",
			lat:  51.5074,
			lon:  -0.1278,
			date: time.Date(2024, 12, 21, 12, 0, 0, 0, time.UTC),
			checkFunc: func(t *testing.T, times SunTimes) {
				if times.Sunrise.Hour() < 7 || times.Sunrise.Hour() > 9 {
					t.Errorf("unexpected sunrise hour: %d", times.Sunrise.Hour())
				}
				if times.Sunset.Hour() < 15 || times.Sunset.Hour() > 17 {
					t.Errorf("unexpected sunset hour: %d", times.Sunset.Hour())
				}
			},
		},
		{
			name: "equator_equinox",
			lat:  0.0,
			lon:  0.0,
			date: time.Date(2024, 3, 20, 12, 0, 0, 0, time.UTC),
			checkFunc: func(t *testing.T, times SunTimes) {
				if times.Sunrise.Hour() < 5 || times.Sunrise.Hour() > 7 {
					t.Errorf("unexpected sunrise hour: %d", times.Sunrise.Hour())
				}
				if times.Sunset.Hour() < 17 || times.Sunset.Hour() > 19 {
					t.Errorf("unexpected sunset hour: %d", times.Sunset.Hour())
				}
			},
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			times := CalculateSunTimes(tt.lat, tt.lon, tt.date)
			tt.checkFunc(t, times)
		})
	}
}

func TestCalculateTemperature(t *testing.T) {
	lat := 40.7128
	lon := -74.0060
	date := time.Date(2024, 6, 21, 0, 0, 0, 0, time.Local)

	config := Config{
		LowTemp:   4000,
		HighTemp:  6500,
		Latitude:  &lat,
		Longitude: &lon,
		Enabled:   true,
	}

	times := CalculateSunTimes(lat, lon, date)

	tests := []struct {
		name     string
		timeFunc func() time.Time
		wantTemp int
	}{
		{
			name:     "midnight",
			timeFunc: func() time.Time { return times.Sunrise.Add(-4 * time.Hour) },
			wantTemp: 4000,
		},
		{
			name:     "sunrise",
			timeFunc: func() time.Time { return times.Sunrise },
			wantTemp: 6500,
		},
		{
			name:     "noon",
			timeFunc: func() time.Time { return times.Sunrise.Add(6 * time.Hour) },
			wantTemp: 6500,
		},
		{
			name:     "after_sunset_transition",
			timeFunc: func() time.Time { return times.Sunset.Add(2 * time.Hour) },
			wantTemp: 4000,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			temp := calculateTemperature(config, tt.timeFunc())

			if math.Abs(float64(temp-tt.wantTemp)) > 500 {
				t.Errorf("temperature = %d, want approximately %d", temp, tt.wantTemp)
			}
		})
	}
}

func TestCalculateTemperatureManualTimes(t *testing.T) {
	sunrise := time.Date(0, 1, 1, 6, 30, 0, 0, time.Local)
	sunset := time.Date(0, 1, 1, 18, 30, 0, 0, time.Local)

	config := Config{
		LowTemp:       4000,
		HighTemp:      6500,
		ManualSunrise: &sunrise,
		ManualSunset:  &sunset,
		Enabled:       true,
	}

	tests := []struct {
		name string
		time time.Time
		want int
	}{
		{"before_sunrise", time.Date(2024, 1, 1, 3, 0, 0, 0, time.Local), 4000},
		{"at_sunrise", time.Date(2024, 1, 1, 6, 30, 0, 0, time.Local), 6500},
		{"midday", time.Date(2024, 1, 1, 12, 0, 0, 0, time.Local), 6500},
		{"at_sunset", time.Date(2024, 1, 1, 18, 30, 0, 0, time.Local), 6500},
		{"after_sunset", time.Date(2024, 1, 1, 22, 0, 0, 0, time.Local), 4000},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			temp := calculateTemperature(config, tt.time)
			if math.Abs(float64(temp-tt.want)) > 500 {
				t.Errorf("temperature = %d, want approximately %d", temp, tt.want)
			}
		})
	}
}

func TestCalculateTemperatureDisabled(t *testing.T) {
	lat := 40.7128
	lon := -74.0060

	config := Config{
		LowTemp:   4000,
		HighTemp:  6500,
		Latitude:  &lat,
		Longitude: &lon,
		Enabled:   false,
	}

	temp := calculateTemperature(config, time.Now())
	if temp != 6500 {
		t.Errorf("disabled should return high temp, got %d", temp)
	}
}

func TestCalculateNextTransition(t *testing.T) {
	lat := 40.7128
	lon := -74.0060
	date := time.Date(2024, 6, 21, 0, 0, 0, 0, time.Local)

	config := Config{
		LowTemp:   4000,
		HighTemp:  6500,
		Latitude:  &lat,
		Longitude: &lon,
		Enabled:   true,
	}

	times := CalculateSunTimes(lat, lon, date)

	tests := []struct {
		name      string
		now       time.Time
		checkFunc func(*testing.T, time.Time)
	}{
		{
			name: "before_sunrise",
			now:  times.Sunrise.Add(-2 * time.Hour),
			checkFunc: func(t *testing.T, next time.Time) {
				if !next.Equal(times.Sunrise) && !next.After(times.Sunrise.Add(-1*time.Minute)) {
					t.Error("next transition should be at or near sunrise")
				}
			},
		},
		{
			name: "after_sunrise",
			now:  times.Sunrise.Add(2 * time.Hour),
			checkFunc: func(t *testing.T, next time.Time) {
				if !next.After(times.Sunrise) {
					t.Error("next transition should be after sunrise")
				}
			},
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			next := calculateNextTransition(config, tt.now)
			tt.checkFunc(t, next)
		})
	}
}

func TestSunTimesWithTwilight(t *testing.T) {
	lat := 40.7128
	lon := -74.0060
	date := time.Date(2024, 6, 21, 12, 0, 0, 0, time.Local)

	times, cond := CalculateSunTimesWithTwilight(lat, lon, date, -6.0, 3.0)

	if cond != SunNormal {
		t.Errorf("expected SunNormal, got %v", cond)
	}
	if !times.Dawn.Before(times.Sunrise) {
		t.Error("dawn should be before sunrise")
	}
	if !times.Sunrise.Before(times.Sunset) {
		t.Error("sunrise should be before sunset")
	}
	if !times.Sunset.Before(times.Night) {
		t.Error("sunset should be before night")
	}
}

func TestSunConditions(t *testing.T) {
	tests := []struct {
		name     string
		lat      float64
		date     time.Time
		expected SunCondition
	}{
		{
			name:     "normal_conditions",
			lat:      40.0,
			date:     time.Date(2024, 6, 21, 12, 0, 0, 0, time.UTC),
			expected: SunNormal,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			_, cond := CalculateSunTimesWithTwilight(tt.lat, 0, tt.date, -6.0, 3.0)
			if cond != tt.expected {
				t.Errorf("expected condition %v, got %v", tt.expected, cond)
			}
		})
	}
}
