// Package gift contains gift debit usecases.
package gift

import (
	"context"

	"hyapp/pkg/appcode"
	"hyapp/pkg/giftlimits"
	"hyapp/pkg/xerr"
	"hyapp/services/wallet-service/internal/domain/ledger"
	"hyapp/services/wallet-service/internal/service/wallet/ports"
)

// Service 校验礼物扣费命令，并把原子账务提交交给 repository。
type Service struct {
	// repository 只需要礼物账本端口，后续单测不用实现完整 wallet Repository。
	repository ports.GiftLedgerStore
}

// New 创建礼物扣费用例。
func New(repository ports.GiftLedgerStore) *Service {
	return &Service{repository: repository}
}

// DebitGift 校验普通送礼扣费命令，并交给 repository 做原子落账和幂等。
func (s *Service) DebitGift(ctx context.Context, command ledger.DebitGiftCommand) (ledger.Receipt, error) {
	command.RobotGift = false
	return s.debitGift(ctx, command)
}

// DebitRobotGift 校验机器人房间专用送礼扣费命令，并强制使用 ROBOT_COIN 账务语义。
func (s *Service) DebitRobotGift(ctx context.Context, command ledger.DebitGiftCommand) (ledger.Receipt, error) {
	command.RobotGift = true
	command.TargetIsHost = false
	command.TargetHostRegionID = 0
	command.TargetAgencyOwnerUserID = 0
	return s.debitGift(ctx, command)
}

func (s *Service) debitGift(ctx context.Context, command ledger.DebitGiftCommand) (ledger.Receipt, error) {
	roomIDRequired := !command.DirectGift
	if !giftlimits.ValidCommandID(command.CommandID) || (roomIDRequired && command.RoomID == "") || command.SenderUserID <= 0 || command.TargetUserID <= 0 || command.GiftID == "" {
		// 房间送礼必须带 RoomID，让 Room Cell、账务分录和房间 outbox 能共享同一个业务边界。
		// 私聊送礼没有房间状态 owner；DirectGift 使用独立 biz_type 和 request hash 隔离幂等，因此允许 RoomID 为空。
		return ledger.Receipt{}, xerr.New(xerr.InvalidArgument, "billing command is incomplete")
	}
	if _, ok := giftlimits.WorkUnits(int64(command.GiftCount), 1); !ok {
		return ledger.Receipt{}, xerr.New(xerr.InvalidArgument, "gift_count exceeds gift command limit")
	}
	if command.TargetIsHost && command.TargetHostRegionID <= 0 {
		// 主播周期钻石后续要按区域工资政策结算；只有 active host profile 且带区域时才允许入账。
		return ledger.Receipt{}, xerr.New(xerr.InvalidArgument, "target_host_region_id is required")
	}
	if s == nil || s.repository == nil {
		return ledger.Receipt{}, xerr.New(xerr.Unavailable, "wallet repository is not configured")
	}
	command.AppCode = appcode.Normalize(command.AppCode)
	ctx = appcode.WithContext(ctx, command.AppCode)

	return s.repository.DebitGift(ctx, command)
}

// BatchDebitGift 校验多目标送礼扣费命令，并要求 repository 在同一事务中完成全部目标结算。
func (s *Service) BatchDebitGift(ctx context.Context, command ledger.BatchDebitGiftCommand) (ledger.BatchGiftReceipt, error) {
	if !giftlimits.ValidCommandID(command.CommandID) || command.RoomID == "" || command.SenderUserID <= 0 || command.GiftID == "" {
		return ledger.BatchGiftReceipt{}, xerr.New(xerr.InvalidArgument, "billing command is incomplete")
	}
	if len(command.Targets) == 0 {
		return ledger.BatchGiftReceipt{}, xerr.New(xerr.InvalidArgument, "gift targets are required")
	}
	seen := make(map[int64]struct{}, len(command.Targets))
	for _, target := range command.Targets {
		if !giftlimits.ValidCommandID(target.CommandID) || target.TargetUserID <= 0 {
			return ledger.BatchGiftReceipt{}, xerr.New(xerr.InvalidArgument, "gift target is incomplete")
		}
		if _, exists := seen[target.TargetUserID]; exists {
			return ledger.BatchGiftReceipt{}, xerr.New(xerr.InvalidArgument, "gift target is duplicated")
		}
		seen[target.TargetUserID] = struct{}{}
		if target.TargetIsHost && target.TargetHostRegionID <= 0 {
			// 主播工资入账必须带该目标自己的主播区域，不能复用房间区域或其他目标快照。
			return ledger.BatchGiftReceipt{}, xerr.New(xerr.InvalidArgument, "target_host_region_id is required")
		}
	}
	if _, ok := giftlimits.WorkUnits(int64(command.GiftCount), len(seen)); !ok {
		// Wallet repeats the fan-out ceiling after target deduplication; gateway is not a trusted
		// security boundary and internal callers must not bypass the same workload contract.
		return ledger.BatchGiftReceipt{}, xerr.New(xerr.InvalidArgument, "gift_count and targets exceed gift command limit")
	}
	if s == nil || s.repository == nil {
		return ledger.BatchGiftReceipt{}, xerr.New(xerr.Unavailable, "wallet repository is not configured")
	}
	command.AppCode = appcode.Normalize(command.AppCode)
	ctx = appcode.WithContext(ctx, command.AppCode)

	return s.repository.BatchDebitGift(ctx, command)
}
