package models

import (
	"testing"
	"time"

	"github.com/stretchr/testify/assert"
)

func TestPeriod_StartTime(t *testing.T) {
	p := Period("2024-07-23 10:05:00")

	location, err := time.LoadLocation("UTC")

	assert.NoError(t, err)
	expected, _ := time.ParseInLocation("2006-01-02 15:04:05", "2024-07-23 10:05:00", location)
	assert.Equal(t, expected, p.StartTime(), "StartTime should parse the string correctly")

	// Test an edge case for parsing.
	p2 := Period("2024-01-01 00:00:00")
	expected2, _ := time.ParseInLocation("2006-01-02 15:04:05", "2024-01-01 00:00:00", location)
	assert.Equal(t, expected2, p2.StartTime(), "StartTime should handle start of year/day correctly")
}

func TestPeriod_EndTime(t *testing.T) {
	// Test a standard period.
	p := Period("2024-07-23 10:05:00")

	location, err := time.LoadLocation("UTC")
	assert.NoError(t, err)
	startTime, _ := time.ParseInLocation("2006-01-02 15:04:05", "2024-07-23 10:05:00", location)
	expected := startTime.Add(PeriodInterval)
	assert.Equal(t, expected, p.EndTime(), "EndTime should add PeriodInterval to StartTime")

	// Test another period.
	p2 := Period("2024-07-23 10:00:00")
	startTime2, _ := time.ParseInLocation("2006-01-02 15:04:05", "2024-07-23 10:00:00", location)
	expected2 := startTime2.Add(PeriodInterval)
	assert.Equal(t, expected2, p2.EndTime(), "EndTime should consistently add PeriodInterval")
}

func TestFromStartTime(t *testing.T) {
	// Test creating Period from a time.Time.

	location, err := time.LoadLocation("UTC")
	assert.NoError(t, err)
	now := time.Date(2024, time.July, 23, 10, 30, 0, 0, location)
	expected := Period("2024-07-23 10:30:00")
	assert.Equal(t, expected, FromStartTime(now), "FromStartTime should format the time correctly to Period")

	// Test another time.
	midnight := time.Date(2025, time.January, 1, 0, 0, 0, 0, location)
	expectedMidnight := Period("2025-01-01 00:00:00")
	assert.Equal(t, expectedMidnight, FromStartTime(midnight), "FromStartTime should handle midnight correctly")
}

func TestFromEndTime(t *testing.T) {
	// Test creating Period from an end time.
	location, err := time.LoadLocation("UTC")
	assert.NoError(t, err)
	endTime := time.Date(2024, time.July, 23, 10, 35, 0, 0, location)
	// Expected start time is endTime - PeriodInterval
	expectedStartTime := endTime.Add(-1 * PeriodInterval)
	expectedPeriod := Period(expectedStartTime.Format("2006-01-02 15:04:05"))
	assert.Equal(t, expectedPeriod, FromEndTime(endTime), "FromEndTime should calculate Period based on endTime minus interval")

	// Test an end time that is exactly an interval boundary.
	endTime2 := time.Date(2024, time.July, 23, 10, 0, 0, 0, location)
	expectedStartTime2 := endTime2.Add(-1 * PeriodInterval)
	expectedPeriod2 := Period(expectedStartTime2.Format("2006-01-02 15:04:05"))
	assert.Equal(t, expectedPeriod2, FromEndTime(endTime2), "FromEndTime should correctly handle interval boundaries")
}

func TestPeriod_Next(t *testing.T) {
	// Test a standard next period calculation.
	p := Period("2024-07-23 10:05:00")
	expectedTime := p.StartTime().Add(PeriodInterval)
	expectedPeriod := Period(expectedTime.Format("2006-01-02 15:04:05"))
	assert.Equal(t, expectedPeriod, p.Next(), "Next should return the period starting right after the current period ends")

	// Test next period at the end of an hour.
	p2 := Period("2024-07-23 10:55:00")
	expectedTime2 := p2.StartTime().Add(PeriodInterval)
	expectedPeriod2 := Period(expectedTime2.Format("2006-01-02 15:04:05"))
	assert.Equal(t, expectedPeriod2, p2.Next(), "Next should correctly handle crossing hour boundaries")
}

func TestGetCurrentMonth(t *testing.T) {
	// Test with a date in the middle of the month.
	location, err := time.LoadLocation("UTC")
	assert.NoError(t, err)
	date := time.Date(2024, time.July, 15, 10, 0, 0, 0, location)
	expectedMonthStart := time.Date(2024, time.July, 1, 0, 0, 0, 0, location)
	assert.Equal(t, expectedMonthStart, GetCurrentMonth(date), "GetCurrentMonth should return the first day of the current month")

	// Test with a date on the first day of the month.
	date2 := time.Date(2024, time.March, 1, 5, 0, 0, 0, location)
	expectedMonthStart2 := time.Date(2024, time.March, 1, 0, 0, 0, 0, location)
	assert.Equal(t, expectedMonthStart2, GetCurrentMonth(date2), "GetCurrentMonth should work correctly for the first day of the month")

	// Test with a date on the last day of the month.
	date3 := time.Date(2024, time.February, 29, 23, 59, 59, 0, location) // Leap year
	expectedMonthStart3 := time.Date(2024, time.February, 1, 0, 0, 0, 0, location)
	assert.Equal(t, expectedMonthStart3, GetCurrentMonth(date3), "GetCurrentMonth should work correctly for the last day of the month")
}

func TestGetLastMonth(t *testing.T) {
	// Test with a date in July.
	location, err := time.LoadLocation("UTC")
	assert.NoError(t, err)
	date := time.Date(2024, time.July, 15, 10, 0, 0, 0, location)
	expectedLastMonthStart := time.Date(2024, time.June, 1, 0, 0, 0, 0, location)
	assert.Equal(t, expectedLastMonthStart, GetLastMonth(date), "GetLastMonth should return the first day of the previous month")

	// Test with a date in January (should go to previous year).
	date2 := time.Date(2024, time.January, 10, 5, 0, 0, 0, location)
	expectedLastMonthStart2 := time.Date(2023, time.December, 1, 0, 0, 0, 0, location)
	assert.Equal(t, expectedLastMonthStart2, GetLastMonth(date2), "GetLastMonth should handle year transition correctly")

	// Test with a date on the first day of the month.
	date3 := time.Date(2024, time.April, 1, 5, 0, 0, 0, location)
	expectedLastMonthStart3 := time.Date(2024, time.March, 1, 0, 0, 0, 0, location)
	assert.Equal(t, expectedLastMonthStart3, GetLastMonth(date3), "GetLastMonth should work correctly for the first day of the month")
}
