package main

import (
	"context"
	"flag"
	"fmt"
	"log"
	"os"
	"os/signal"
	"strings"
	"syscall"
	"time"

	"hyapp/pkg/appcode"
	"hyapp/services/wallet-service/internal/config"
	walletservice "hyapp/services/wallet-service/internal/service/wallet"
	mysqlstorage "hyapp/services/wallet-service/internal/storage/mysql"
)

// main 只做一次性礼物墙回填：复用 wallet-service 的投影状态机，不手写聚合 SQL。
func main() {
	configPath := flag.String("config", "services/wallet-service/configs/config.yaml", "wallet-service config path")
	appCode := flag.String("app-code", appcode.Default, "app code to backfill")
	workerID := flag.String("worker-id", "gift-wall-backfill", "projection worker id")
	batchSize := flag.Int("batch-size", 50, "events claimed per batch")
	maxBatches := flag.Int("max-batches", 0, "maximum batches to process; 0 means until drained")
	progressEvery := flag.Int("progress-every", 10, "print progress every N non-empty batches")
	lockTTL := flag.Duration("lock-ttl", 30*time.Second, "projection lock ttl")
	sleep := flag.Duration("sleep", 200*time.Millisecond, "pause between non-empty batches")
	dryRun := flag.Bool("dry-run", false, "load config and ping mysql without processing")
	flag.Parse()

	normalizedAppCode := appcode.Normalize(strings.TrimSpace(*appCode))
	if normalizedAppCode == "" {
		log.Fatal("app-code is required")
	}
	normalizedWorkerID := strings.TrimSpace(*workerID)
	if normalizedWorkerID == "" {
		log.Fatal("worker-id is required")
	}

	cfg, err := config.Load(*configPath)
	if err != nil {
		log.Fatalf("load config: %v", err)
	}

	ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM)
	defer stop()
	ctx = appcode.WithContext(ctx, normalizedAppCode)

	repository, err := mysqlstorage.Open(ctx, cfg.MySQLDSN)
	if err != nil {
		log.Fatalf("open mysql: %v", err)
	}
	defer func() { _ = repository.Close() }()

	if *dryRun {
		if err := repository.Ping(ctx); err != nil {
			log.Fatalf("ping mysql: %v", err)
		}
		fmt.Printf("dry_run_ok app_code=%s batch_size=%d worker_id=%s\n", normalizedAppCode, *batchSize, normalizedWorkerID)
		return
	}
	if *progressEvery <= 0 {
		*progressEvery = 1
	}

	svc := walletservice.New(repository)
	total := 0
	emptyBatches := 0
	started := time.Now()
	for batchNo := 1; ; batchNo++ {
		if *maxBatches > 0 && batchNo > *maxBatches {
			break
		}
		select {
		case <-ctx.Done():
			fmt.Fprintf(os.Stderr, "stopped app_code=%s total=%d batches=%d duration=%s\n", normalizedAppCode, total, batchNo-1, time.Since(started).Round(time.Millisecond))
			return
		default:
		}

		// ProjectPendingGiftWallEvents 会认领缺失或失败的事件，并在同一事务里写 user_gift_wall 与 done 标记。
		processed, err := svc.ProjectPendingGiftWallEvents(ctx, normalizedWorkerID, *batchSize, *lockTTL)
		if err != nil {
			log.Fatalf("project batch %d: %v", batchNo, err)
		}
		total += processed
		if processed == 0 || batchNo == 1 || batchNo%*progressEvery == 0 {
			fmt.Printf("batch=%d processed=%d total=%d elapsed=%s\n", batchNo, processed, total, time.Since(started).Round(time.Millisecond))
		}

		if processed == 0 {
			emptyBatches++
			if emptyBatches >= 2 {
				break
			}
			time.Sleep(*sleep)
			continue
		}
		emptyBatches = 0
		if *sleep > 0 {
			time.Sleep(*sleep)
		}
	}
	fmt.Printf("done app_code=%s total=%d duration=%s\n", normalizedAppCode, total, time.Since(started).Round(time.Millisecond))
}
