package com.datacenter.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.datacenter.entity.stat.StatCopyTradePosition;
import com.datacenter.mapper.StatCopyTradePositionMapper;
import com.datacenter.service.StatCopyTradePositionService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.Collections;
import java.util.List;
import java.util.Map;

@Slf4j
@Service
public class StatCopyTradePositionServiceImpl
        extends ServiceImpl<StatCopyTradePositionMapper, StatCopyTradePosition>
        implements StatCopyTradePositionService {

    @Resource
    private RestTemplate restTemplate;

    @Value("${admin.host}")
    private String adminHost;

    @Override
    public void computeAndSave() {
        Map<String, BigDecimal> prices = fetchPrices();
        if (prices.isEmpty()) {
            log.error("[CopyTradePosition] 无法获取实时价格，本次快照已跳过");
            return;
        }
        doComputeAndSave(prices);
    }

    @Override
    public void computeAndSave(Map<String, BigDecimal> externalPrices) {
        if (externalPrices == null || externalPrices.isEmpty()) {
            log.warn("[CopyTradePosition] 外部价格为空，回退到自行拉取");
            computeAndSave();
            return;
        }
        doComputeAndSave(externalPrices);
    }

    private void doComputeAndSave(Map<String, BigDecimal> prices) {
        LocalDateTime statTime = LocalDateTime.now().truncatedTo(ChronoUnit.SECONDS);
        log.info("[CopyTradePosition] 开始快照统计 statTime={}", statTime);

        List<StatCopyTradePosition> rows = baseMapper.computeSnapshot(statTime);
        log.info("[CopyTradePosition] 查询到 {} 条原始记录", rows.size());

        for (StatCopyTradePosition row : rows) {
            // 价格 Map 可能用 "ETHUSDT" 或 "ETH/USDT" 两种格式，都尝试
            BigDecimal curPrice = prices.getOrDefault(row.getSymbol(),
                    prices.getOrDefault(row.getSymbol().replace("/", ""), BigDecimal.ZERO));
            if (curPrice.compareTo(BigDecimal.ZERO) == 0) {
                log.warn("[CopyTradePosition] symbol={} 无法获取实时价格，跳过该行", row.getSymbol());
                continue;
            }
            enrichRow(row, curPrice);
        }
        rows.removeIf(r -> r.getTraderPosUsdt() == null);

        remove(new LambdaQueryWrapper<>());
        if (!rows.isEmpty()) {
            saveBatch(rows);
        }
        log.info("[CopyTradePosition] 快照写入完成，共 {} 条", rows.size());
    }

    private void enrichRow(StatCopyTradePosition row, BigDecimal curPrice) {
        BigDecimal shareNum    = safe(row.getShareNumber());
        BigDecimal traderQty   = safe(row.getTraderQty());
        BigDecimal traderAvg   = safe(row.getTraderAvgOpen());
        BigDecimal traderPri   = safe(row.getTraderTotalPrincipal());
        int dir                = row.getDirection() != null ? row.getDirection() : 0;
        // COIN type (money_type=1): qty is already in base coin, pnl = priceDiff * qty
        // USDT type (money_type=0): qty is in contracts, pnl = priceDiff * qty * shareNum / avgOpen
        boolean isCoin         = Integer.valueOf(1).equals(row.getMoneyType());

        // 带单者
        if (traderAvg.compareTo(BigDecimal.ZERO) > 0) {
            BigDecimal posUsdt;
            BigDecimal pnl;
            BigDecimal priceDiff = dir == 0 ? curPrice.subtract(traderAvg) : traderAvg.subtract(curPrice);
            if (isCoin) {
                posUsdt = traderQty.multiply(curPrice).setScale(2, RoundingMode.HALF_UP);
                pnl     = priceDiff.multiply(traderQty).setScale(2, RoundingMode.HALF_UP);
            } else {
                posUsdt = traderQty.multiply(shareNum).multiply(curPrice)
                                   .divide(traderAvg, 2, RoundingMode.HALF_UP);
                pnl     = priceDiff.multiply(traderQty).multiply(shareNum)
                                   .divide(traderAvg, 2, RoundingMode.HALF_UP);
            }
            BigDecimal rate = traderPri.compareTo(BigDecimal.ZERO) > 0
                    ? pnl.multiply(BigDecimal.valueOf(100)).divide(traderPri, 2, RoundingMode.HALF_UP)
                    : BigDecimal.ZERO;
            row.setTraderPosUsdt(posUsdt);
            row.setTraderUnrealizedPnl(pnl);
            row.setProfitRate(rate);
        } else {
            row.setTraderPosUsdt(BigDecimal.ZERO);
            row.setTraderUnrealizedPnl(BigDecimal.ZERO);
            row.setProfitRate(BigDecimal.ZERO);
        }

        // 普通跟单
        BigDecimal normalQty = safe(row.getNormalFollowQty());
        BigDecimal normalAvg = safe(row.getNormalFollowAvgOpen());
        if (normalAvg.compareTo(BigDecimal.ZERO) > 0) {
            BigDecimal priceDiff = dir == 0 ? curPrice.subtract(normalAvg) : normalAvg.subtract(curPrice);
            BigDecimal posUsdt;
            BigDecimal pnl;
            if (isCoin) {
                posUsdt = normalQty.multiply(curPrice).setScale(2, RoundingMode.HALF_UP);
                pnl     = priceDiff.multiply(normalQty).setScale(2, RoundingMode.HALF_UP);
            } else {
                posUsdt = normalQty.multiply(shareNum).multiply(curPrice)
                                   .divide(normalAvg, 2, RoundingMode.HALF_UP);
                pnl     = priceDiff.multiply(normalQty).multiply(shareNum)
                                   .divide(normalAvg, 2, RoundingMode.HALF_UP);
            }
            row.setNormalFollowPosUsdt(posUsdt);
            row.setNormalFollowPnl(pnl);
        } else {
            row.setNormalFollowPosUsdt(BigDecimal.ZERO);
            row.setNormalFollowPnl(BigDecimal.ZERO);
        }

        // 无损跟单
        BigDecimal specialQty = safe(row.getSpecialFollowQty());
        BigDecimal specialAvg = safe(row.getSpecialFollowAvgOpen());
        if (specialAvg.compareTo(BigDecimal.ZERO) > 0) {
            BigDecimal priceDiff = dir == 0 ? curPrice.subtract(specialAvg) : specialAvg.subtract(curPrice);
            BigDecimal posUsdt;
            BigDecimal pnl;
            if (isCoin) {
                posUsdt = specialQty.multiply(curPrice).setScale(2, RoundingMode.HALF_UP);
                pnl     = priceDiff.multiply(specialQty).setScale(2, RoundingMode.HALF_UP);
            } else {
                posUsdt = specialQty.multiply(shareNum).multiply(curPrice)
                                    .divide(specialAvg, 2, RoundingMode.HALF_UP);
                pnl     = priceDiff.multiply(specialQty).multiply(shareNum)
                                   .divide(specialAvg, 2, RoundingMode.HALF_UP);
            }
            row.setSpecialFollowPosUsdt(posUsdt);
            row.setSpecialFollowPnl(pnl);
        } else {
            row.setSpecialFollowPosUsdt(BigDecimal.ZERO);
            row.setSpecialFollowPnl(BigDecimal.ZERO);
        }
    }

    private Map<String, BigDecimal> fetchPrices() {
        // 调 Admin 的免鉴权代理接口（Admin 内部有 Eureka/Ribbon 可解析 contract-swap-api）
        try {
            String url = adminHost + "/admin/noauth/swap-prices";
            ParameterizedTypeReference<Map<String, BigDecimal>> typeRef =
                    new ParameterizedTypeReference<Map<String, BigDecimal>>() {};
            ResponseEntity<Map<String, BigDecimal>> resp =
                    restTemplate.exchange(url, HttpMethod.POST, new HttpEntity<>(null), typeRef);
            if (resp.getBody() != null && !resp.getBody().isEmpty()) {
                log.info("[CopyTradePosition] 获取到 {} 个交易对价格", resp.getBody().size());
                return resp.getBody();
            }
        } catch (Exception e) {
            log.warn("[CopyTradePosition] 获取实时价格失败: {}", e.getMessage());
        }
        return Collections.emptyMap();
    }

    private BigDecimal safe(BigDecimal v) {
        return v != null ? v : BigDecimal.ZERO;
    }
}
