// Package invite runs asynchronous invite validity consumers.
package invite

import (
	"context"
	"log/slog"
	"time"

	"hyapp/pkg/logx"
	invitedomain "hyapp/services/user-service/internal/domain/invite"
)

// Repository applies wallet recharge events to user-service invite progress.
type Repository interface {
	ApplyRechargeEvent(ctx context.Context, event invitedomain.RechargeEvent) (invitedomain.RechargeApplyResult, error)
}

// WalletOutboxReader scans wallet recharge facts in creation order.
type WalletOutboxReader interface {
	ListRechargeEventsAfter(ctx context.Context, cursor invitedomain.WalletRechargeCursor, batchSize int) ([]invitedomain.RechargeEvent, error)
}

// Service owns the invite validity event consumer.
type Service struct {
	repository Repository
	reader     WalletOutboxReader
}

// WorkerOptions controls local wallet outbox polling.
type WorkerOptions struct {
	WorkerID     string
	PollInterval time.Duration
	BatchSize    int
}

// New creates an invite service with persistence and wallet outbox reader dependencies.
func New(repository Repository, reader WalletOutboxReader) *Service {
	return &Service{repository: repository, reader: reader}
}

// RunWalletRechargeWorker consumes WalletRechargeRecorded facts and advances valid_invite_count.
func (s *Service) RunWalletRechargeWorker(ctx context.Context, options WorkerOptions) {
	if s == nil || s.repository == nil || s.reader == nil {
		logx.Warn(ctx, "worker_disabled", slog.String("worker", "wallet_recharge"), slog.String("component", "user_invite"), slog.String("reason", "missing_dependency"))
		return
	}
	if options.PollInterval <= 0 {
		options.PollInterval = 5 * time.Second
	}
	if options.BatchSize <= 0 {
		options.BatchSize = 100
	}

	var cursor invitedomain.WalletRechargeCursor
	for {
		processed, err := s.processBatch(ctx, &cursor, options.BatchSize)
		if err != nil {
			logx.Error(ctx, "worker_batch_failed", err, slog.String("worker", "wallet_recharge"), slog.String("component", "user_invite"), slog.String("worker_id", options.WorkerID), slog.Int("batch_size", options.BatchSize))
		}
		if ctx.Err() != nil {
			return
		}
		if processed >= options.BatchSize {
			// 有积压时不 sleep，直到追上 wallet_outbox 当前末尾。
			continue
		}
		select {
		case <-ctx.Done():
			return
		case <-time.After(options.PollInterval):
		}
	}
}

func (s *Service) processBatch(ctx context.Context, cursor *invitedomain.WalletRechargeCursor, batchSize int) (int, error) {
	events, err := s.reader.ListRechargeEventsAfter(ctx, *cursor, batchSize)
	if err != nil {
		return 0, err
	}
	for _, event := range events {
		if _, err := s.repository.ApplyRechargeEvent(ctx, event); err != nil {
			return 0, err
		}
		cursor.CreatedAtMs = event.OccurredAtMs
		cursor.EventID = event.EventID
	}
	return len(events), nil
}
