package service

import (
	"context"
	"fmt"
	"strings"
	"time"

	"google.golang.org/protobuf/proto"
	activityv1 "hyapp.local/api/proto/activity/v1"
	roomv1 "hyapp.local/api/proto/room/v1"
	walletv1 "hyapp.local/api/proto/wallet/v1"
	"hyapp/pkg/appcode"
	"hyapp/pkg/xerr"
	"hyapp/services/room-service/internal/room/command"
)

func (s *Service) executeLuckyGiftDraws(ctx context.Context, meta *roomv1.RequestMeta, cmd command.SendGift, roomMeta RoomMeta, targetBillings []giftTargetBilling, now time.Time) ([]*roomv1.LuckyGiftDrawResult, error) {
	if len(targetBillings) == 0 {
		return nil, nil
	}
	if len(targetBillings) == 1 {
		result, err := s.executeLuckyGiftDraw(ctx, meta, cmd, roomMeta, targetBillings[0], now)
		if err != nil {
			return nil, err
		}
		return []*roomv1.LuckyGiftDrawResult{result}, nil
	}

	return s.executeLuckyGiftDrawBatch(ctx, meta, cmd, roomMeta, targetBillings, now)
}

func (s *Service) executeLuckyGiftDraw(ctx context.Context, meta *roomv1.RequestMeta, cmd command.SendGift, roomMeta RoomMeta, targetBilling giftTargetBilling, now time.Time) (*roomv1.LuckyGiftDrawResult, error) {
	if targetBilling.Billing == nil {
		return nil, xerr.New(xerr.Unavailable, "wallet target debit response is empty")
	}
	// 抽奖必须按每个收礼目标独立落事实；command_id、target_user_id 和 coin_spent 都来自该目标的 wallet 子交易。
	drawResp, err := s.luckyGift.ExecuteLuckyGiftDraw(ctx, &activityv1.ExecuteLuckyGiftDrawRequest{
		LuckyGift: luckyGiftMetaForTarget(ctx, meta, cmd, roomMeta, targetBilling, now),
	})
	if err != nil {
		return nil, err
	}
	if drawResp == nil || drawResp.GetResult() == nil {
		return nil, xerr.New(xerr.Unavailable, "lucky gift draw response is empty")
	}
	// 同步返回只承载抽奖表现事实；返奖后的余额由 activity 在中奖落库后发送钱包私有 IM，避免 HTTP 响应领先账务通知链路。
	return luckyGiftResultFromProto(drawResp.GetResult(), targetBilling.TargetUserID), nil
}

func (s *Service) executeLuckyGiftDrawBatch(ctx context.Context, meta *roomv1.RequestMeta, cmd command.SendGift, roomMeta RoomMeta, targetBillings []giftTargetBilling, now time.Time) ([]*roomv1.LuckyGiftDrawResult, error) {
	req := &activityv1.BatchExecuteLuckyGiftDrawRequest{LuckyGifts: make([]*activityv1.LuckyGiftMeta, 0, len(targetBillings))}
	for _, targetBilling := range targetBillings {
		if targetBilling.Billing == nil {
			return nil, xerr.New(xerr.Unavailable, "wallet target debit response is empty")
		}
		// 多人送礼只跨服务调用一次；activity-service 在一个事务内按这个顺序推进锁和抽奖事实。
		req.LuckyGifts = append(req.LuckyGifts, luckyGiftMetaForTarget(ctx, meta, cmd, roomMeta, targetBilling, now))
	}
	resp, err := s.luckyGift.BatchExecuteLuckyGiftDraw(ctx, req)
	if err != nil {
		return nil, err
	}
	if resp == nil || len(resp.GetResults()) != len(targetBillings) {
		return nil, xerr.New(xerr.Unavailable, "lucky gift batch draw response is incomplete")
	}
	results := make([]*roomv1.LuckyGiftDrawResult, 0, len(targetBillings))
	for index, result := range resp.GetResults() {
		if result == nil {
			return nil, xerr.New(xerr.Unavailable, "lucky gift batch draw result is empty")
		}
		results = append(results, luckyGiftResultFromProto(result, targetBillings[index].TargetUserID))
	}
	return results, nil
}

func luckyGiftMetaForTarget(ctx context.Context, meta *roomv1.RequestMeta, cmd command.SendGift, roomMeta RoomMeta, targetBilling giftTargetBilling, now time.Time) *activityv1.LuckyGiftMeta {
	return &activityv1.LuckyGiftMeta{
		Meta:         activityMetaFromRoom(ctx, meta),
		CommandId:    targetBilling.CommandID,
		UserId:       cmd.ActorUserID(),
		TargetUserId: targetBilling.TargetUserID,
		// 目前没有独立设备 ID 字段，优先用 session_id；没有时退回目标子 command_id，保证每次抽奖 scope 不为空。
		DeviceId:  luckyGiftDeviceID(cmd, targetBilling.CommandID),
		RoomId:    cmd.RoomID(),
		AnchorId:  luckyGiftAnchorID(roomMeta),
		GiftId:    cmd.GiftID,
		GiftCount: cmd.GiftCount,
		CoinSpent: targetBilling.Billing.GetCoinSpent(),
		// 房间链路统一使用 UTC epoch ms；不能把本地时区时间传给活动风控窗口。
		PaidAtMs:        now.UTC().UnixMilli(),
		PoolId:          cmd.PoolID,
		VisibleRegionId: roomMeta.VisibleRegionID,
		CountryId:       cmd.SenderCountryID,
	}
}

func (s *Service) shouldDrawLuckyGift(poolID string, giftTypeCode string) bool {
	if s.luckyGift == nil {
		return false
	}
	switch strings.TrimSpace(giftTypeCode) {
	case "lucky", "super_lucky":
		return true
	default:
		return strings.TrimSpace(poolID) != ""
	}
}

func (s *Service) withLuckyGiftSendLock(ctx context.Context, enabled bool, userID int64, fn func() error) error {
	if !enabled || s == nil || s.luckyGiftSendLocker == nil {
		return fn()
	}
	release, err := s.luckyGiftSendLocker.AcquireLuckyGiftSendLock(ctx, appcode.FromContext(ctx), userID, s.luckyGiftSendLockTTL)
	if err != nil {
		return err
	}
	if release != nil {
		defer func() {
			releaseCtx, cancel := context.WithTimeout(context.Background(), time.Second)
			defer cancel()
			release(releaseCtx)
		}()
	}
	return fn()
}

func activityMetaFromRoom(ctx context.Context, meta *roomv1.RequestMeta) *activityv1.RequestMeta {
	return &activityv1.RequestMeta{
		RequestId:     meta.GetRequestId(),
		Caller:        "room-service",
		GatewayNodeId: meta.GetGatewayNodeId(),
		SentAtMs:      time.Now().UTC().UnixMilli(),
		AppCode:       appcode.FromContext(ctx),
	}
}

func luckyGiftDeviceID(cmd command.SendGift, fallbackCommandID string) string {
	if value := strings.TrimSpace(cmd.SessionID); value != "" {
		return value
	}
	if value := strings.TrimSpace(fallbackCommandID); value != "" {
		return value
	}
	return cmd.ID()
}

func luckyGiftAnchorID(roomMeta RoomMeta) string {
	if roomMeta.OwnerUserID <= 0 {
		return roomMeta.RoomID
	}
	return fmt.Sprintf("%d", roomMeta.OwnerUserID)
}

func luckyGiftResultFromProto(result *activityv1.LuckyGiftDrawResult, targetUserID int64) *roomv1.LuckyGiftDrawResult {
	if result == nil {
		return nil
	}
	return &roomv1.LuckyGiftDrawResult{
		Enabled:                   true,
		DrawId:                    result.GetDrawId(),
		CommandId:                 result.GetCommandId(),
		PoolId:                    result.GetPoolId(),
		GiftId:                    result.GetGiftId(),
		RuleVersion:               result.GetRuleVersion(),
		ExperiencePool:            result.GetExperiencePool(),
		SelectedTierId:            result.GetSelectedTierId(),
		MultiplierPpm:             result.GetMultiplierPpm(),
		BaseRewardCoins:           result.GetBaseRewardCoins(),
		RoomAtmosphereRewardCoins: result.GetRoomAtmosphereRewardCoins(),
		ActivitySubsidyCoins:      result.GetActivitySubsidyCoins(),
		EffectiveRewardCoins:      result.GetEffectiveRewardCoins(),
		RewardStatus:              result.GetRewardStatus(),
		StageFeedback:             result.GetStageFeedback(),
		HighMultiplier:            result.GetHighMultiplier(),
		CreatedAtMs:               result.GetCreatedAtMs(),
		WalletTransactionId:       result.GetWalletTransactionId(),
		// 这里故意不透出 activity 返回的返奖后余额。SendGift HTTP 只承诺扣费后的真实余额，中奖返奖后的余额必须等钱包落账后由 WalletBalanceChanged 私有 IM 覆盖。
		CoinBalanceAfter: 0,
		TargetUserId:     targetUserID,
	}
}

func aggregateLuckyGiftResults(commandID string, results []*roomv1.LuckyGiftDrawResult) *roomv1.LuckyGiftDrawResult {
	if len(results) == 0 {
		return nil
	}
	if len(results) == 1 {
		return results[0]
	}
	aggregate := proto.Clone(results[0]).(*roomv1.LuckyGiftDrawResult)
	aggregate.CommandId = commandID
	aggregate.SelectedTierId = "batch"
	aggregate.TargetUserId = 0
	aggregate.WalletTransactionId = ""
	aggregate.MultiplierPpm = 0
	aggregate.BaseRewardCoins = 0
	aggregate.RoomAtmosphereRewardCoins = 0
	aggregate.ActivitySubsidyCoins = 0
	aggregate.EffectiveRewardCoins = 0
	aggregate.RewardStatus = "granted"
	aggregate.StageFeedback = false
	aggregate.HighMultiplier = false
	aggregate.CoinBalanceAfter = 0
	for _, result := range results {
		if result == nil {
			continue
		}
		aggregate.RuleVersion = result.GetRuleVersion()
		aggregate.ExperiencePool = result.GetExperiencePool()
		aggregate.CreatedAtMs = result.GetCreatedAtMs()
		aggregate.MultiplierPpm += result.GetMultiplierPpm()
		aggregate.BaseRewardCoins += result.GetBaseRewardCoins()
		aggregate.RoomAtmosphereRewardCoins += result.GetRoomAtmosphereRewardCoins()
		aggregate.ActivitySubsidyCoins += result.GetActivitySubsidyCoins()
		aggregate.EffectiveRewardCoins += result.GetEffectiveRewardCoins()
		aggregate.StageFeedback = aggregate.StageFeedback || result.GetStageFeedback()
		aggregate.HighMultiplier = aggregate.HighMultiplier || result.GetHighMultiplier()
		switch result.GetRewardStatus() {
		case "pending":
			aggregate.RewardStatus = "pending"
		case "failed":
			if aggregate.RewardStatus != "pending" {
				aggregate.RewardStatus = "failed"
			}
		case "granted":
		default:
			if aggregate.RewardStatus == "" {
				aggregate.RewardStatus = result.GetRewardStatus()
			}
		}
	}
	return aggregate
}

func syntheticLuckyGiftResult(cmd command.SendGift, targetBillings []giftTargetBilling, now time.Time) *roomv1.LuckyGiftDrawResult {
	if len(targetBillings) == 0 || targetBillings[0].Billing == nil {
		return nil
	}
	rewardCoins := firstPositiveInt64(cmd.SyntheticLuckyRewardCoins, targetBillings[0].Billing.GetCoinSpent()*5)
	multiplier := firstPositiveInt64(cmd.SyntheticLuckyMultiplierPPM, 5000000)
	return &roomv1.LuckyGiftDrawResult{
		Enabled:              true,
		DrawId:               fmt.Sprintf("robot_lucky_%s_%d", cmd.ID(), now.UnixMilli()),
		CommandId:            cmd.ID(),
		PoolId:               cmd.PoolID,
		GiftId:               cmd.GiftID,
		MultiplierPpm:        multiplier,
		BaseRewardCoins:      rewardCoins,
		EffectiveRewardCoins: rewardCoins,
		RewardStatus:         "granted",
		StageFeedback:        true,
		HighMultiplier:       multiplier >= 5000000,
		CreatedAtMs:          now.UnixMilli(),
		TargetUserId:         cmd.TargetUserID,
	}
}

func giftFinalCoinBalanceAfter(billing *walletv1.DebitGiftResponse) int64 {
	if billing == nil {
		return 0
	}
	// SendGift 的 HTTP 余额只表示本次送礼扣费完成后的 COIN 余额；lucky/super_lucky 返奖属于后续入账事实，由钱包余额 IM 更新客户端。
	return billing.GetBalanceAfter()
}

func firstPositiveInt64(values ...int64) int64 {
	for _, value := range values {
		if value > 0 {
			return value
		}
	}
	return 0
}
