package service

import (
	"context"
	"fmt"

	walletv1 "hyapp.local/api/proto/wallet/v1"
	"hyapp/pkg/appcode"
	"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
	}
	// 钱包以 command_id 做幂等主键；多目标必须按接收方派生稳定子命令，才能在同一批内保存独立交易。
	return fmt.Sprintf("%s:target:%d", commandID, targetUserID)
}

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
}
