// ============================================================
// base.ts — REST 客户端抽象基类
// ============================================================
// 各交易所适配器继承此类，复用 REST 请求限流、重试等通用逻辑。
// 子类通过注入各交易所原生 SDK（binance / okx / bybit ...）
// 实现具体的数据拉取，无需依赖 ccxt。
//
// 子类只需实现：
//   - fetchKlines()    — 历史 K 线拉取（基于目标交易所 SDK）
//   - fetchMarkets()   — 交易对元数据拉取（用于自动注册交易对）
//
// WebSocket 实时行情由各适配器自行管理，不在此基类范围内。
// ============================================================

import { logger } from "../utils/logger";
import type { Kline, MarketInfo, FetchKlinesParams, RestClientConfig } from "../types";
import { DEFAULT_REST_CONFIG } from "../types/base";

// ============================================================
// 工具：异步 sleep
// ============================================================

/** 返回一个在 ms 毫秒后 resolve 的 Promise */
function sleep(ms: number): Promise<void> {
    return new Promise((resolve) => setTimeout(resolve, ms));
}

// ============================================================
// BaseRestClient
// ============================================================

export abstract class BaseRestClient {
    /** 交易所标识（子类必须覆盖） */
    abstract readonly exchange: string;

    /** REST 客户端配置（可在子类构造函数中覆盖默认值） */
    protected readonly config: RestClientConfig;

    /** REST 请求节流 Map（key → 上次请求时间戳） */
    private lastRestFetch = new Map<string, number>();

    // ============================================================
    // 构造函数
    // ============================================================

    constructor(config: Partial<RestClientConfig> = {}) {
        this.config = { ...DEFAULT_REST_CONFIG, ...config };
    }

    // ============================================================
    // 限流工具
    // ============================================================

    /**
     * 对同一 key 的 REST 请求进行冷却节流。
     * 若距离上次请求不足 restRateLimitMs，自动等待剩余时间。
     *
     * @param key - 节流标识（如 `${symbol}:${interval}`）
     */
    protected async throttle(key: string): Promise<void> {
        const lastFetch = this.lastRestFetch.get(key) ?? 0;
        const elapsed = Date.now() - lastFetch;
        if (elapsed < this.config.restRateLimitMs) {
            const waitMs = this.config.restRateLimitMs - elapsed;
            logger.debug(
                { exchange: this.exchange, key, waitMs },
                `[${this.exchange}] REST 限流等待 ${waitMs}ms`,
            );
            await sleep(waitMs);
        }
        this.lastRestFetch.set(key, Date.now());
    }

    // ============================================================
    // 抽象方法 —— 子类必须使用各自的交易所 SDK 实现
    // ============================================================

    /**
     * 拉取 1m 历史 K 线数据（REST）。
     *
     * 子类负责：
     *   1. 调用交易所原生 SDK 的 K 线接口
     *   2. 将原始数据转换为本系统标准化 Kline 结构
     *   3. 处理分页逻辑（若时间跨度超过单次请求上限）
     *
     * @param params - fetchKlines 统一参数对象
     */
    abstract fetchKlines(params: FetchKlinesParams): Promise<Kline[]>;

    /**
     * 获取交易所交易对信息（REST）。
     *
     * 子类负责调用交易所原生 SDK 的 /exchangeInfo 等价接口，
     * 并转换为本系统标准化 MarketInfo 结构。
     */
    abstract fetchMarkets(): Promise<MarketInfo[]>;
}

export default BaseRestClient;
