package service

import (
	"context"
	"crypto/sha256"
	"fmt"

	walletv1 "hyapp.local/api/proto/wallet/v1"
	"hyapp/pkg/appcode"
	"hyapp/pkg/giftlimits"
	"hyapp/pkg/xerr"
	"hyapp/services/room-service/internal/room/command"
)

type giftTargetBilling struct {
	TargetUserID int64
	CommandID    string
	Billing      *walletv1.DebitGiftResponse
}

func (s *Service) debitGiftTargets(ctx context.Context, cmd command.SendGift, roomMeta RoomMeta) (*walletv1.DebitGiftResponse, []giftTargetBilling, error) {
	if cmd.RobotWalletGift {
		if len(cmd.TargetUserIDs) != 1 {
			return nil, nil, xerr.New(xerr.InvalidArgument, "robot gift only supports one target")
		}
		billing, err := s.wallet.DebitRobotGift(ctx, &walletv1.DebitRobotGiftRequest{
			CommandId:      cmd.ID(),
			RoomId:         cmd.RoomID(),
			SenderUserId:   cmd.ActorUserID(),
			TargetUserId:   cmd.TargetUserID,
			GiftId:         cmd.GiftID,
			GiftCount:      cmd.GiftCount,
			AppCode:        appcode.FromContext(ctx),
			RegionId:       roomMeta.VisibleRegionID,
			SenderRegionId: cmd.SenderRegionID,
		})
		if err != nil {
			return nil, nil, err
		}
		return billing, []giftTargetBilling{{TargetUserID: cmd.TargetUserID, CommandID: cmd.ID(), Billing: billing}}, nil
	}
	if len(cmd.TargetUserIDs) == 1 {
		targetScope := giftTargetHostScopeFor(cmd, cmd.TargetUserID)
		// 单目标保留原 wallet command_id，避免改变已有幂等键、账务回执和排障路径。
		billing, err := s.wallet.DebitGift(ctx, &walletv1.DebitGiftRequest{
			CommandId:               cmd.ID(),
			RoomId:                  cmd.RoomID(),
			SenderUserId:            cmd.ActorUserID(),
			TargetUserId:            cmd.TargetUserID,
			GiftId:                  cmd.GiftID,
			GiftCount:               cmd.GiftCount,
			AppCode:                 appcode.FromContext(ctx),
			RegionId:                roomMeta.VisibleRegionID,
			SenderRegionId:          cmd.SenderRegionID,
			TargetIsHost:            targetScope.TargetIsHost,
			TargetHostRegionId:      targetScope.TargetHostRegionID,
			TargetAgencyOwnerUserId: targetScope.TargetAgencyOwnerUserID,
			EntitlementId:           cmd.EntitlementID,
			ChargeSource:            cmd.Source,
		})
		if err != nil {
			return nil, nil, err
		}
		return billing, []giftTargetBilling{{TargetUserID: cmd.TargetUserID, CommandID: cmd.ID(), Billing: billing}}, nil
	}

	targets := make([]*walletv1.DebitGiftTarget, 0, len(cmd.TargetUserIDs))
	for _, targetUserID := range cmd.TargetUserIDs {
		targetScope := giftTargetHostScopeFor(cmd, targetUserID)
		target := &walletv1.DebitGiftTarget{
			CommandId:    giftTargetCommandID(cmd.ID(), targetUserID, len(cmd.TargetUserIDs)),
			TargetUserId: targetUserID,
		}
		if targetScope.TargetIsHost {
			// 每个目标只使用 gateway 为该 target 固化的主播快照，避免批量送礼时第一个接收方以外的主播丢工资钻石。
			target.TargetIsHost = true
			target.TargetHostRegionId = targetScope.TargetHostRegionID
			target.TargetAgencyOwnerUserId = targetScope.TargetAgencyOwnerUserID
		}
		targets = append(targets, target)
	}
	resp, err := s.wallet.BatchDebitGift(ctx, &walletv1.BatchDebitGiftRequest{
		CommandId:      cmd.ID(),
		RoomId:         cmd.RoomID(),
		SenderUserId:   cmd.ActorUserID(),
		GiftId:         cmd.GiftID,
		GiftCount:      cmd.GiftCount,
		AppCode:        appcode.FromContext(ctx),
		RegionId:       roomMeta.VisibleRegionID,
		SenderRegionId: cmd.SenderRegionID,
		Targets:        targets,
		EntitlementId:  cmd.EntitlementID,
		ChargeSource:   cmd.Source,
	})
	if err != nil {
		return nil, nil, err
	}
	targetBillings, err := giftTargetBillingsFromBatch(resp, cmd.TargetUserIDs)
	if err != nil {
		return nil, nil, err
	}
	return resp.GetAggregate(), targetBillings, nil
}

func giftTargetHostScopeFor(cmd command.SendGift, targetUserID int64) command.GiftTargetHostScope {
	for _, scope := range cmd.TargetHostScopes {
		if scope.TargetUserID == targetUserID {
			return scope
		}
	}
	if targetUserID == cmd.TargetUserID && cmd.TargetIsHost {
		// 老 gateway 只会传单目标字段；保留这个兜底，避免滚动发布期间新 room-service 丢旧请求的主播入账。
		return command.GiftTargetHostScope{
			TargetUserID:            targetUserID,
			TargetIsHost:            true,
			TargetHostRegionID:      cmd.TargetHostRegionID,
			TargetAgencyOwnerUserID: cmd.TargetAgencyOwnerUserID,
		}
	}
	return command.GiftTargetHostScope{TargetUserID: targetUserID}
}

func giftTargetCommandID(commandID string, targetUserID int64, targetCount int) string {
	if targetCount <= 1 {
		return commandID
	}
	// 短主键沿用首版派生格式，保证滚动发布期间已经进入 saga 的请求仍命中原 wallet/lucky 幂等事实。
	legacy := fmt.Sprintf("%s:target:%d", commandID, targetUserID)
	if len(legacy) <= giftlimits.MaxCommandIDBytes {
		return legacy
	}
	// room 主 command_id 本身允许占满 128 bytes，不能再直接拼接 target。固定域分隔哈希同时绑定
	// parent 和 target，长度恒定且重试稳定；wallet 子交易与 lucky draw 继续共用这一把幂等键。
	digest := sha256.Sum256([]byte(fmt.Sprintf("room-gift-target-v2\x00%s\x00%d", commandID, targetUserID)))
	return fmt.Sprintf("gift-target:v2:%x", digest)
}

func giftTargetBillingsFromBatch(resp *walletv1.BatchDebitGiftResponse, targetUserIDs []int64) ([]giftTargetBilling, error) {
	if resp == nil || resp.GetAggregate() == nil {
		return nil, xerr.New(xerr.Unavailable, "wallet batch debit response is empty")
	}
	receipts := make(map[int64]*walletv1.BatchDebitGiftReceipt, len(resp.GetReceipts()))
	for _, receipt := range resp.GetReceipts() {
		if receipt.GetTargetUserId() <= 0 || receipt.GetBilling() == nil {
			return nil, xerr.New(xerr.Unavailable, "wallet batch debit receipt is incomplete")
		}
		receipts[receipt.GetTargetUserId()] = receipt
	}
	targetBillings := make([]giftTargetBilling, 0, len(targetUserIDs))
	for _, targetUserID := range targetUserIDs {
		receipt := receipts[targetUserID]
		if receipt == nil {
			// 批量钱包响应必须覆盖每个房间目标；少一条就不能提交房间表现和 outbox。
			return nil, xerr.New(xerr.Unavailable, "wallet batch debit receipt is missing")
		}
		targetBillings = append(targetBillings, giftTargetBilling{
			TargetUserID: targetUserID,
			CommandID:    receipt.GetCommandId(),
			Billing:      receipt.GetBilling(),
		})
	}
	return targetBillings, nil
}
