package wallet

import (
	"context"
	"sort"
	"strings"

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

// GetRedPacketConfig 返回红包配置；未配置时 repository 会返回关闭状态。
func (s *Service) GetRedPacketConfig(ctx context.Context, appCode string) (ledger.RedPacketConfig, error) {
	if s.repository == nil {
		return ledger.RedPacketConfig{}, xerr.New(xerr.Unavailable, "wallet repository is not configured")
	}
	appCode = appcode.Normalize(appCode)
	ctx = appcode.WithContext(ctx, appCode)
	return s.repository.GetRedPacketConfig(ctx, appCode)
}

// UpdateRedPacketConfig 保存后台红包配置；时间和档位必须在 wallet-service 内强校验。
func (s *Service) UpdateRedPacketConfig(ctx context.Context, config ledger.RedPacketConfig) (ledger.RedPacketConfig, error) {
	if s.repository == nil {
		return ledger.RedPacketConfig{}, xerr.New(xerr.Unavailable, "wallet repository is not configured")
	}
	config.AppCode = appcode.Normalize(config.AppCode)
	config.CountTiers = normalizeCountTiers(config.CountTiers)
	config.AmountTiers = normalizeAmountTiers(config.AmountTiers)
	config.RuleURL = strings.TrimSpace(config.RuleURL)
	config.ExpireSeconds = ledger.RedPacketExpireSeconds
	if config.UpdatedByAdminID <= 0 {
		return ledger.RedPacketConfig{}, xerr.New(xerr.InvalidArgument, "operator_user_id is required")
	}
	if len(config.CountTiers) == 0 || len(config.AmountTiers) == 0 {
		return ledger.RedPacketConfig{}, xerr.New(xerr.InvalidArgument, "red packet tiers are required")
	}
	if config.DelayedOpenSeconds < 0 || config.DailySendLimit < 0 {
		return ledger.RedPacketConfig{}, xerr.New(xerr.InvalidArgument, "red packet config is invalid")
	}
	if config.DelayedOpenSeconds >= ledger.RedPacketExpireSeconds {
		return ledger.RedPacketConfig{}, xerr.New(xerr.InvalidArgument, "red packet expire_seconds must be greater than delayed_open_seconds")
	}
	if len(config.CountTiers) > 0 && len(config.AmountTiers) > 0 && config.AmountTiers[0] < int64(config.CountTiers[len(config.CountTiers)-1]) {
		return ledger.RedPacketConfig{}, xerr.New(xerr.InvalidArgument, "red packet amount tiers must cover count tiers")
	}
	ctx = appcode.WithContext(ctx, config.AppCode)
	return s.repository.UpdateRedPacketConfig(ctx, config)
}

// CreateRedPacket 发普通或延迟红包；扣款、随机拆分和 outbox 都由 repository 事务完成。
func (s *Service) CreateRedPacket(ctx context.Context, command ledger.RedPacketCreateCommand) (ledger.RedPacketCreateReceipt, error) {
	if s.repository == nil {
		return ledger.RedPacketCreateReceipt{}, xerr.New(xerr.Unavailable, "wallet repository is not configured")
	}
	command.AppCode = appcode.Normalize(command.AppCode)
	command.CommandID = strings.TrimSpace(command.CommandID)
	command.RoomID = strings.TrimSpace(command.RoomID)
	command.PacketType = ledger.NormalizeRedPacketType(command.PacketType)
	if command.CommandID == "" || command.SenderUserID <= 0 || command.RoomID == "" || command.RegionID <= 0 {
		return ledger.RedPacketCreateReceipt{}, xerr.New(xerr.InvalidArgument, "red packet command is incomplete")
	}
	if command.PacketType == "" {
		return ledger.RedPacketCreateReceipt{}, xerr.New(xerr.InvalidArgument, "red packet type is invalid")
	}
	if command.TotalAmount <= 0 || command.PacketCount <= 0 {
		return ledger.RedPacketCreateReceipt{}, xerr.New(xerr.InvalidArgument, "red packet amount and count are required")
	}
	if command.TotalAmount < int64(command.PacketCount) {
		return ledger.RedPacketCreateReceipt{}, xerr.New(xerr.RedPacketAmountTooSmall, "red packet amount is too small")
	}
	ctx = appcode.WithContext(ctx, command.AppCode)
	return s.repository.CreateRedPacket(ctx, command)
}

// ClaimRedPacket 抢一个红包份额；同一个用户同一红包只允许成功一次。
func (s *Service) ClaimRedPacket(ctx context.Context, command ledger.RedPacketClaimCommand) (ledger.RedPacketClaimReceipt, error) {
	if s.repository == nil {
		return ledger.RedPacketClaimReceipt{}, xerr.New(xerr.Unavailable, "wallet repository is not configured")
	}
	command.AppCode = appcode.Normalize(command.AppCode)
	command.CommandID = strings.TrimSpace(command.CommandID)
	command.PacketID = strings.TrimSpace(command.PacketID)
	if command.CommandID == "" || command.PacketID == "" || command.UserID <= 0 {
		return ledger.RedPacketClaimReceipt{}, xerr.New(xerr.InvalidArgument, "red packet claim command is incomplete")
	}
	ctx = appcode.WithContext(ctx, command.AppCode)
	return s.repository.ClaimRedPacket(ctx, command)
}

func (s *Service) ListRedPackets(ctx context.Context, query ledger.RedPacketQuery) ([]ledger.RedPacket, int64, error) {
	if s.repository == nil {
		return nil, 0, xerr.New(xerr.Unavailable, "wallet repository is not configured")
	}
	query.AppCode = appcode.Normalize(query.AppCode)
	query.RoomID = strings.TrimSpace(query.RoomID)
	query.PacketType = ledger.NormalizeRedPacketType(query.PacketType)
	query.Status = ledger.NormalizeRedPacketStatus(query.Status)
	if query.Page <= 0 {
		query.Page = 1
	}
	if query.PageSize <= 0 {
		query.PageSize = 20
	}
	if query.PageSize > 100 {
		query.PageSize = 100
	}
	ctx = appcode.WithContext(ctx, query.AppCode)
	return s.repository.ListRedPackets(ctx, query)
}

func (s *Service) GetRedPacket(ctx context.Context, appCode string, packetID string, viewerUserID int64, includeClaims bool) (ledger.RedPacket, error) {
	if s.repository == nil {
		return ledger.RedPacket{}, xerr.New(xerr.Unavailable, "wallet repository is not configured")
	}
	packetID = strings.TrimSpace(packetID)
	if packetID == "" {
		return ledger.RedPacket{}, xerr.New(xerr.InvalidArgument, "packet_id is required")
	}
	appCode = appcode.Normalize(appCode)
	ctx = appcode.WithContext(ctx, appCode)
	return s.repository.GetRedPacket(ctx, appCode, packetID, viewerUserID, includeClaims)
}

func (s *Service) ExpireRedPackets(ctx context.Context, appCode string, batchSize int32) (ledger.RedPacketExpireResult, error) {
	if s.repository == nil {
		return ledger.RedPacketExpireResult{}, xerr.New(xerr.Unavailable, "wallet repository is not configured")
	}
	// 空 app_code 专用于内部 worker 的跨租户扫描；显式 RPC 仍归一化并保持单租户边界。
	appCode = strings.ToLower(strings.TrimSpace(appCode))
	if appCode != "" {
		appCode = appcode.Normalize(appCode)
		ctx = appcode.WithContext(ctx, appCode)
	}
	return s.repository.ExpireRedPackets(ctx, appCode, batchSize)
}

func (s *Service) RetryRedPacketRefund(ctx context.Context, appCode string, packetID string, operatorUserID int64) (ledger.RedPacketRefundReceipt, error) {
	if s.repository == nil {
		return ledger.RedPacketRefundReceipt{}, xerr.New(xerr.Unavailable, "wallet repository is not configured")
	}
	packetID = strings.TrimSpace(packetID)
	if packetID == "" || operatorUserID <= 0 {
		return ledger.RedPacketRefundReceipt{}, xerr.New(xerr.InvalidArgument, "red packet refund retry command is incomplete")
	}
	appCode = appcode.Normalize(appCode)
	ctx = appcode.WithContext(ctx, appCode)
	return s.repository.RetryRedPacketRefund(ctx, appCode, packetID, operatorUserID)
}

func normalizeCountTiers(values []int32) []int32 {
	seen := make(map[int32]bool, len(values))
	out := make([]int32, 0, len(values))
	for _, value := range values {
		if value <= 0 || seen[value] {
			continue
		}
		seen[value] = true
		out = append(out, value)
	}
	sort.Slice(out, func(i, j int) bool { return out[i] < out[j] })
	return out
}

func normalizeAmountTiers(values []int64) []int64 {
	seen := make(map[int64]bool, len(values))
	out := make([]int64, 0, len(values))
	for _, value := range values {
		if value <= 0 || seen[value] {
			continue
		}
		seen[value] = true
		out = append(out, value)
	}
	sort.Slice(out, func(i, j int) bool { return out[i] < out[j] })
	return out
}
