package com.bizzan.bitrade.service;

import com.bizzan.bitrade.entity.ConfigCoinSymbol;
import com.bizzan.bitrade.entity.MemberWallet;
import com.bizzan.bitrade.vo.FundAssetDto;
import com.bizzan.bitrade.vo.FundAssetListResp;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 资金账户资产列表服务
 * <p>
 * 查询用户 member_wallet 中各币种余额，折合 USDT 估值后返回。
 * 可开启的币种列表取自 config_coin_symbol（内存缓存，10 分钟刷新）。
 * </p>
 *
 * @author auto
 * @date 2026-05-20
 */
@Slf4j
@Service
public class FundAssetService {

    /** Redis hash key，存储现货最新价格，field 格式：{coinUnit}usdt（全小写） */
    private static final String REDIS_LATEST_PRICE_KEY = "ExchangeNew2:latestPrice";

    /** USDT 汇率精度（小数位） */
    private static final int DEFAULT_USDT_PRECISION = 2;

    /** USDT 币种符号 */
    private static final String USDT = "USDT";

    @Autowired
    private MemberWalletService memberWalletService;

    @Autowired
    private ConfigCoinSymbolService configCoinSymbolService;

    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    /**
     * 获取用户资金账户资产列表
     *
     * @param memberId 用户ID
     * @param hideZero true=隐藏余额为 0 的账户；false=展示所有开启币种（余额不足时补0）
     * @return 资产列表响应，包含各币种资产及 USDT 总估值
     * @author auto
     * @date 2026-05-20
     */
    @Transactional(readOnly = true)
    public FundAssetListResp getUserAssetList(Long memberId, boolean hideZero) {
        // 1. 按模式构建资产列表
        List<FundAssetDto> assetList;
        if (hideZero) {
            assetList = buildHideZeroList(memberId);
        } else {
            assetList = buildShowAllList(memberId);
        }

        // 2. 汇总 USDT 总估值
        BigDecimal totalAmount = calcTotalUsdt(assetList);

        FundAssetListResp resp = new FundAssetListResp();
        resp.setAssetList(assetList);
        resp.setTotalAmount(totalAmount);
        return resp;
    }

    /**
     * 隐藏零余额模式：仅返回 balance + frozenBalance > 0 的钱包
     *
     * @param memberId 用户ID
     * @return 资产DTO列表（按 USDT 估值降序）
     */
    private List<FundAssetDto> buildHideZeroList(Long memberId) {
        List<MemberWallet> wallets = memberWalletService.findAllByMemberId(memberId);
        if (wallets == null || wallets.isEmpty()) {
            return new ArrayList<>();
        }

        return wallets.stream()
                .filter(w -> w.getCoin() != null && w.getCoin().getUnit() != null)
                .filter(this::hasBalance)
                .map(this::buildAssetDto)
                .sorted(this::compareByUsdtAmountDesc)
                .collect(Collectors.toList());
    }

    /**
     * 显示全部模式：遍历所有开启币种，有钱包取实际余额，无钱包补 0 余额 DTO
     *
     * @param memberId 用户ID
     * @return 资产DTO列表（按 USDT 估值降序）
     */
    private List<FundAssetDto> buildShowAllList(Long memberId) {
        // 1. 构建用户钱包 symbol → wallet 映射
        List<MemberWallet> wallets = memberWalletService.findAllByMemberId(memberId);
        Map<String, MemberWallet> walletMap = buildWalletMap(wallets);

        // 2. 遍历所有开启币种
        List<ConfigCoinSymbol> openCoins = configCoinSymbolService.getOpenCoinSymbols();
        if (openCoins == null || openCoins.isEmpty()) {
            return new ArrayList<>();
        }

        return openCoins.stream()
                .filter(c -> c.getCoinSymbol() != null)
                .map(c -> {
                    MemberWallet w = walletMap.get(c.getCoinSymbol());
                    if (w != null) {
                        return buildAssetDto(w);
                    } else {
                        return buildZeroAssetDto(c.getCoinSymbol());
                    }
                })
                .sorted(this::compareByUsdtAmountDesc)
                .collect(Collectors.toList());
    }

    /**
     * 构建 symbol → MemberWallet 映射，symbol 键同 coin.unit
     *
     * @param wallets 钱包列表
     * @return 映射表
     */
    private Map<String, MemberWallet> buildWalletMap(List<MemberWallet> wallets) {
        if (wallets == null || wallets.isEmpty()) {
            return Collections.emptyMap();
        }
        return wallets.stream()
                .filter(w -> w.getCoin() != null && w.getCoin().getUnit() != null)
                .collect(Collectors.toMap(
                        w -> w.getCoin().getUnit(),
                        w -> w,
                        (a, b) -> a));
    }

    /**
     * 判断钱包是否有余额（balance + frozenBalance > 0）
     *
     * @param wallet 钱包
     * @return true 表示有余额
     */
    private boolean hasBalance(MemberWallet wallet) {
        BigDecimal bal    = wallet.getBalance()       != null ? wallet.getBalance()       : BigDecimal.ZERO;
        BigDecimal frozen = wallet.getFrozenBalance() != null ? wallet.getFrozenBalance() : BigDecimal.ZERO;
        return bal.add(frozen).compareTo(BigDecimal.ZERO) > 0;
    }

    /**
     * 将 MemberWallet 转换为资产 DTO，并计算 USDT 估值
     *
     * @param wallet 用户钱包
     * @return 资产 DTO
     */
    private FundAssetDto buildAssetDto(MemberWallet wallet) {
        BigDecimal available = wallet.getBalance()       != null ? wallet.getBalance()       : BigDecimal.ZERO;
        BigDecimal frozen    = wallet.getFrozenBalance() != null ? wallet.getFrozenBalance() : BigDecimal.ZERO;
        BigDecimal total     = available.add(frozen);
        String symbol        = wallet.getCoin().getUnit();
        BigDecimal rate      = getRate(symbol);

        FundAssetDto dto = new FundAssetDto();
        dto.setSymbol(symbol);
        dto.setAvailable(available);
        dto.setFrozen(frozen);
        dto.setTotal(total);
        dto.setRate(rate);

        if (rate != null && rate.compareTo(BigDecimal.ZERO) > 0) {
            dto.setAvailableUsdt(available.multiply(rate).setScale(DEFAULT_USDT_PRECISION, RoundingMode.DOWN));
            dto.setFrozenUsdt(frozen.multiply(rate).setScale(DEFAULT_USDT_PRECISION, RoundingMode.DOWN));
            dto.setUsdtAmount(total.multiply(rate).setScale(DEFAULT_USDT_PRECISION, RoundingMode.DOWN));
        } else {
            dto.setAvailableUsdt(BigDecimal.ZERO);
            dto.setFrozenUsdt(BigDecimal.ZERO);
            dto.setUsdtAmount(BigDecimal.ZERO);
        }
        return dto;
    }

    /**
     * 构建余额全为 0 的资产 DTO（用于展示开启但未持有的币种）
     *
     * @param symbol 币种符号
     * @return 零余额资产 DTO
     */
    private FundAssetDto buildZeroAssetDto(String symbol) {
        FundAssetDto dto = new FundAssetDto();
        dto.setSymbol(symbol);
        dto.setAvailable(BigDecimal.ZERO);
        dto.setFrozen(BigDecimal.ZERO);
        dto.setTotal(BigDecimal.ZERO);
        dto.setRate(getRate(symbol));
        dto.setAvailableUsdt(BigDecimal.ZERO);
        dto.setFrozenUsdt(BigDecimal.ZERO);
        dto.setUsdtAmount(BigDecimal.ZERO);
        return dto;
    }

    /**
     * 汇总资产列表的 USDT 总估值
     *
     * @param assetList 资产列表
     * @return USDT 总估值
     */
    private BigDecimal calcTotalUsdt(List<FundAssetDto> assetList) {
        return assetList.stream()
                .map(FundAssetDto::getUsdtAmount)
                .filter(v -> v != null && v.compareTo(BigDecimal.ZERO) > 0)
                .reduce(BigDecimal.ZERO, BigDecimal::add)
                .setScale(DEFAULT_USDT_PRECISION, RoundingMode.DOWN);
    }

    /**
     * 按 USDT 估值降序比较（用于排序）
     *
     * @param a 资产A
     * @param b 资产B
     * @return 比较结果
     */
    private int compareByUsdtAmountDesc(FundAssetDto a, FundAssetDto b) {
        BigDecimal amtA = a.getUsdtAmount() != null ? a.getUsdtAmount() : BigDecimal.ZERO;
        BigDecimal amtB = b.getUsdtAmount() != null ? b.getUsdtAmount() : BigDecimal.ZERO;
        return amtB.compareTo(amtA);
    }

    /**
     * 从 Redis ExchangeNew2:latestPrice 获取币种对 USDT 的汇率
     * USDT 本身直接返回 1，其他币种 field 格式为全小写拼接，如 btcusdt
     *
     * @param symbol 币种符号
     * @return 汇率，无行情时返回 null
     */
    private BigDecimal getRate(String symbol) {
        if (USDT.equalsIgnoreCase(symbol)) {
            return BigDecimal.ONE;
        }
        try {
            String field = symbol.toLowerCase() + "usdt";
            Object val = redisTemplate.opsForHash().get(REDIS_LATEST_PRICE_KEY, field);
            if (val != null && !val.toString().trim().isEmpty()) {
                BigDecimal price = new BigDecimal(val.toString().trim());
                if (price.compareTo(BigDecimal.ZERO) > 0) {
                    return price;
                }
            }
        } catch (Exception e) {
            log.warn("[FundAssetService] 获取币种汇率失败 symbol={}, error={}", symbol, e.getMessage());
        }
        return null;
    }
}
