package task_test

import (
	"context"
	"testing"
	"time"

	"google.golang.org/grpc"
	walletv1 "hyapp.local/api/proto/wallet/v1"
	taskdomain "hyapp/services/activity-service/internal/domain/task"
	taskservice "hyapp/services/activity-service/internal/service/task"
	"hyapp/services/activity-service/internal/testutil/mysqltest"
)

// TestTaskEventQueryAndClaimFlow 覆盖任务定义、事件进度、App 查询、领奖和领奖幂等的主闭环。
func TestTaskEventQueryAndClaimFlow(t *testing.T) {
	repository := mysqltest.NewRepository(t)
	wallet := &fakeWalletClient{}
	svc := taskservice.New(repository, wallet)
	now := time.Date(2026, 5, 9, 10, 0, 0, 0, time.FixedZone("CST", 8*60*60))
	svc.SetClock(func() time.Time { return now })

	definition, created, err := svc.UpsertTaskDefinition(context.Background(), taskdomain.DefinitionCommand{
		TaskType:         taskdomain.TypeDaily,
		Category:         "gift",
		MetricType:       "gift_spend_coin",
		Title:            "send gift",
		TargetValue:      100,
		TargetUnit:       "coin",
		RewardCoinAmount: 10,
		Status:           taskdomain.StatusActive,
		SortOrder:        1,
		OperatorAdminID:  90001,
	})
	if err != nil || !created {
		t.Fatalf("create task definition failed: created=%v err=%v", created, err)
	}

	result, err := svc.ConsumeTaskEvent(context.Background(), taskdomain.Event{
		EventID:       "evt-gift-1",
		EventType:     "WalletGiftDebited",
		SourceService: "wallet",
		UserID:        10001,
		MetricType:    "gift_spend_coin",
		Value:         120,
		OccurredAtMS:  now.UnixMilli(),
	})
	if err != nil {
		t.Fatalf("ConsumeTaskEvent failed: %v", err)
	}
	if result.Status != taskdomain.EventStatusConsumed || result.MatchedTaskCount != 1 {
		t.Fatalf("event result mismatch: %+v", result)
	}
	duplicate, err := svc.ConsumeTaskEvent(context.Background(), taskdomain.Event{
		EventID:       "evt-gift-1",
		EventType:     "WalletGiftDebited",
		SourceService: "wallet",
		UserID:        10001,
		MetricType:    "gift_spend_coin",
		Value:         120,
		OccurredAtMS:  now.UnixMilli(),
	})
	if err != nil || duplicate.Status != taskdomain.EventStatusConsumed {
		t.Fatalf("duplicate event should be acknowledged: result=%+v err=%v", duplicate, err)
	}

	list, err := svc.ListUserTasks(context.Background(), 10001)
	if err != nil {
		t.Fatalf("ListUserTasks failed: %v", err)
	}
	daily := list.Sections[0]
	if len(daily.Items) != 1 || daily.Items[0].TaskID != definition.TaskID || daily.Items[0].ProgressValue != 120 || daily.Items[0].UserStatus != taskdomain.StatusCompleted || !daily.Items[0].Claimable {
		t.Fatalf("daily task item mismatch: %+v", daily.Items)
	}

	claim, err := svc.ClaimTaskReward(context.Background(), taskdomain.ClaimCommand{
		UserID:    10001,
		TaskID:    definition.TaskID,
		TaskType:  taskdomain.TypeDaily,
		CycleKey:  "2026-05-09",
		CommandID: "cmd-claim-1",
	})
	if err != nil {
		t.Fatalf("ClaimTaskReward failed: %v", err)
	}
	if claim.Status != taskdomain.ClaimStatusGranted || claim.WalletTransactionID != "wtx-task-1" || wallet.calls != 1 {
		t.Fatalf("claim result mismatch: claim=%+v wallet_calls=%d", claim, wallet.calls)
	}
	again, err := svc.ClaimTaskReward(context.Background(), taskdomain.ClaimCommand{
		UserID:    10001,
		TaskID:    definition.TaskID,
		TaskType:  taskdomain.TypeDaily,
		CycleKey:  "2026-05-09",
		CommandID: "cmd-claim-1",
	})
	if err != nil {
		t.Fatalf("ClaimTaskReward retry failed: %v", err)
	}
	if again.ClaimID != claim.ClaimID || wallet.calls != 1 {
		t.Fatalf("claim idempotency mismatch: first=%+v second=%+v calls=%d", claim, again, wallet.calls)
	}
}

type fakeWalletClient struct {
	calls int
}

func (f *fakeWalletClient) CreditTaskReward(context.Context, *walletv1.CreditTaskRewardRequest, ...grpc.CallOption) (*walletv1.CreditTaskRewardResponse, error) {
	f.calls++
	return &walletv1.CreditTaskRewardResponse{
		TransactionId: "wtx-task-1",
		Amount:        10,
		GrantedAtMs:   1778292000000,
		Balance:       &walletv1.AssetBalance{AssetType: "COIN", AvailableAmount: 10},
	}, nil
}
