package cardobservation

import (
	"context"
	"strconv"
	"time"

	"gorm.io/gorm"
	"gorm.io/gorm/clause"

	domain "github.com/break/junhong_cmp_fiber/internal/domain/cardobservation"
	"github.com/break/junhong_cmp_fiber/internal/model"
	"github.com/break/junhong_cmp_fiber/pkg/errors"
)

// TrafficIncrementedEvent 是卡流量正增量的可靠领域事实。
type TrafficIncrementedEvent struct {
	EventID       string    `json:"event_id"`
	CardID        uint      `json:"card_id"`
	IncrementMB   float64   `json:"increment_mb"`
	ObservedAt    time.Time `json:"observed_at"`
	Source        string    `json:"source"`
	Scene         string    `json:"scene"`
	RequestID     string    `json:"request_id,omitempty"`
	CorrelationID string    `json:"correlation_id,omitempty"`
}

// ApplyTrafficObservation 串行应用流量读数，并在正增量时同事务写 Outbox。
func (s *Service) ApplyTrafficObservation(ctx context.Context, observation domain.TrafficObservation) (domain.TrafficDecision, error) {
	if s == nil || s.db == nil || s.eventWriter == nil {
		return domain.TrafficDecision{}, errors.New(errors.CodeInternalError, "卡流量观测能力未完整配置")
	}
	var decision domain.TrafficDecision
	err := s.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
		var card model.IotCard
		if err := tx.Clauses(clause.Locking{Strength: "UPDATE"}).Where("id = ?", observation.CardID).First(&card).Error; err != nil {
			if err == gorm.ErrRecordNotFound {
				return errors.New(errors.CodeNotFound, "IoT卡不存在")
			}
			return errors.Wrap(errors.CodeDatabaseError, err, "锁定IoT卡流量事实失败")
		}
		nextDecision, decisionErr := domain.ApplyTraffic(domain.CardTrafficSnapshot{
			CardID: card.ID, DataUsageMB: card.DataUsageMB, CurrentMonthUsageMB: card.CurrentMonthUsageMB,
			CurrentMonthStartDate: card.CurrentMonthStartDate, LastMonthTotalMB: card.LastMonthTotalMB,
			LastGatewayReadingMB: card.LastGatewayReadingMB,
		}, observation)
		if decisionErr != nil {
			return decisionErr
		}
		decision = nextDecision
		updates := map[string]any{
			"last_data_check_at": observation.Metadata.ObservedAt, "last_sync_time": observation.Metadata.ObservedAt,
			"current_month_start_date": decision.CurrentMonthStartDate, "last_month_total_mb": decision.LastMonthTotalMB,
			"current_month_usage_mb": decision.CurrentMonthUsageMB, "data_usage_mb": decision.DataUsageMB,
		}
		if decision.ReadingAccepted {
			updates["last_gateway_reading_mb"] = decision.LastGatewayReadingMB
		}
		result := tx.Model(&model.IotCard{}).
			Where("id = ? AND last_gateway_reading_mb = ?", card.ID, card.LastGatewayReadingMB).
			Updates(updates)
		if result.Error != nil {
			return errors.Wrap(errors.CodeDatabaseError, result.Error, "更新卡流量事实失败")
		}
		if result.RowsAffected != 1 {
			return errors.New(errors.CodeConflict, "卡流量基线已被其他请求更新")
		}
		if decision.IncrementMB <= 0 {
			return nil
		}
		eventID := "card-traffic:" + strconv.FormatUint(uint64(card.ID), 10) + ":" + observation.Metadata.ObservationID + ":incremented"
		return s.eventWriter.AppendTraffic(ctx, tx, TrafficIncrementedEvent{
			EventID: eventID, CardID: card.ID, IncrementMB: decision.IncrementMB,
			ObservedAt: observation.Metadata.ObservedAt, Source: observation.Metadata.Source,
			Scene: observation.Metadata.Scene, RequestID: observation.Metadata.RequestID,
			CorrelationID: observation.Metadata.CorrelationID,
		})
	})
	if err != nil {
		return domain.TrafficDecision{}, err
	}
	if s.cache != nil {
		s.cache.Invalidate(ctx, observation.CardID)
	}
	return decision, nil
}
