package com.bizzan.bitrade.job;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import org.springframework.http.*;
import com.bizzan.bitrade.entity.CurrencyCoin;
import com.bizzan.bitrade.service.CurrencyCoinService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;

/**
 * 定时从币安拉取法币 USDT 汇率并更新数据库
 *
 * 策略：
 *   1. 拉取币安 Spot 所有交易对价格
 *   2. 匹配 {FIAT}USDT（rate = 1/price）或 USDT{FIAT}（rate = price）
 *   3. USD 固定为 1.0
 *   4. Spot 没有的法币（如 CNY）通过币安 C2C P2P 接口获取均价
 *   5. 有记录则更新汇率，无记录则自动插入（symbol 留空，precision 默认 2）
 */
@Component
@Slf4j
public class CurrencyCoinRateJob {

    private static final String BINANCE_TICKER_URL =
            "https://api.binance.com/api/v3/ticker/price";

    /** 币安 C2C P2P 接口：获取 USDT 实际市场价（最符合加密交易所场景） */
    private static final String BINANCE_C2C_URL =
            "https://p2p.binance.com/bapi/c2c/v2/friendly/c2c/adv/search";

    /** 公开外汇接口（备用1）：官方银行间汇率，约比 P2P 低 0.5%~1% */
    private static final String OPEN_ER_URL =
            "https://open.er-api.com/v6/latest/USD";

    /** 公开外汇接口（备用2）：jsDelivr CDN，全球可达 */
    private static final String FAWAZ_URL =
            "https://cdn.jsdelivr.net/npm/@fawazahmed0/currency-api@latest/v1/currencies/usd.json";

    /** 法币符号默认映射（自动插入新记录时使用） */
    private static final Map<String, String> DEFAULT_SYMBOLS = new HashMap<>();
    static {
        DEFAULT_SYMBOLS.put("USD", "$");
        DEFAULT_SYMBOLS.put("CNY", "¥");
        DEFAULT_SYMBOLS.put("EUR", "€");
        DEFAULT_SYMBOLS.put("GBP", "£");
        DEFAULT_SYMBOLS.put("JPY", "¥");
        DEFAULT_SYMBOLS.put("KRW", "₩");
        DEFAULT_SYMBOLS.put("HKD", "HK$");
        DEFAULT_SYMBOLS.put("TWD", "NT$");
        DEFAULT_SYMBOLS.put("TRY", "₺");
        DEFAULT_SYMBOLS.put("BRL", "R$");
        DEFAULT_SYMBOLS.put("RUB", "₽");
        DEFAULT_SYMBOLS.put("INR", "₹");
        DEFAULT_SYMBOLS.put("ARS", "$");
        DEFAULT_SYMBOLS.put("VND", "₫");
        DEFAULT_SYMBOLS.put("THB", "฿");
        DEFAULT_SYMBOLS.put("MYR", "RM");
        DEFAULT_SYMBOLS.put("SGD", "S$");
        DEFAULT_SYMBOLS.put("AUD", "A$");
        DEFAULT_SYMBOLS.put("CAD", "C$");
    }

    @Autowired
    private CurrencyCoinService currencyCoinService;

    @Autowired
    @Qualifier("plainRestTemplate")
    private RestTemplate restTemplate;

    /**
     * 每 60 分钟执行一次
     */
    @Scheduled(cron = "0 0 * * * *")
    public void syncRates() {
        log.info("[CurrencyCoinRateJob] 开始同步法币汇率...");
        try {
            // Step1: 获取所有数据库法币
            List<CurrencyCoin> coinList = currencyCoinService.findAll();

            // Step2: 拉取币安 Spot 所有价格
            Map<String, BigDecimal> spotPrices = fetchSpotPrices();

            // Step3: 提前拉取全量法币汇率（只请求一次，供所有 Spot 没有的法币使用）
            Map<String, BigDecimal> fiatRates = fetchAllFiatRates();
            log.info("[CurrencyCoinRateJob] 法币汇率表获取 {} 条", fiatRates.size());

            for (CurrencyCoin coin : coinList) {
                String currency = coin.getCurrency().toUpperCase();
                BigDecimal rate = resolveRate(currency, spotPrices, fiatRates);
                if (rate == null) {
                    log.warn("[CurrencyCoinRateJob] 无法获取 {} 的汇率，跳过", currency);
                    continue;
                }
                coin.setRate(rate.setScale(coin.getCoinPrecision() != null ? coin.getCoinPrecision() : 2, RoundingMode.HALF_UP));
                currencyCoinService.update(coin);
                log.info("[CurrencyCoinRateJob] 更新 {} 汇率 = {}", currency, coin.getRate());
            }

            log.info("[CurrencyCoinRateJob] 汇率同步完成，共处理 {} 条", coinList.size());
        } catch (Exception e) {
            log.error("[CurrencyCoinRateJob] 汇率同步异常", e);
        }
    }

    /**
     * 根据法币名称解析汇率（1 USDT = ? 法币）
     */
    private BigDecimal resolveRate(String currency, Map<String, BigDecimal> spotPrices,
                                   Map<String, BigDecimal> fiatRates) {
        // USD 固定为 1
        if ("USD".equals(currency)) {
            return BigDecimal.ONE;
        }

        // 尝试 {FIAT}USDT：price = 1 {FIAT} 兑多少 USDT → rate = 1/price
        String pairFiatUsdt = currency + "USDT";
        if (spotPrices.containsKey(pairFiatUsdt)) {
            BigDecimal price = spotPrices.get(pairFiatUsdt);
            if (price.compareTo(BigDecimal.ZERO) > 0) {
                return BigDecimal.ONE.divide(price, 8, RoundingMode.HALF_UP);
            }
        }

        // 尝试 USDT{FIAT}：price = 1 USDT 兑多少 {FIAT} → rate = price
        String pairUsdtFiat = "USDT" + currency;
        if (spotPrices.containsKey(pairUsdtFiat)) {
            return spotPrices.get(pairUsdtFiat);
        }

        // Spot 没有，从预取的法币汇率表中查找（USDT ≈ 1 USD）
        BigDecimal rate = fiatRates.get(currency);
        if (rate != null && rate.compareTo(BigDecimal.ZERO) > 0) {
            log.info("[CurrencyCoinRateJob] {} 使用法币汇率接口，rate={}", currency, rate);
            return rate;
        }

        log.warn("[CurrencyCoinRateJob] 法币汇率表中未找到 {}，fiatRates.size={}", currency, fiatRates.size());
        return null;
    }

    /**
     * 拉取币安 Spot 所有价格
     */
    private Map<String, BigDecimal> fetchSpotPrices() {
        Map<String, BigDecimal> map = new HashMap<>();
        try {
            String json = restTemplate.getForObject(BINANCE_TICKER_URL, String.class);
            JSONArray arr = JSON.parseArray(json);
            for (int i = 0; i < arr.size(); i++) {
                JSONObject obj = arr.getJSONObject(i);
                String symbol = obj.getString("symbol");
                BigDecimal price = obj.getBigDecimal("price");
                if (symbol != null && price != null) {
                    map.put(symbol, price);
                }
            }
        } catch (Exception e) {
            log.error("[CurrencyCoinRateJob] 拉取 Spot 价格失败", e);
        }
        return map;
    }

    /**
     * 一次性拉取全量法币汇率（1 USDT = ? 各法币）
     * 优先级：币安 C2C P2P（实际市场价）→ open.er-api → fawazahmed0 CDN
     */
    private Map<String, BigDecimal> fetchAllFiatRates() {
        // 优先：币安 C2C P2P（逐个法币按需获取，放入 map 供后续使用）
        // 此处先拿 CNY 作为代表性尝试，其他不在 Spot 的法币同理
        Map<String, BigDecimal> result = new HashMap<>();
        BigDecimal cnyCc2 = tryFetchC2CRate("CNY");
        if (cnyCc2 != null) {
            result.put("CNY", cnyCc2);
            log.info("[CurrencyCoinRateJob] 币安 C2C 获取 CNY 成功，rate={}", cnyCc2);
            // C2C 只需要 CNY，其余法币仍从 open.er-api 补充
            Map<String, BigDecimal> erRates = tryFetchOpenEr();
            if (!erRates.isEmpty()) {
                erRates.forEach(result::putIfAbsent); // 不覆盖已有的 C2C 汇率
            }
            return result;
        }

        // 备用1：open.er-api.com（官方外汇牌价）
        log.warn("[CurrencyCoinRateJob] 币安 C2C 失败，切换 open.er-api");
        result = tryFetchOpenEr();
        if (!result.isEmpty()) {
            log.info("[CurrencyCoinRateJob] open.er-api 拉取成功，共 {} 种法币", result.size());
            return result;
        }

        // 备用2：fawazahmed0 CDN
        log.warn("[CurrencyCoinRateJob] open.er-api 失败，切换备用接口 fawazahmed0 CDN");
        result = tryFetchFawaz();
        if (!result.isEmpty()) {
            log.info("[CurrencyCoinRateJob] 备用接口拉取成功，共 {} 种法币", result.size());
            return result;
        }

        log.error("[CurrencyCoinRateJob] 所有接口均失败，法币汇率表为空");
        return result;
    }

    /**
     * 通过币安 C2C P2P 接口获取指定法币的 USDT 市场价（取卖出方均价）
     */
    private BigDecimal tryFetchC2CRate(String fiat) {
        try {
            HttpHeaders headers = new HttpHeaders();
            headers.setContentType(MediaType.APPLICATION_JSON);
            headers.set("User-Agent", "Mozilla/5.0");

            JSONObject body = new JSONObject();
            body.put("proMerchantAds", false);
            body.put("page", 1);
            body.put("rows", 5);
            body.put("payTypes", new JSONArray());
            body.put("countries", new JSONArray());
            body.put("publisherType", null);
            body.put("asset", "USDT");
            body.put("fiat", fiat);
            body.put("tradeType", "SELL");

            HttpEntity<String> entity = new HttpEntity<>(body.toJSONString(), headers);
            ResponseEntity<String> response = restTemplate.exchange(
                    BINANCE_C2C_URL, HttpMethod.POST, entity, String.class);

            if (response.getStatusCode() == HttpStatus.OK && response.getBody() != null) {
                JSONObject resp = JSON.parseObject(response.getBody());
                JSONArray data = resp.getJSONArray("data");
                if (data != null && !data.isEmpty()) {
                    BigDecimal sum = BigDecimal.ZERO;
                    int count = 0;
                    for (int i = 0; i < data.size(); i++) {
                        JSONObject adv = data.getJSONObject(i).getJSONObject("adv");
                        if (adv != null) {
                            BigDecimal price = adv.getBigDecimal("price");
                            if (price != null && price.compareTo(BigDecimal.ZERO) > 0) {
                                sum = sum.add(price);
                                count++;
                            }
                        }
                    }
                    if (count > 0) {
                        return sum.divide(BigDecimal.valueOf(count), 8, RoundingMode.HALF_UP);
                    }
                }
            }
        } catch (Exception e) {
            log.warn("[CurrencyCoinRateJob] 币安 C2C 获取 {} 失败: {}", fiat, e.getMessage());
        }
        return null;
    }

    private Map<String, BigDecimal> tryFetchOpenEr() {
        Map<String, BigDecimal> map = new HashMap<>();
        try {
            String json = restTemplate.getForObject(OPEN_ER_URL, String.class);
            JSONObject resp = JSON.parseObject(json);
            if (!"success".equals(resp.getString("result"))) {
                log.warn("[CurrencyCoinRateJob] open.er-api result={}", resp.getString("result"));
                return map;
            }
            JSONObject rates = resp.getJSONObject("rates");
            if (rates != null) {
                for (String key : rates.keySet()) {
                    BigDecimal val = rates.getBigDecimal(key);
                    if (val != null && val.compareTo(BigDecimal.ZERO) > 0) {
                        map.put(key.toUpperCase(), val);
                    }
                }
            }
        } catch (Exception e) {
            log.error("[CurrencyCoinRateJob] open.er-api 请求失败: {}", e.getMessage());
        }
        return map;
    }

    private Map<String, BigDecimal> tryFetchFawaz() {
        Map<String, BigDecimal> map = new HashMap<>();
        try {
            String json = restTemplate.getForObject(FAWAZ_URL, String.class);
            JSONObject resp = JSON.parseObject(json);
            // 响应格式：{"date":"...","usd":{"cny":7.27,"eur":0.92,...}}
            JSONObject rates = resp.getJSONObject("usd");
            if (rates != null) {
                for (String key : rates.keySet()) {
                    BigDecimal val = rates.getBigDecimal(key);
                    if (val != null && val.compareTo(BigDecimal.ZERO) > 0) {
                        map.put(key.toUpperCase(), val);
                    }
                }
            }
        } catch (Exception e) {
            log.error("[CurrencyCoinRateJob] fawazahmed0 CDN 请求失败: {}", e.getMessage());
        }
        return map;
    }
}
