package financeorder

import (
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"math"
	"sort"
	"strconv"
	"strings"

	"hyapp-admin-server/internal/appctx"
	"hyapp-admin-server/internal/model"
	"hyapp-admin-server/internal/modules/shared"

	"gorm.io/gorm"
)

const (
	coinSellerRechargeExchangeRateGroup = "finance-coin-seller-recharge"
	coinSellerRechargeExchangeRateKey   = "exchange-rate-policy"
	maxExchangeRateTiers                = 100
	maxExchangeRateWhitelistEntries     = 1000
)

type coinSellerRechargeExchangeRateTierRequest struct {
	MinUSDAmount float64 `json:"minUsdAmount"`
	MaxUSDAmount float64 `json:"maxUsdAmount"`
	CoinsPerUSD  int64   `json:"coinsPerUsd"`
}

type coinSellerRechargeExchangeRateWhitelistRequest struct {
	UserID      string `json:"userId"`
	CoinsPerUSD int64  `json:"coinsPerUsd"`
}

type coinSellerRechargeExchangeRatePolicyRequest struct {
	Tiers     []coinSellerRechargeExchangeRateTierRequest      `json:"tiers"`
	Whitelist []coinSellerRechargeExchangeRateWhitelistRequest `json:"whitelist"`
}

type coinSellerRechargeExchangeRateTierDTO struct {
	MinUSDAmount string `json:"minUsdAmount"`
	MaxUSDAmount string `json:"maxUsdAmount"`
	CoinsPerUSD  int64  `json:"coinsPerUsd"`
}

type coinSellerRechargeExchangeRateWhitelistDTO struct {
	UserID      string `json:"userId"`
	CoinsPerUSD int64  `json:"coinsPerUsd"`
}

type coinSellerRechargeExchangeRatePolicyDTO struct {
	AppCode     string                                       `json:"appCode"`
	Tiers       []coinSellerRechargeExchangeRateTierDTO      `json:"tiers"`
	Whitelist   []coinSellerRechargeExchangeRateWhitelistDTO `json:"whitelist"`
	UpdatedAtMS int64                                        `json:"updatedAtMs"`
}

type coinSellerRechargeQuoteDTO struct {
	AppCode             string `json:"appCode"`
	TargetUserID        string `json:"targetUserId"`
	TargetDisplayUserID string `json:"targetDisplayUserId"`
	USDAmount           string `json:"usdAmount"`
	CoinAmount          int64  `json:"coinAmount"`
	CoinsPerUSD         int64  `json:"coinsPerUsd"`
	RateSource          string `json:"rateSource"`
	MatchedUserID       string `json:"matchedUserId,omitempty"`
	MinUSDAmount        string `json:"minUsdAmount,omitempty"`
	MaxUSDAmount        string `json:"maxUsdAmount,omitempty"`
}

type coinSellerRechargeExchangeRatePolicy struct {
	Tiers     []coinSellerRechargeExchangeRateTier      `json:"tiers"`
	Whitelist []coinSellerRechargeExchangeRateWhitelist `json:"whitelist"`
}

type coinSellerRechargeExchangeRateTier struct {
	MinUSDMinor int64 `json:"minUsdMinor"`
	MaxUSDMinor int64 `json:"maxUsdMinor"`
	CoinsPerUSD int64 `json:"coinsPerUsd"`
}

type coinSellerRechargeExchangeRateWhitelist struct {
	UserID      string `json:"userId"`
	CoinsPerUSD int64  `json:"coinsPerUsd"`
}

func (s *Service) QuoteOrder(ctx context.Context, actor shared.Actor, req coinSellerRechargeQuoteRequest, requestID string) (*coinSellerRechargeQuoteDTO, error) {
	if actor.UserID == 0 || !hasPermission(actor.Permissions, permissionCoinSellerRechargeCreate) {
		return nil, errCoinSellerRechargeForbidden
	}
	appCode := appctx.Normalize(req.AppCode)
	targetUserID := strings.TrimSpace(req.TargetUserID)
	usdAmount, usdMinorAmount, err := normalizeUSD(req.USDAmount)
	if err != nil || appCode == "" || targetUserID == "" {
		return nil, errInvalidCoinSellerRechargeOrderInput
	}
	// 报价先解析真实币商，保证白名单同时支持运营输入的展示 ID 和账务内部 user_id，且不会给非币商生成可提交结果。
	target, err := s.resolveCoinSellerTarget(ctx, requestID, appCode, targetUserID)
	if err != nil {
		return nil, err
	}
	quote, err := s.quoteResolvedTarget(appCode, targetUserID, target, usdMinorAmount)
	if err != nil {
		return nil, err
	}
	quote.USDAmount = usdAmount
	return &quote, nil
}

func (s *Service) createOrderCoinAmount(input normalizedCreateOrderInput, target coinSellerTarget) (int64, error) {
	if input.IsMakeup {
		// 补单仍绑定并校验真实充值凭证、记录 USD 充值统计，但明确不读取汇率、不产生金币发放；GrantOrder 会走既有 0 金币完成分支。
		return 0, nil
	}
	quote, err := s.quoteResolvedTarget(input.AppCode, input.TargetUserID, target, input.USDMinorAmount)
	if err != nil {
		return 0, err
	}
	return quote.CoinAmount, nil
}

func (s *Service) GetExchangeRatePolicy(appCode string) (*coinSellerRechargeExchangeRatePolicyDTO, error) {
	appCode = appctx.Normalize(appCode)
	if appCode == "" {
		return nil, errInvalidCoinSellerRechargeOrderInput
	}
	policy, updatedAtMS, err := s.loadExchangeRatePolicy(appCode)
	if err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return exchangeRatePolicyDTO(appCode, coinSellerRechargeExchangeRatePolicy{}, 0), nil
		}
		return nil, err
	}
	return exchangeRatePolicyDTO(appCode, policy, updatedAtMS), nil
}

func (s *Service) ReplaceExchangeRatePolicy(actor shared.Actor, appCode string, req coinSellerRechargeExchangeRatePolicyRequest) (*coinSellerRechargeExchangeRatePolicyDTO, error) {
	if actor.UserID == 0 || !hasPermission(actor.Permissions, permissionCoinSellerExchangeRate) {
		return nil, errCoinSellerRechargeForbidden
	}
	appCode = appctx.Normalize(appCode)
	policy, err := normalizeExchangeRatePolicyRequest(req)
	if err != nil || appCode == "" {
		if err != nil {
			return nil, err
		}
		return nil, errInvalidCoinSellerRechargeOrderInput
	}
	value, err := json.Marshal(policy)
	if err != nil {
		return nil, err
	}
	// 配置写入 app-scoped 行，禁止一个 APP 的汇率覆盖其他 APP；订单创建会重新读取此行，不信任前端缓存的报价。
	if err := s.store.UpsertScopedAppConfigs(appCode, []model.AppConfig{{
		Group:       coinSellerRechargeExchangeRateGroup,
		Key:         coinSellerRechargeExchangeRateKey,
		Value:       string(value),
		Description: "币商充值 USD 金币兑换区间与用户白名单",
	}}); err != nil {
		return nil, err
	}
	return s.GetExchangeRatePolicy(appCode)
}

func (s *Service) quoteResolvedTarget(appCode string, rawTarget string, target coinSellerTarget, usdMinorAmount int64) (coinSellerRechargeQuoteDTO, error) {
	policy, _, err := s.loadExchangeRatePolicy(appCode)
	if err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return coinSellerRechargeQuoteDTO{}, errors.New("当前 APP 尚未配置币商充值金币汇率")
		}
		return coinSellerRechargeQuoteDTO{}, err
	}
	quote, err := calculateCoinSellerRechargeQuote(policy, rawTarget, target, usdMinorAmount)
	if err != nil {
		return coinSellerRechargeQuoteDTO{}, err
	}
	quote.AppCode = appCode
	quote.TargetUserID = formatInt64(target.UserID)
	quote.TargetDisplayUserID = target.DisplayUserID
	return quote, nil
}

func (s *Service) loadExchangeRatePolicy(appCode string) (coinSellerRechargeExchangeRatePolicy, int64, error) {
	if s == nil || s.store == nil {
		return coinSellerRechargeExchangeRatePolicy{}, 0, errors.New("admin store is not configured")
	}
	item, err := s.store.GetScopedAppConfig(appctx.Normalize(appCode), coinSellerRechargeExchangeRateGroup, coinSellerRechargeExchangeRateKey)
	if err != nil {
		return coinSellerRechargeExchangeRatePolicy{}, 0, err
	}
	var policy coinSellerRechargeExchangeRatePolicy
	if err := json.Unmarshal([]byte(item.Value), &policy); err != nil {
		return coinSellerRechargeExchangeRatePolicy{}, 0, errors.New("币商充值汇率配置损坏")
	}
	return policy, item.UpdatedAtMS, nil
}

func normalizeExchangeRatePolicyRequest(req coinSellerRechargeExchangeRatePolicyRequest) (coinSellerRechargeExchangeRatePolicy, error) {
	if len(req.Tiers) == 0 {
		return coinSellerRechargeExchangeRatePolicy{}, errors.New("至少配置一个 USD 金额区间")
	}
	if len(req.Tiers) > maxExchangeRateTiers || len(req.Whitelist) > maxExchangeRateWhitelistEntries {
		return coinSellerRechargeExchangeRatePolicy{}, errors.New("币商充值汇率配置条目过多")
	}
	policy := coinSellerRechargeExchangeRatePolicy{
		Tiers:     make([]coinSellerRechargeExchangeRateTier, 0, len(req.Tiers)),
		Whitelist: make([]coinSellerRechargeExchangeRateWhitelist, 0, len(req.Whitelist)),
	}
	for _, item := range req.Tiers {
		_, minMinor, minErr := normalizeUSD(item.MinUSDAmount)
		_, maxMinor, maxErr := normalizeUSD(item.MaxUSDAmount)
		if minErr != nil || maxErr != nil || minMinor <= 0 || maxMinor < minMinor || item.CoinsPerUSD <= 0 {
			return coinSellerRechargeExchangeRatePolicy{}, errors.New("USD 金额区间或金币汇率不正确")
		}
		policy.Tiers = append(policy.Tiers, coinSellerRechargeExchangeRateTier{MinUSDMinor: minMinor, MaxUSDMinor: maxMinor, CoinsPerUSD: item.CoinsPerUSD})
	}
	sort.Slice(policy.Tiers, func(i, j int) bool { return policy.Tiers[i].MinUSDMinor < policy.Tiers[j].MinUSDMinor })
	for index := 1; index < len(policy.Tiers); index++ {
		if policy.Tiers[index].MinUSDMinor <= policy.Tiers[index-1].MaxUSDMinor {
			return coinSellerRechargeExchangeRatePolicy{}, errors.New("USD 金额区间不能重叠")
		}
	}
	seen := make(map[string]struct{}, len(req.Whitelist))
	for _, item := range req.Whitelist {
		userID := strings.TrimSpace(item.UserID)
		if userID == "" || item.CoinsPerUSD <= 0 {
			return coinSellerRechargeExchangeRatePolicy{}, errors.New("白名单用户 ID 或金币汇率不正确")
		}
		if _, exists := seen[userID]; exists {
			return coinSellerRechargeExchangeRatePolicy{}, fmt.Errorf("白名单用户 ID 重复: %s", userID)
		}
		seen[userID] = struct{}{}
		policy.Whitelist = append(policy.Whitelist, coinSellerRechargeExchangeRateWhitelist{UserID: userID, CoinsPerUSD: item.CoinsPerUSD})
	}
	return policy, nil
}

func calculateCoinSellerRechargeQuote(policy coinSellerRechargeExchangeRatePolicy, rawTarget string, target coinSellerTarget, usdMinorAmount int64) (coinSellerRechargeQuoteDTO, error) {
	if usdMinorAmount <= 0 {
		return coinSellerRechargeQuoteDTO{}, errors.New("充值美金数量不正确")
	}
	targetIDs := map[string]struct{}{
		strings.TrimSpace(rawTarget):            {},
		strings.TrimSpace(target.DisplayUserID): {},
		formatInt64(target.UserID):              {},
	}
	for _, item := range policy.Whitelist {
		if _, ok := targetIDs[item.UserID]; ok {
			return quoteFromRate(usdMinorAmount, item.CoinsPerUSD, "whitelist", item.UserID, 0, 0)
		}
	}
	for _, item := range policy.Tiers {
		if usdMinorAmount >= item.MinUSDMinor && usdMinorAmount <= item.MaxUSDMinor {
			return quoteFromRate(usdMinorAmount, item.CoinsPerUSD, "tier", "", item.MinUSDMinor, item.MaxUSDMinor)
		}
	}
	return coinSellerRechargeQuoteDTO{}, errors.New("USD 金额未命中当前 APP 的金币兑换区间")
}

func quoteFromRate(usdMinorAmount int64, coinsPerUSD int64, source string, matchedUserID string, minMinor int64, maxMinor int64) (coinSellerRechargeQuoteDTO, error) {
	// USD 最多两位小数；金币必须为整数，因此按最接近整数四舍五入，创建订单时会用同一公式重新计算。
	if coinsPerUSD <= 0 || usdMinorAmount > (math.MaxInt64-50)/coinsPerUSD {
		return coinSellerRechargeQuoteDTO{}, errors.New("计算后的充值金币数量过大")
	}
	coinAmount := (usdMinorAmount*coinsPerUSD + 50) / 100
	return coinSellerRechargeQuoteDTO{
		USDAmount:     formatUSDMinorAmount(usdMinorAmount),
		CoinAmount:    coinAmount,
		CoinsPerUSD:   coinsPerUSD,
		RateSource:    source,
		MatchedUserID: matchedUserID,
		MinUSDAmount:  optionalUSDMinorAmount(minMinor),
		MaxUSDAmount:  optionalUSDMinorAmount(maxMinor),
	}, nil
}

func exchangeRatePolicyDTO(appCode string, policy coinSellerRechargeExchangeRatePolicy, updatedAtMS int64) *coinSellerRechargeExchangeRatePolicyDTO {
	dto := &coinSellerRechargeExchangeRatePolicyDTO{
		AppCode:     appCode,
		Tiers:       make([]coinSellerRechargeExchangeRateTierDTO, 0, len(policy.Tiers)),
		Whitelist:   make([]coinSellerRechargeExchangeRateWhitelistDTO, 0, len(policy.Whitelist)),
		UpdatedAtMS: updatedAtMS,
	}
	for _, item := range policy.Tiers {
		dto.Tiers = append(dto.Tiers, coinSellerRechargeExchangeRateTierDTO{MinUSDAmount: formatUSDMinorAmount(item.MinUSDMinor), MaxUSDAmount: formatUSDMinorAmount(item.MaxUSDMinor), CoinsPerUSD: item.CoinsPerUSD})
	}
	for _, item := range policy.Whitelist {
		dto.Whitelist = append(dto.Whitelist, coinSellerRechargeExchangeRateWhitelistDTO{UserID: item.UserID, CoinsPerUSD: item.CoinsPerUSD})
	}
	return dto
}

func formatUSDMinorAmount(value int64) string {
	return strconv.FormatInt(value/100, 10) + "." + fmt.Sprintf("%02d", value%100)
}

func optionalUSDMinorAmount(value int64) string {
	if value <= 0 {
		return ""
	}
	return formatUSDMinorAmount(value)
}
