package service

import (
	"time"
	"xboard-go/internal/database"
	"xboard-go/internal/model"
)

const (
	ResetTrafficFirstDayMonth = 0
	ResetTrafficMonthly       = 1
	ResetTrafficNever         = 2
	ResetTrafficFirstDayYear  = 3
	ResetTrafficYearly        = 4
)

func GetResetDay(user *model.User) *int {
	nextResetTime := CalculateNextResetTime(user)
	if nextResetTime == nil {
		return nil
	}

	now := time.Now()
	if nextResetTime.Before(now) {
		zero := 0
		return &zero
	}

	days := int(nextResetTime.Sub(now).Hours()/24) + 1
	return &days
}

func CalculateNextResetTime(user *model.User) *time.Time {
	if user.PlanID == nil {
		return nil
	}
	
	// We need to fetch the plan's reset method. 
	// For simplicity in this service, we assume the caller has access or we fetch it here.
	// However, usually it's better to pass the plan or the reset method.
	// Let's implement it carefully.
	
	// If the user has no plan or reset method is never, return nil.
	// Since we don't have the plan object here, we'll need to fetch it if not provided.
	// For now, let's look at how Xboard does it (it uses $user->plan).
	
	// Wait, I should check if I can get the plan easily.
	var resetMethod int
	if err := database.DB.Model(&model.Plan{}).Select("reset_traffic_method").Where("id = ?", *user.PlanID).Scan(&resetMethod).Error; err != nil {
		return nil
	}

	if resetMethod == ResetTrafficNever || user.ExpiredAt == nil {
		return nil
	}

	now := time.Now()
	expiredAt := time.Unix(*user.ExpiredAt, 0)

	switch resetMethod {
	case ResetTrafficFirstDayMonth:
		return getNextMonthFirstDay(now)
	case ResetTrafficMonthly:
		return getNextMonthlyReset(user, now, expiredAt)
	case ResetTrafficFirstDayYear:
		return getNextYearFirstDay(now)
	case ResetTrafficYearly:
		return getNextYearlyReset(user, now, expiredAt)
	default:
		return nil
	}
}

func getNextMonthFirstDay(from time.Time) *time.Time {
	t := time.Date(from.Year(), from.Month(), 1, 0, 0, 0, 0, from.Location()).AddDate(0, 1, 0)
	return &t
}

func getNextMonthlyReset(user *model.User, from time.Time, expiredAt time.Time) *time.Time {
	resetDay := expiredAt.Day()
	
	// Try current month
	target := time.Date(from.Year(), from.Month(), resetDay, expiredAt.Hour(), expiredAt.Minute(), expiredAt.Second(), 0, from.Location())
	if target.After(from) {
		return &target
	}
	
	// Try next month
	target = target.AddDate(0, 1, 0)
	// Handle cases where next month doesn't have that day (e.g. Feb 30)
	if target.Day() != resetDay {
		// Go back to last day of intended month
		target = time.Date(target.Year(), target.Month(), 0, expiredAt.Hour(), expiredAt.Minute(), expiredAt.Second(), 0, from.Location())
	}
	
	return &target
}

func getNextYearFirstDay(from time.Time) *time.Time {
	t := time.Date(from.Year()+1, 1, 1, 0, 0, 0, 0, from.Location())
	return &t
}

func getNextYearlyReset(user *model.User, from time.Time, expiredAt time.Time) *time.Time {
	resetMonth := expiredAt.Month()
	resetDay := expiredAt.Day()
	
	// Try current year
	target := time.Date(from.Year(), resetMonth, resetDay, expiredAt.Hour(), expiredAt.Minute(), expiredAt.Second(), 0, from.Location())
	if target.After(from) {
		return &target
	}
	
	// Try next year
	target = time.Date(from.Year()+1, resetMonth, resetDay, expiredAt.Hour(), expiredAt.Minute(), expiredAt.Second(), 0, from.Location())
	return &target
}
