package com.market.utils;

import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * 现货 WebSocket 常量类
 *
 * @author system
 * @date 2026-05-07
 */
public final class SpotConstant {

    private SpotConstant() {}

    // ------------------------------------------------------------------ K线周期映射

    /** 币安周期 → Flutter 周期映射（用于 topic 拼接） */
    public static final Map<String, String> KLINE_TO_PERIOD_FLUTTER = new HashMap<String, String>() {{
        put("1m",  "1min");  put("3m",  "3min");  put("5m",  "5min");
        put("15m", "15min"); put("30m", "30min"); put("1h",  "60min");
        put("2h",  "2h");    put("4h",  "4h");    put("6h",  "6h");
        put("8h",  "8h");    put("12h", "12h");   put("1d",  "1day");
        put("3d",  "3day");  put("1w",  "1week"); put("1M",  "1month");
    }};

    /** Flutter 周期 → 数据库周期映射 */
    public static final Map<String, String> FLUTTER_KLINE_TO_DB_PERIOD = new HashMap<String, String>() {{
        put("1min",   "1MIN");  put("3min",  "3MIN");  put("5min",  "5MIN");
        put("15min",  "15MIN"); put("30min", "30MIN"); put("60min", "1HOUR");
        put("2h",     "2HOUR"); put("4h",    "4HOUR"); put("6h",    "6HOUR");
        put("8h",     "8HOUR"); put("12h",   "12HOUR"); put("1day", "1DAY");
        put("3d",     "3DAY");  put("1week", "1WEEK"); put("1month","1MONTH");
    }};

    /** 币安周期 → 数据库周期映射 */
    public static final Map<String, String> KLINE_PERIOD_MAP = new HashMap<String, String>() {{
        put("1m",  "1MIN");  put("3m",  "3MIN");  put("5m",  "5MIN");
        put("15m", "15MIN"); put("30m", "30MIN"); put("1h",  "1HOUR");
        put("2h",  "2HOUR"); put("4h",  "4HOUR"); put("6h",  "6HOUR");
        put("8h",  "8HOUR"); put("12h", "12HOUR"); put("1d", "1DAY");
        put("3d",  "3DAY");  put("1w",  "1WEEK"); put("1M",  "1MONTH");
    }};

    /** 现货订阅 K 线周期（币安格式），与合约保持一致 */
    public static final String[] KLINE_PERIODS = {
        "1m", "5m", "15m", "30m", "1h", "4h", "1d", "1w", "1M"
    };

    // ------------------------------------------------------------------ 分页和限制

    /** K线默认分页大小 */
    public static final int DEFAULT_KLINE_PAGE_SIZE = 150;
    /** 深度数据默认返回条数 */
    public static final int DEFAULT_DEPTH_LIMIT = 150;
    /** 深度聚合每档最大条数 */
    public static final int DEFAULT_DEPTH_STEP_LIMIT = 30;
    /** 成交记录默认条数 */
    public static final int DEFAULT_TRADE_PAGE_SIZE = 30;
    /** 成交记录最大条数 */
    public static final int MAX_TRADE_PAGE_SIZE = 30;

    // ------------------------------------------------------------------ 时间

    /** 毫秒转秒除数 */
    public static final long MILLISECONDS_TO_SECONDS = 1000L;
    /** 心跳超时时间（秒） */
    public static final int HEARTBEAT_TIMEOUT_SECONDS = 120;
    /** 心跳间隔（秒） */
    public static final int HEARTBEAT_INTERVAL_SECONDS = 60;
    /** 队列 poll 超时（毫秒） */
    public static final long QUEUE_POLL_TIMEOUT_MS = 100L;
    /** 成交推送频率控制间隔（毫秒） */
    public static final long TRADE_FREQUENCY_INTERVAL_MS = 300L;
    /** K线落库最小间隔（毫秒） */
    public static final long KLINE_DB_SAVE_INTERVAL_MS = 5000L;
    /** 消息过期时间（毫秒） */
    public static final long MESSAGE_EXPIRE_TIME_MS = 60000L;
    /** 精度配置刷新间隔（毫秒，60分钟） */
    public static final long PRECISION_REFRESH_INTERVAL_MS = 60 * 60 * 1000L;

    // ------------------------------------------------------------------ 数学

    /** 百分比精度 */
    public static final int DECIMAL_SCALE = 5;
    /** 百分比倍数 */
    public static final int PERCENTAGE_MULTIPLIER = 100;
    /** 百分比符号 */
    public static final String PERCENTAGE_SIGN = "%";

    // ------------------------------------------------------------------ 事件类型

    public static final String EVENT_SUB = "sub";
    public static final String EVENT_UNSUB = "unsub";
    public static final String EVENT_SUB_BATCH = "sub_batch";
    public static final String EVENT_UNSUB_BATCH = "unsub_batch";
    public static final String EVENT_REQ = "req";
    public static final String EVENT_REP = "req";
    public static final String EVENT_SUB_REP = "sub";
    public static final String STATUS_OK = "ok";
    public static final String STATUS_ERROR = "error";

    // ------------------------------------------------------------------ 响应字段

    public static final String RESPONSE_FIELD_CHANNEL = "channel";
    public static final String RESPONSE_FIELD_TS = "ts";
    public static final String RESPONSE_FIELD_EVENT_REP = "event_rep";
    public static final String RESPONSE_FIELD_STATUS = "status";
    public static final String RESPONSE_FIELD_EVENT = "event";
    public static final String RESPONSE_FIELD_TICK = "tick";
    public static final String RESPONSE_FIELD_DATA = "data";

    // ------------------------------------------------------------------ 数据类型

    public static final String DATA_TYPE_TICKER = "ticker";
    public static final String DATA_TYPE_KLINE_PREFIX = "kline_";
    public static final String DATA_TYPE_DEPTH = "depth";
    public static final String DATA_TYPE_TRADE = "trade";
    public static final String DATA_TYPE_MARK = "mark";
    public static final int KLINE_PREFIX_LENGTH = 6;

    // ------------------------------------------------------------------ Channel 类型

    public static final String CHANNEL_PREFIX_MARKET = "market_";
    public static final String CHANNEL_TYPE_KLINE = "_kline_";
    public static final String CHANNEL_TYPE_DEPTH = "_depth";
    public static final String CHANNEL_TYPE_TRADE_TICKER = "_trade_ticker";
    public static final String CHANNEL_TYPE_DETAIL_TRADE = "_detailtrade";
    public static final String CHANNEL_TYPE_TICKER = "_ticker";
    public static final int CHANNEL_MIN_PARTS_LENGTH = 4;
    public static final String TOPIC_PREFIX_MARKET_DOT = "market";
    public static final String TOPIC_SEPARATOR_REGEX = "\\.";
    public static final int TOPIC_PARTS_KLINE = 4;
    public static final int TOPIC_PARTS_COMMON = 3;
    public static final int TOPIC_PART_INDEX_PREFIX = 0;
    public static final int TOPIC_PART_INDEX_SYMBOL = 1;
    public static final int TOPIC_PART_INDEX_TYPE = 2;
    public static final int TOPIC_PART_INDEX_INTERVAL = 3;

    /**
     * 现货资产变动 channel（订阅时传 channel=spot_asset，连接时需传 uid 参数）
     */
    public static final String CHANNEL_SPOT_ASSET = "spot_asset";

    /**
     * 订单变动 channel（订阅时传 channel=spot_order，连接时需传 uid 参数）
     */
    public static final String CHANNEL_EXORDER = "spot_order";

    // ------------------------------------------------------------------ 参数字段

    public static final String PARAM_FIELD_TOP = "top";
    public static final String PARAM_FIELD_PAGE_SIZE = "pageSize";
    public static final String PARAM_FIELD_END_IDX = "endIdx";
    public static final String PARAM_FIELD_CHANNEL = "channel";
    public static final String PARAM_FIELD_ACCOUNT_ID = "accountId";
    public static final String PARAM_FIELD_UID = "uid";
    public static final String PARAM_FIELD_CB_ID = "cb_id";
    public static final String PARAM_FIELD_PLATFORM = "platform";
    public static final String PLATFORM_FLUTTER = "flutter";

    // ------------------------------------------------------------------ 数据字段

    public static final String DEFAULT_CB_ID = "1";
    public static final String SPECIAL_SYMBOL_MIFIUSDT = "mifiusdt";
    public static final String DATA_FIELD_ID = "id";
    public static final String DATA_FIELD_DS = "ds";
    public static final String DATA_FIELD_TIME = "time";
    public static final String DATA_FIELD_TS = "ts";
    public static final String DATA_FIELD_OPEN = "open";
    public static final String DATA_FIELD_CLOSE = "close";
    public static final String DATA_FIELD_HIGH = "high";
    public static final String DATA_FIELD_LOW = "low";
    public static final String DATA_FIELD_VOL = "vol";
    public static final String DATA_FIELD_AMOUNT = "amount";
    public static final String DATA_FIELD_ROSE = "rose";
    public static final String DATA_FIELD_SCALE = "scale";
    public static final String DATA_FIELD_HIGH52W = "high52w";
    public static final String DATA_FIELD_LOW52W = "low52w";
    public static final String DATA_FIELD_ASKS = "asks";
    public static final String DATA_FIELD_BUYS = "buys";
    public static final String TICKER_KEY_1Y = "1y";
    public static final String TICKER_KEY_1D = "1d";

    // ------------------------------------------------------------------ Redis key

    public static final String REDIS_DEPTH_ASK_PREFIX = "ExchangeNew2:depth:ask:";
    public static final String REDIS_DEPTH_BID_PREFIX = "ExchangeNew2:depth:bid:";
    public static final String REDIS_TRADE_FREQUENCY_PREFIX = "ExchangeNew2:trade:frequency:";

    // ------------------------------------------------------------------ 心跳

    public static final String PING_MESSAGE_TEMPLATE = "{\"ping\":%d}";
    public static final int SCHEDULED_THREAD_POOL_SIZE = 10;

    // ------------------------------------------------------------------ MQ

    public static final int MQ_MAX_RECONSUME_TIMES = 3;
    public static final String SUBSCRIBE_ACTION = "sub";

    // ------------------------------------------------------------------ 业务枚举值

    public static final String SIDE_BUY = "BUY";
    public static final String SIDE_SELL = "SELL";
    public static final String INTERVAL_1M = "1m";
    public static final String TICKER_KEY_24H = "24hrTicker";
    public static final String FIELD_BINANCE_DATA = "data";
    public static final String FIELD_BINANCE_EVENT = "e";
    public static final String WS_CLOSE_REASON_REFRESH = "refresh";
    public static final String WS_CLOSE_REASON_SHUTDOWN = "shutdown";
    public static final String WS_ERROR_BAD_REQUEST = "bad-request";
    public static final String WS_ERROR_NOT_FOUND = "not-found";
    public static final String MSG_UNKNOWN_ACTION = "Unknown action";
    public static final String MSG_FROM_TO_REQUIRED_PREFIX = "from and to are required for kline: ";
    public static final String MSG_UNSUPPORTED_REQ_PREFIX = "Unsupported req topic: ";
    public static final String MSG_NO_TICKER_PREFIX = "No ticker data available for: ";
    public static final String MSG_FROM_GT_TO = "from must be <= to";

    // ------------------------------------------------------------------ 数据处理参数

    public static final long THROTTLE_TRADE_MS = 500L;
    public static final long THROTTLE_TICKER_MS = 1000L;
    public static final int DEPTH_LEVELS = 20;
    public static final int RANDOM_STEP_BASE = 1;
    public static final int RANDOM_STEP_BOUND = 3;
    public static final int RANDOM_QTY_BASE = 1000;
    public static final int RANDOM_QTY_BOUND = 4001;
    public static final int DEPTH_SCALE_EXTRA = 4;

    // ------------------------------------------------------------------ 精度默认值

    public static final int DEFAULT_PRECISION_DEPTH0 = 2;
    public static final int DEFAULT_PRECISION_DEPTH1 = 4;
    public static final int DEFAULT_PRECISION_DEPTH2 = 6;

    // ------------------------------------------------------------------ 客户端ID

    public static final String CLIENT_ID_PREFIX = "spot_client_";

    // ------------------------------------------------------------------ 协议字段

    /** 旧协议事件字段名 */
    public static final String FIELD_EVENT = "event";
    /** 新协议操作字段名 */
    public static final String FIELD_OP = "op";
    /** 新协议参数数组字段名 */
    public static final String FIELD_ARGS = "args";
    /** 心跳 pong 字段名 */
    public static final String FIELD_PONG = "pong";

    // ------------------------------------------------------------------ 连接参数

    /** queryString 中客户端类型参数名 */
    public static final String PARAM_CLIENT_TYPE = "clientType";
    /** Flutter 客户端类型值 */
    public static final String CLIENT_TYPE_FLUTTER = "flutter";
    /** queryString 中用户 ID 参数名 */
    public static final String PARAM_UID = "uid";

    // ------------------------------------------------------------------ 队列大小

    public static final int REQ_QUEUE_SIZE = 500;
    public static final int BROADCAST_QUEUE_SIZE = 5000;

    // ------------------------------------------------------------------ 表名

    public static final String TABLE_KLINE_PREFIX = "kline_";

    // ------------------------------------------------------------------ K线表存在缓存（进程内，不用 Redis）

    public static final Cache<String, Boolean> KLINE_TABLE_EXISTS_CACHE =
            Caffeine.newBuilder()
                    .expireAfterAccess(1, TimeUnit.HOURS)
                    .maximumSize(10000)
                    .build();
}
