package wallet

import (
	"context"
	"strings"
	"time"

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

// ListPointWithdrawalCoinSellers 返回服务端过滤后的币商白名单；区域和国家都来自可信用户资料，客户端不能自报服务范围。
func (s *Service) ListPointWithdrawalCoinSellers(ctx context.Context, appCode string, regionID int64, countryCode string, includeDisabled bool) ([]ledger.PointWithdrawalCoinSellerConfig, error) {
	if strings.TrimSpace(appCode) == "" || regionID < 0 {
		return nil, xerr.New(xerr.InvalidArgument, "app_code is required")
	}
	if s.repository == nil {
		return nil, xerr.New(xerr.Unavailable, "wallet repository is not configured")
	}
	appCode = appcode.Normalize(appCode)
	return s.repository.ListPointWithdrawalCoinSellers(appcode.WithContext(ctx, appCode), appCode, regionID, strings.ToUpper(strings.TrimSpace(countryCode)), includeDisabled)
}

// TransferPointToCoinSeller 验证客户端允许提交的最小字段；兑换比例由 repository 事务内锁定配置解析。
func (s *Service) TransferPointToCoinSeller(ctx context.Context, command ledger.PointToCoinSellerCommand) (ledger.PointToCoinSellerReceipt, error) {
	command.SourceAssetType = strings.ToUpper(strings.TrimSpace(command.SourceAssetType))
	if command.SourceAssetType == "" {
		command.SourceAssetType = ledger.AssetPoint
	}
	validAmount := command.PointAmount > 0
	if command.SourceAssetType == ledger.AssetPointDiamond {
		validAmount = command.GrossUSDMinor > 0
	}
	if strings.TrimSpace(command.AppCode) == "" || strings.TrimSpace(command.CommandID) == "" || command.SourceUserID <= 0 || command.SellerUserID <= 0 || !validAmount || command.RegionID <= 0 ||
		(command.SourceAssetType != ledger.AssetPoint && command.SourceAssetType != ledger.AssetPointDiamond) {
		return ledger.PointToCoinSellerReceipt{}, xerr.New(xerr.InvalidArgument, "point coin seller transfer command is incomplete")
	}
	command.AppCode = appcode.Normalize(command.AppCode)
	command.CommandID = strings.TrimSpace(command.CommandID)
	command.SourceCountryCode = strings.ToUpper(strings.TrimSpace(command.SourceCountryCode))
	command.Reason = strings.TrimSpace(command.Reason)
	if len(command.CommandID) > 128 || len(command.SourceCountryCode) > 16 || len(command.Reason) > 512 {
		return ledger.PointToCoinSellerReceipt{}, xerr.New(xerr.InvalidArgument, "point coin seller transfer text fields are too long")
	}
	if s.repository == nil {
		return ledger.PointToCoinSellerReceipt{}, xerr.New(xerr.Unavailable, "wallet repository is not configured")
	}
	return s.repository.TransferPointToCoinSeller(appcode.WithContext(ctx, command.AppCode), command)
}

// ExchangePointToCoin 校验入参；目标 COIN 数量由 repository 事务内的 wallet 政策与统一工资兑币比例决定。
func (s *Service) ExchangePointToCoin(ctx context.Context, command ledger.PointToCoinCommand) (ledger.PointToCoinReceipt, error) {
	command.SourceAssetType = strings.ToUpper(strings.TrimSpace(command.SourceAssetType))
	if command.SourceAssetType == "" {
		command.SourceAssetType = ledger.AssetPoint
	}
	if strings.TrimSpace(command.AppCode) == "" || strings.TrimSpace(command.CommandID) == "" || command.UserID <= 0 || command.PointAmount <= 0 || command.RegionID < 0 ||
		(command.SourceAssetType != ledger.AssetPoint && command.SourceAssetType != ledger.AssetPointDiamond) {
		return ledger.PointToCoinReceipt{}, xerr.New(xerr.InvalidArgument, "point to coin exchange command is incomplete")
	}
	command.AppCode = appcode.Normalize(command.AppCode)
	command.CommandID = strings.TrimSpace(command.CommandID)
	if len(command.CommandID) > 128 {
		return ledger.PointToCoinReceipt{}, xerr.New(xerr.InvalidArgument, "point to coin exchange command id is too long")
	}
	if command.NowMS <= 0 {
		command.NowMS = time.Now().UTC().UnixMilli()
	}
	if s.repository == nil {
		return ledger.PointToCoinReceipt{}, xerr.New(xerr.Unavailable, "wallet repository is not configured")
	}
	return s.repository.ExchangePointToCoin(appcode.WithContext(ctx, command.AppCode), command)
}

// GetPointWithdrawalConfig 读取已发布 wallet 政策快照；nowMS 由服务端补齐，避免客户端选择过期配置。
func (s *Service) GetPointWithdrawalConfig(ctx context.Context, appCode string, regionID int64, nowMS int64) (ledger.PointWithdrawalRuntimeConfig, error) {
	return s.GetPointWithdrawalConfigForAsset(ctx, appCode, regionID, nowMS, ledger.AssetPoint)
}

func (s *Service) GetPointWithdrawalConfigForAsset(ctx context.Context, appCode string, regionID int64, nowMS int64, assetType string) (ledger.PointWithdrawalRuntimeConfig, error) {
	return s.getPointWithdrawalConfig(ctx, appCode, 0, regionID, nowMS, assetType)
}

// GetPointWithdrawalConfigForUser 为钱包 overview 追加实时日期/次数能力；动作执行仍在账变事务内二次校验。
func (s *Service) GetPointWithdrawalConfigForUser(ctx context.Context, appCode string, userID int64, regionID int64, nowMS int64, assetType string) (ledger.PointWithdrawalRuntimeConfig, error) {
	if userID <= 0 {
		return ledger.PointWithdrawalRuntimeConfig{}, xerr.New(xerr.InvalidArgument, "point withdrawal config user is invalid")
	}
	return s.getPointWithdrawalConfig(ctx, appCode, userID, regionID, nowMS, assetType)
}

func (s *Service) getPointWithdrawalConfig(ctx context.Context, appCode string, userID int64, regionID int64, nowMS int64, assetType string) (ledger.PointWithdrawalRuntimeConfig, error) {
	if strings.TrimSpace(appCode) == "" || regionID < 0 {
		return ledger.PointWithdrawalRuntimeConfig{}, xerr.New(xerr.InvalidArgument, "point withdrawal config query is invalid")
	}
	if s.repository == nil {
		return ledger.PointWithdrawalRuntimeConfig{}, xerr.New(xerr.Unavailable, "wallet repository is not configured")
	}
	if nowMS <= 0 {
		nowMS = time.Now().UnixMilli()
	}
	appCode = appcode.Normalize(appCode)
	assetType = strings.ToUpper(strings.TrimSpace(assetType))
	if assetType == "" {
		assetType = ledger.AssetPoint
	}
	if assetType != ledger.AssetPoint && assetType != ledger.AssetPointDiamond {
		return ledger.PointWithdrawalRuntimeConfig{}, xerr.New(xerr.InvalidArgument, "point asset_type is invalid")
	}
	if userID > 0 {
		return s.repository.GetPointWithdrawalConfigForUser(appcode.WithContext(ctx, appCode), appCode, userID, regionID, nowMS, assetType)
	}
	return s.repository.GetPointWithdrawalConfigForAsset(appcode.WithContext(ctx, appCode), appCode, regionID, nowMS, assetType)
}
