package firstrecharge

import (
	"context"
	"testing"

	"google.golang.org/grpc"
	walletv1 "hyapp.local/api/proto/wallet/v1"
	"hyapp/pkg/appcode"
	domain "hyapp/services/activity-service/internal/domain/firstrecharge"
	"hyapp/services/activity-service/internal/testutil/mysqltest"
)

func TestFirstRechargeRewardConsumesRechargeFactWithMySQL(t *testing.T) {
	repository := mysqltest.NewRepository(t)
	wallet := &fakeWallet{}
	svc := New(repository, wallet)
	ctx := appcode.WithContext(context.Background(), "lalu")

	config, err := svc.UpdateConfig(ctx, domain.Config{
		Enabled:          true,
		UpdatedByAdminID: 90001,
		Tiers: []domain.Tier{{
			TierCode:        "first_099",
			TierName:        "首冲 $0.99",
			USDMinorAmount:  99,
			GoogleProductID: "first_recharge_google_099",
			DiscountPercent: 70,
			ResourceGroupID: 88001,
			Status:          domain.TierStatusActive,
			SortOrder:       1,
		}, {
			TierCode:        "first_199",
			TierName:        "首冲 $1.99",
			USDMinorAmount:  199,
			GoogleProductID: "first_recharge_google_199",
			DiscountPercent: 60,
			ResourceGroupID: 88002,
			Status:          domain.TierStatusActive,
			SortOrder:       2,
		}},
	})
	if err != nil {
		t.Fatalf("UpdateConfig failed: %v", err)
	}
	if len(config.Tiers) != 2 || config.Tiers[0].TierID <= 0 || config.Tiers[1].TierID <= 0 {
		t.Fatalf("tier id was not generated: %+v", config.Tiers)
	}

	event := domain.RechargeEvent{
		AppCode:            "lalu",
		EventID:            "evt-recharge-1",
		EventType:          domain.RechargeEventWalletRecorded,
		TransactionID:      "wtx-recharge-1",
		CommandID:          "seller-transfer-1",
		UserID:             42001,
		RechargeCoinAmount: 0,
		RechargeSequence:   2,
		RechargeType:       "google",
		RechargeUSDMinor:   99,
		GoogleProductID:    "first_recharge_google_099",
		OccurredAtMS:       1710000000000,
	}
	err = svc.ConsumeWalletRechargeEvent(ctx, event)
	if err != nil {
		t.Fatalf("ConsumeWalletRechargeEvent failed: %v", err)
	}
	claim, exists, err := repository.GetFirstRechargeRewardClaimByUser(ctx, 42001)
	if err != nil {
		t.Fatalf("GetFirstRechargeRewardClaimByUser failed: %v", err)
	}
	if !exists || claim.Status != domain.StatusGranted || claim.ResourceGroupID != 88001 || claim.GoogleProductID != "first_recharge_google_099" || claim.RechargeCoinAmount != 0 {
		t.Fatalf("consume result mismatch: exists=%v claim=%+v", exists, claim)
	}
	if len(wallet.grants) != 1 || wallet.grants[0].GroupId != 88001 || wallet.grants[0].TargetUserId != 42001 {
		t.Fatalf("wallet grant mismatch: %+v", wallet.grants)
	}

	status, err := svc.GetStatus(ctx, 42001)
	if err != nil {
		t.Fatalf("GetStatus failed: %v", err)
	}
	if status.Rewarded || len(status.Tiers) != 1 || status.Tiers[0].GoogleProductID != "first_recharge_google_199" || len(status.Claims) != 1 {
		t.Fatalf("status should keep the unpurchased tier visible: %+v", status)
	}

	err = svc.ConsumeWalletRechargeEvent(ctx, event)
	if err != nil {
		t.Fatalf("retry ConsumeWalletRechargeEvent failed: %v", err)
	}
	if len(wallet.grants) != 1 {
		t.Fatalf("retry should be idempotent, grants=%d", len(wallet.grants))
	}

	event.EventID = "evt-recharge-2"
	event.TransactionID = "wtx-recharge-2"
	event.CommandID = "seller-transfer-2"
	event.RechargeUSDMinor = 199
	event.GoogleProductID = "first_recharge_google_199"
	err = svc.ConsumeWalletRechargeEvent(ctx, event)
	if err != nil {
		t.Fatalf("second recharge ConsumeWalletRechargeEvent failed: %v", err)
	}
	if len(wallet.grants) != 2 || wallet.grants[1].GroupId != 88002 {
		t.Fatalf("second tier should be granted, grants=%+v", wallet.grants)
	}
	status, err = svc.GetStatus(ctx, 42001)
	if err != nil {
		t.Fatalf("GetStatus after all tiers failed: %v", err)
	}
	if !status.Rewarded || len(status.Tiers) != 0 || len(status.Claims) != 2 {
		t.Fatalf("status should complete after all tiers are purchased: %+v", status)
	}
}

type fakeWallet struct {
	grants []*walletv1.GrantResourceGroupRequest
}

func (f *fakeWallet) GrantResourceGroup(_ context.Context, req *walletv1.GrantResourceGroupRequest, _ ...grpc.CallOption) (*walletv1.ResourceGrantResponse, error) {
	f.grants = append(f.grants, req)
	return &walletv1.ResourceGrantResponse{Grant: &walletv1.ResourceGrant{
		AppCode:      req.GetAppCode(),
		GrantId:      "grant-first-recharge",
		CommandId:    req.GetCommandId(),
		TargetUserId: req.GetTargetUserId(),
		GrantSource:  req.GetGrantSource(),
		Status:       "granted",
	}}, nil
}
