package cardobservation

import (
	"math"
	"time"

	"github.com/break/junhong_cmp_fiber/pkg/errors"
)

// TrafficObservation 是领域层接收的标准运营商流量读数。
type TrafficObservation struct {
	CardID           uint
	GatewayReadingMB float64
	ResetDay         int
	Metadata         ObservationMetadata
}

// CardTrafficSnapshot 是流量规则需要的最小卡快照。
type CardTrafficSnapshot struct {
	CardID                uint
	DataUsageMB           int64
	CurrentMonthUsageMB   float64
	CurrentMonthStartDate *time.Time
	LastMonthTotalMB      float64
	LastGatewayReadingMB  float64
}

// TrafficDecision 描述一次流量观测应持久化的最终值。
type TrafficDecision struct {
	IncrementMB           float64
	ReadingAccepted       bool
	CrossMonth            bool
	DataUsageMB           int64
	CurrentMonthUsageMB   float64
	CurrentMonthStartDate time.Time
	LastMonthTotalMB      float64
	LastGatewayReadingMB  float64
}

// ApplyTraffic 应用运营商重置、跨月和异常下降保护规则。
func ApplyTraffic(snapshot CardTrafficSnapshot, observation TrafficObservation) (TrafficDecision, error) {
	if err := validateObservationMetadata(observation.CardID, observation.Metadata); err != nil {
		return TrafficDecision{}, err
	}
	if math.IsNaN(observation.GatewayReadingMB) || math.IsInf(observation.GatewayReadingMB, 0) || observation.GatewayReadingMB < 0 {
		return TrafficDecision{}, errors.New(errors.CodeInvalidParam, "流量观测读数无效")
	}
	if observation.ResetDay < 1 || observation.ResetDay > 31 {
		return TrafficDecision{}, errors.New(errors.CodeInvalidParam, "运营商流量重置日无效")
	}
	now := observation.Metadata.ObservedAt
	monthStart := time.Date(now.Year(), now.Month(), 1, 0, 0, 0, 0, now.Location())
	decision := TrafficDecision{
		ReadingAccepted: true, DataUsageMB: snapshot.DataUsageMB,
		CurrentMonthUsageMB: snapshot.CurrentMonthUsageMB, CurrentMonthStartDate: monthStart,
		LastMonthTotalMB: snapshot.LastMonthTotalMB, LastGatewayReadingMB: observation.GatewayReadingMB,
	}
	increment := observation.GatewayReadingMB - snapshot.LastGatewayReadingMB
	if increment < 0 {
		if isTrafficResetWindow(now, observation.ResetDay) {
			increment = observation.GatewayReadingMB
		} else {
			increment = 0
			decision.ReadingAccepted = false
			decision.LastGatewayReadingMB = snapshot.LastGatewayReadingMB
		}
	}
	decision.IncrementMB = increment
	decision.CrossMonth = snapshot.CurrentMonthStartDate == nil || snapshot.CurrentMonthStartDate.Before(monthStart)
	if decision.CrossMonth {
		decision.LastMonthTotalMB = snapshot.CurrentMonthUsageMB
		decision.CurrentMonthUsageMB = increment
	} else if increment > 0 {
		decision.CurrentMonthUsageMB += increment
	}
	if increment > 0 {
		decision.DataUsageMB += int64(increment)
	}
	return decision, nil
}

func validateObservationMetadata(cardID uint, metadata ObservationMetadata) error {
	return validateObservation(RealnameObservation{CardID: cardID, Verified: true, Metadata: metadata})
}

func isTrafficResetWindow(now time.Time, resetDay int) bool {
	if now.Day() == resetDay {
		return true
	}
	resetDate := time.Date(now.Year(), now.Month(), resetDay, 0, 0, 0, 0, now.Location())
	return now.Day() == resetDate.AddDate(0, 0, -1).Day()
}
