package com.market.service;

import com.baomidou.dynamic.datasource.annotation.DS;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.market.constant.SpotRedisKey;
import com.market.entity.Kline;
import com.market.mapper.SpotKlineMapper;
import com.market.model.SpotBaseMessage;
import com.market.utils.SpotConstant;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

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

/**
 * 现货数据查询服务（响应前端 req 请求）
 * <p>
 * 支持：K线历史、深度数据、成交记录、多日行情查询。
 *
 * @author system
 * @date 2026-05-07
 */
@Service
public class SpotDataQueryService {

    private static final Logger log = LoggerFactory.getLogger(SpotDataQueryService.class);

    private final StringRedisTemplate redisTemplate;
    private final SpotKlineMapper klineMapper;
    private final ObjectMapper objectMapper;

    public SpotDataQueryService(StringRedisTemplate redisTemplate,
                                SpotKlineMapper klineMapper) {
        this.redisTemplate = redisTemplate;
        this.klineMapper   = klineMapper;
        this.objectMapper  = new ObjectMapper();
    }

    /**
     * 根据 channel 类型查询对应数据
     *
     * @param channel   channel 名（小写）
     * @param message   原始请求消息（含参数）
     * @param isFlutter 是否 Flutter 客户端
     * @return 响应 Map，查询失败返回 null
     */
    public Map<String, Object> queryData(String channel, SpotBaseMessage message, boolean isFlutter) {
        try {
            long timestamp = System.currentTimeMillis();
            String symbol  = message.getSymbol();
            if (symbol == null || symbol.isEmpty()) {
                return null;
            }

            Map<String, Object> response = new HashMap<>();
            response.put(SpotConstant.RESPONSE_FIELD_CHANNEL, channel);
            response.put(SpotConstant.RESPONSE_FIELD_TS, timestamp);
            response.put(SpotConstant.RESPONSE_FIELD_EVENT_REP, SpotConstant.EVENT_REP);

            // 1. K线历史
            if (channel.contains(SpotConstant.CHANNEL_TYPE_KLINE)) {
                return queryKlineData(channel, message, response, symbol);
            }
            // 2. 深度数据
            if (channel.contains(SpotConstant.CHANNEL_TYPE_DEPTH)) {
                response.put(SpotConstant.RESPONSE_FIELD_STATUS, SpotConstant.STATUS_OK);
                response.put(SpotConstant.RESPONSE_FIELD_EVENT, SpotConstant.EVENT_REQ);
                return queryDepthData(channel, response, symbol);
            }
            // 3. 成交记录
            if (channel.contains(SpotConstant.CHANNEL_TYPE_TRADE_TICKER)) {
                response.put(SpotConstant.RESPONSE_FIELD_STATUS, SpotConstant.STATUS_OK);
                response.put(SpotConstant.RESPONSE_FIELD_EVENT, SpotConstant.EVENT_REQ);
                return queryTradeData(channel, message, response, symbol);
            }
            // 4. 多日行情
            if (channel.contains(SpotConstant.CHANNEL_TYPE_DETAIL_TRADE)) {
                response.put(SpotConstant.RESPONSE_FIELD_STATUS, SpotConstant.STATUS_OK);
                response.put(SpotConstant.RESPONSE_FIELD_EVENT, SpotConstant.EVENT_REQ);
                return queryDetailTradeData(channel, response, symbol);
            }

            return response;
        } catch (Exception e) {
            log.error("查询数据失败: channel={}", channel, e);
            return null;
        }
    }

    // ------------------------------------------------------------------ K线历史

    private Map<String, Object> queryKlineData(String channel, SpotBaseMessage message,
                                               Map<String, Object> response, String symbol) {
        try {
            String[] channelArr = channel.split("_");
            if (channelArr.length < SpotConstant.CHANNEL_MIN_PARTS_LENGTH) {
                return response;
            }
            String flutterPeriod = channelArr[3];
            String dbPeriod = SpotConstant.FLUTTER_KLINE_TO_DB_PERIOD.get(flutterPeriod);
            if (dbPeriod == null) {
                return response;
            }

            int  pageSize = SpotConstant.DEFAULT_KLINE_PAGE_SIZE;
            long endIdx   = 0;
            if (message.getParams() != null) {
                if (message.getParams().containsKey(SpotConstant.PARAM_FIELD_PAGE_SIZE)) {
                    pageSize = ((Number) message.getParams().get(SpotConstant.PARAM_FIELD_PAGE_SIZE)).intValue();
                }
                if (message.getParams().containsKey(SpotConstant.PARAM_FIELD_END_IDX)) {
                    endIdx = ((Number) message.getParams().get(SpotConstant.PARAM_FIELD_END_IDX)).longValue();
                }
            }

            List<Map<String, Object>> dataResult = queryKlineFromDatabase(symbol, dbPeriod, endIdx, pageSize);
            response.put(SpotConstant.RESPONSE_FIELD_DATA, dataResult);
        } catch (Exception e) {
            log.error("查询 K 线数据失败: channel={}, symbol={}", channel, symbol, e);
            response.put(SpotConstant.RESPONSE_FIELD_DATA, new ArrayList<>());
        }
        return response;
    }

    /**
     * 从数据库查询 K 线数据（使用 spot_kline 数据源）
     *
     * @param symbol   交易对（小写）
     * @param dbPeriod 数据库周期
     * @param endIdx   结束索引（0表示不限）
     * @param pageSize 每页条数
     * @return K 线数据列表
     */
    @DS("market")
    public List<Map<String, Object>> queryKlineFromDatabase(String symbol, String dbPeriod,
                                                             long endIdx, int pageSize) {
        List<Map<String, Object>> result = new ArrayList<>();
        try {
            String tableName = SpotConstant.TABLE_KLINE_PREFIX + symbol.toLowerCase();
            Long endTime = endIdx > 0 ? endIdx * SpotConstant.MILLISECONDS_TO_SECONDS : null;

            List<Kline> klines = klineMapper.selectByPeriodAndTimeRange(
                    tableName, dbPeriod, null, endTime, pageSize);

            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            for (int i = klines.size() - 1; i >= 0; i--) {
                Kline kline = klines.get(i);
                long id = kline.getBeginTime() / SpotConstant.MILLISECONDS_TO_SECONDS;
                Map<String, Object> klineData = new HashMap<>();
                klineData.put(SpotConstant.DATA_FIELD_ID,     id);
                klineData.put(SpotConstant.DATA_FIELD_OPEN,   kline.getOpen());
                klineData.put(SpotConstant.DATA_FIELD_CLOSE,  kline.getClose());
                klineData.put(SpotConstant.DATA_FIELD_HIGH,   kline.getHigh());
                klineData.put(SpotConstant.DATA_FIELD_LOW,    kline.getLow());
                klineData.put(SpotConstant.DATA_FIELD_VOL,    kline.getVolume());
                klineData.put(SpotConstant.DATA_FIELD_AMOUNT, kline.getTurnover());
                klineData.put(SpotConstant.DATA_FIELD_DS,     sdf.format(new Date(id * SpotConstant.MILLISECONDS_TO_SECONDS)));
                result.add(klineData);
            }
        } catch (Exception e) {
            log.error("从数据库查询 K 线数据失败: symbol={}, period={}", symbol, dbPeriod, e);
        }
        return result;
    }

    // ------------------------------------------------------------------ 深度数据

    private Map<String, Object> queryDepthData(String channel, Map<String, Object> response, String symbol) {
        try {
            String askKey = SpotConstant.REDIS_DEPTH_ASK_PREFIX + symbol;
            String bidKey = SpotConstant.REDIS_DEPTH_BID_PREFIX + symbol;

            String askData = redisTemplate.opsForValue().get(askKey);
            String bidData = redisTemplate.opsForValue().get(bidKey);

            List<List<String>> asks = parseDepthList(askData);
            List<List<String>> bids = parseDepthList(bidData);

            if (asks.size() > SpotConstant.DEFAULT_DEPTH_LIMIT) {
                asks = asks.subList(0, SpotConstant.DEFAULT_DEPTH_LIMIT);
            }
            if (bids.size() > SpotConstant.DEFAULT_DEPTH_LIMIT) {
                bids = bids.subList(0, SpotConstant.DEFAULT_DEPTH_LIMIT);
            }

            Map<String, Object> tick = new HashMap<>();
            tick.put(SpotConstant.DATA_FIELD_ASKS, asks);
            tick.put(SpotConstant.DATA_FIELD_BUYS, bids);
            response.put(SpotConstant.RESPONSE_FIELD_TICK, tick);
        } catch (Exception e) {
            log.error("查询深度数据失败: channel={}, symbol={}", channel, symbol, e);
            Map<String, Object> tick = new HashMap<>();
            tick.put(SpotConstant.DATA_FIELD_ASKS, new ArrayList<>());
            tick.put(SpotConstant.DATA_FIELD_BUYS, new ArrayList<>());
            response.put(SpotConstant.RESPONSE_FIELD_TICK, tick);
        }
        return response;
    }

    // ------------------------------------------------------------------ 成交记录

    private Map<String, Object> queryTradeData(String channel, SpotBaseMessage message,
                                               Map<String, Object> response, String symbol) {
        try {
            int pageSize = SpotConstant.DEFAULT_TRADE_PAGE_SIZE;
            if (message.getParams() != null && message.getParams().containsKey(SpotConstant.PARAM_FIELD_TOP)) {
                pageSize = Math.min(
                        ((Number) message.getParams().get(SpotConstant.PARAM_FIELD_TOP)).intValue(),
                        SpotConstant.MAX_TRADE_PAGE_SIZE);
            }

            String cacheKey = SpotRedisKey.trade(symbol);
            Set<String> tradeHistory = redisTemplate.opsForZSet().reverseRange(cacheKey, 0, -1);
            List<Map<String, Object>> dataResult = new ArrayList<>();
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

            if (tradeHistory != null) {
                int count = 0;
                for (String trade : tradeHistory) {
                    if (count >= pageSize) {
                        break;
                    }
                    Map<String, Object> tempData = objectMapper.readValue(trade, new TypeReference<Map<String, Object>>() {});
                    long time = ((Number) tempData.get(SpotConstant.DATA_FIELD_TIME)).longValue();
                    long id   = time / SpotConstant.MILLISECONDS_TO_SECONDS;
                    tempData.put(SpotConstant.DATA_FIELD_TS, time);
                    tempData.put(SpotConstant.DATA_FIELD_DS, sdf.format(new Date(id * SpotConstant.MILLISECONDS_TO_SECONDS)));
                    dataResult.add(tempData);
                    count++;
                }
            }

            Map<String, Object> tick = new HashMap<>();
            tick.put(SpotConstant.RESPONSE_FIELD_DATA, dataResult);
            response.put(SpotConstant.RESPONSE_FIELD_TICK, tick);
        } catch (Exception e) {
            log.error("查询成交数据失败: channel={}, symbol={}", channel, symbol, e);
            Map<String, Object> tick = new HashMap<>();
            tick.put(SpotConstant.RESPONSE_FIELD_DATA, new ArrayList<>());
            response.put(SpotConstant.RESPONSE_FIELD_TICK, tick);
        }
        return response;
    }

    // ------------------------------------------------------------------ 多日行情

    private Map<String, Object> queryDetailTradeData(String channel, Map<String, Object> response, String symbol) {
        try {
            String cacheKey = SpotRedisKey.longTimeTicker(symbol);
            Map<Object, Object> longTimeTicker = redisTemplate.opsForHash().entries(cacheKey);
            Map<String, Map<String, String>> result = new HashMap<>();

            for (Map.Entry<Object, Object> entry : longTimeTicker.entrySet()) {
                String key   = entry.getKey().toString();
                String value = entry.getValue().toString();
                Map<String, String> tempData = objectMapper.readValue(value, new TypeReference<Map<String, String>>() {});

                tempData.put(SpotConstant.DATA_FIELD_SCALE,
                        tempData.get(SpotConstant.DATA_FIELD_ROSE) + SpotConstant.PERCENTAGE_SIGN);

                try {
                    BigDecimal rose = new BigDecimal(tempData.get(SpotConstant.DATA_FIELD_ROSE));
                    BigDecimal processed = rose.divide(
                            new BigDecimal(String.valueOf(SpotConstant.PERCENTAGE_MULTIPLIER)),
                            SpotConstant.DECIMAL_SCALE, RoundingMode.DOWN);
                    tempData.put(SpotConstant.DATA_FIELD_ROSE, processed.toPlainString());
                } catch (Exception e) {
                    log.warn("处理 rose 字段失败: {}", tempData.get(SpotConstant.DATA_FIELD_ROSE));
                }
                result.put(key, tempData);
            }
            response.put(SpotConstant.RESPONSE_FIELD_DATA, result);
        } catch (Exception e) {
            log.error("查询多日行情数据失败: channel={}, symbol={}", channel, symbol, e);
            response.put(SpotConstant.RESPONSE_FIELD_DATA, new HashMap<>());
        }
        return response;
    }

    // ------------------------------------------------------------------ helpers

    private List<List<String>> parseDepthList(String json) throws Exception {
        if (json == null || json.isEmpty()) {
            return new ArrayList<>();
        }
        return objectMapper.readValue(json, new TypeReference<List<List<String>>>() {});
    }
}
