package mysql

import (
	"context"
	"testing"

	"hyapp/internal/testutil/mysqlschema"
	"hyapp/pkg/appcode"
	domain "hyapp/services/activity-service/internal/domain/wheel"
)

func TestWheelRewardOutboxClaimAndDeliveryAreLeaseFencedAndAtomic(t *testing.T) {
	schema := mysqlschema.New(t, mysqlschema.Config{
		EnvVar:         "ACTIVITY_SERVICE_MYSQL_TEST_DSN",
		InitDBPath:     mysqlschema.InitDBPath(t, mysqlschema.CallerFile(t, 1), "..", "..", "..", "deploy", "mysql", "initdb", "001_activity_service.sql"),
		DatabasePrefix: "hyapp_activity_wheel_outbox_test",
	})
	repository, err := Open(context.Background(), schema.DSN)
	if err != nil {
		t.Fatalf("open repository: %v", err)
	}
	t.Cleanup(func() { _ = repository.Close() })
	ctx := appcode.WithContext(context.Background(), "fami")
	const (
		drawID   = "wheel_draw_outbox_test"
		outboxID = "wheel_reward_wheel_draw_outbox_test"
	)
	if _, err := schema.DB.ExecContext(ctx, `
		INSERT INTO wheel_draw_stats (
			app_code, wheel_id, total_draws, unique_users, total_spent_coins, total_rtp_value_coins,
			pending_draws, granted_draws, failed_draws, created_at_ms, updated_at_ms
		) VALUES ('fami', 'classic', 1, 1, 100, 20, 1, 0, 0, 1000, 1000)`); err != nil {
		t.Fatalf("seed wheel stats: %v", err)
	}
	if _, err := schema.DB.ExecContext(ctx, `
		INSERT INTO wheel_draw_records (
			app_code, draw_id, command_id, user_id, device_id, visible_region_id, wheel_id, coin_spent,
			rule_version, rtp_window_index, selected_tier_id, reward_type, reward_id, reward_count,
			reward_coins, rtp_value_coins, candidate_tiers_json, rtp_snapshot_json, prize_metadata_json,
			reward_status, paid_at_ms, created_at_ms, updated_at_ms
		) VALUES ('fami', ?, 'draw-command', 1001, 'device', 9, 'classic', 100,
			1, 1, 'tier-1', 'coin', '', 1, 20, 20, '{}', '{}', '{}', 'pending', 1000, 1000, 1000)`, drawID); err != nil {
		t.Fatalf("seed wheel draw: %v", err)
	}
	if _, err := schema.DB.ExecContext(ctx, `
		INSERT INTO activity_outbox (
			app_code, outbox_id, event_type, payload, status, retry_count, next_retry_at_ms,
			locked_by, lock_until_ms, last_error, created_at_ms, updated_at_ms
		) VALUES ('fami', ?, ?, '{}', 'pending', 0, 1000, '', 0, '', 1000, 1000)`, outboxID, domain.EventTypeWheelRewardSettlement); err != nil {
		t.Fatalf("seed wheel outbox: %v", err)
	}

	records, err := repository.ClaimWheelRewardOutbox(ctx, "worker-1", 1000, 31000, 10, 16)
	if err != nil || len(records) != 1 || records[0].LockedBy != "worker-1" || records[0].Status != domain.OutboxStatusDelivering {
		t.Fatalf("claim mismatch: records=%+v err=%v", records, err)
	}
	secondClaim, err := repository.ClaimWheelRewardOutbox(ctx, "worker-2", 2000, 32000, 10, 16)
	if err != nil || len(secondClaim) != 0 {
		t.Fatalf("active lease must block second worker: records=%+v err=%v", secondClaim, err)
	}
	if err := repository.MarkWheelRewardOutboxDelivered(ctx, outboxID, "worker-1", []string{drawID}, "wtx-existing", 3000); err != nil {
		t.Fatalf("mark delivered: %v", err)
	}

	var drawStatus, receiptRef, outboxStatus, lockedBy string
	var pending, granted int64
	if err := schema.DB.QueryRowContext(ctx, `SELECT reward_status, reward_transaction_id FROM wheel_draw_records WHERE app_code = 'fami' AND draw_id = ?`, drawID).Scan(&drawStatus, &receiptRef); err != nil {
		t.Fatalf("read draw state: %v", err)
	}
	if err := schema.DB.QueryRowContext(ctx, `SELECT pending_draws, granted_draws FROM wheel_draw_stats WHERE app_code = 'fami' AND wheel_id = 'classic'`).Scan(&pending, &granted); err != nil {
		t.Fatalf("read wheel stats: %v", err)
	}
	if err := schema.DB.QueryRowContext(ctx, `SELECT status, locked_by FROM activity_outbox WHERE app_code = 'fami' AND outbox_id = ?`, outboxID).Scan(&outboxStatus, &lockedBy); err != nil {
		t.Fatalf("read outbox state: %v", err)
	}
	if drawStatus != domain.StatusGranted || receiptRef != "wtx-existing" || pending != 0 || granted != 1 || outboxStatus != domain.OutboxStatusDelivered || lockedBy != "" {
		t.Fatalf("delivery state mismatch: draw=%s receipt=%s pending=%d granted=%d outbox=%s lock=%q", drawStatus, receiptRef, pending, granted, outboxStatus, lockedBy)
	}
}
