import { MainClient, type Kline as BinanceRestKline } from "binance";

import { logger } from "../utils/logger";
import { BaseRestClient } from './base_rest';
import type { KlineInterval, Kline, MarketInfo } from '../types';

/** K 线周期 → 毫秒数映射（用于时间桶计算） */
export const KLINE_INTERVAL_MS: Record<KlineInterval, number> = {
    "1m": 60_000,
    "5m": 300_000,
    "15m": 900_000,
    "30m": 1_800_000,
    "1h": 3_600_000,
    "4h": 14_400_000,
    "1d": 86_400_000,
    "1w": 604_800_000,
};

// ============================================================
// Binance REST K 线 → 本系统标准化 Kline 转换
// ============================================================

/**
 * Binance SDK Kline 元组格式（getKlines / getUIKlines 返回）：
 *   [0] openTime:         number           — 开盘时间（Unix ms）
 *   [1] open:             numberInString   — 开盘价
 *   [2] high:             numberInString   — 最高价
 *   [3] low:              numberInString   — 最低价
 *   [4] close:            numberInString   — 收盘价
 *   [5] volume:           numberInString   — 成交量（base 币种）
 *   [6] closeTime:        number           — 收盘时间（Unix ms）
 *   [7] quoteVolume:      numberInString   — 成交额（quote 币种）
 *   [8] tradeCount:       number           — 成交笔数
 *   [9] takerBuyBaseVol:  numberInString   — 主动买入成交量
 *   [10] takerBuyQuoteVol: numberInString  — 主动买入成交额
 *   [11] ignore:           numberInString  — 忽略字段
 *
 * numberInString = string | number，通过 Number() 统一转换。
 *
 * 参考：node_modules/binance/lib/types/shared.d.ts:85-98
 */
function convertBinanceKline(
    raw: BinanceRestKline,
    symbol: string,
    interval: KlineInterval,
): Kline {
    const [
        openTime,
        open,
        high,
        low,
        close,
        volume,
        closeTime,
        quoteVolume,
        tradeCount,
        takerBuyBaseVol,
        takerBuyQuoteVol,
        // [11] ignore — 丢弃
    ] = raw;

    return {
        exchange: "binance",
        symbol,
        interval,
        openTime: openTime,
        closeTime: closeTime,
        open: String(open),
        high: String(high),
        low: String(low),
        close: String(close),
        volume: String(volume),
        quoteVolume: String(quoteVolume),
        takerBuyBaseVol: String(takerBuyBaseVol),
        takerBuyQuoteVol: String(takerBuyQuoteVol),
        tradeCount: String(tradeCount),
        isClosed: true, // REST 返回的 K 线均为已闭合历史数据
    };
}

// ============================================================
// Binance REST 拉取函数
// ============================================================

/**
 * 通过 Binance 原生 SDK 拉取 UI K 线并转换为本系统 Kline。
 *
 * getUIKlines 与 getKlines 返回同构的 Kline[] 元组，
 * getUIKlines 额外支持 timeZone 参数，适合按交易所时区对齐。
 *
 * @param symbol    - 交易对（如 BTCUSDT）
 * @param interval  - K 线周期
 * @param startTime - 起始时间（Unix ms）
 * @param endTime   - 结束时间（Unix ms），可选
 * @param limit     - 单次拉取条数，默认 500（最大 1000）
 */
async function fetchBinanceKlines(
    symbol: string,
    interval: KlineInterval,
    startTime: number,
    endTime?: number,
    limit = 500,
): Promise<Kline[]> {
    const client = new MainClient({
        api_key: 'ONSJKIGRpDYLn6FdV17aAKfjclZ4I2LzamflhuMpsoRQA427lLKeyJlGtg2RZ7DH',
        api_secret: '5Mfv4TgvDlRzCHbtl2nJL4mVHUvMm8pyjKiRjMoosBMxrhlqMw6CuQbg2qbS2Npd',
    }, {
        timeout: 3000,
    });

    // Binance 硬限制：单次最多 1000 条
    const safeLimit = Math.min(limit, 1000);

    const rawKlines = await client.getKlines({
        symbol,
        interval,
        startTime,
        endTime,
        limit: safeLimit,
    });

    logger.info({
        symbol,
        interval,
        startTime,
        endTime,
        limit: safeLimit,
    }, 'fetchBinanceKlines arguments');

    if (!rawKlines || rawKlines.length === 0) {
        return [];
    }

    return filterConsecutive(
        rawKlines.map((k, index) => {
            // if (index === rawKlines.length - 1) {
            //     console.log(k);
            // }
            return convertBinanceKline(k, symbol, interval);
        }),
        interval,
    );
}

/**
 * 过滤出严格连续（无时间缺口）的 K 线序列。
 *
 * 处理流程：
 *   1. 按 openTime 升序排序（防御性，确保时间单调递增）
 *   2. 从首条 K 线开始遍历，仅保留相邻间隔恰好等于 intervalMs 的条目
 *   3. 一旦检测到缺口（间隔 ≠ intervalMs），立即终止并丢弃后续所有数据
 *
 * 设计意图：
 *   - 时间序列分析（回测、指标计算）依赖连续数据，缺口会引入偏误
 *   - 缺口之后的数据可能来自另一段不连续的拉取结果，混入后风险更高
 *   - "截断"策略优于"填充/跳过"，避免伪造数据或隐藏数据质量问题
 *
 * @param klines   - 待过滤的 K 线数组（可能乱序、可能含缺口）
 * @param interval - K 线周期，用于查表获取 intervalMs
 * @returns 从首条开始严格连续的最大前缀子序列；空数组无缺口时返回完整排序结果
 */
function filterConsecutive(klines: Kline[], interval: KlineInterval) {
    // 查表获取当前 K 线周期对应的毫秒数
    const intervalMs = KLINE_INTERVAL_MS[interval];

    // 防御性排序：Binance API 不保证返回顺序，升序排列确保时间单调
    const results = klines.sort((a: Kline, b: Kline) => {
        return a.openTime - b.openTime;
    });

    return results;

    // console.log(results);

    // let _openTime = 0;        // 哨兵：0 表示尚未初始化，非 0 表示上一条已收录 K 线的 openTime
    // const rets: Kline[] = [];  // 累积连续 K 线结果

    // for (let item of results) {

    //     console.log(item.openTime);
    //     // 分支 1 —— 首条 K 线：无条件收录，并初始化哨兵
    //     if (_openTime === 0) {
    //         _openTime = item.openTime;
    //         rets.push(item);
    //         continue;
    //     }

    //     // 分支 2 —— 严格连续：当前 openTime 与上一条恰好相差一个周期
    //     if (item.openTime - _openTime === intervalMs) {
    //         _openTime = item.openTime;
    //         rets.push(item);
    //         continue;
    //     }

    //     // 分支 3 —— 检测到缺口：截断，丢弃当前及之后所有 K 线
    //     break;
    // }

    // return rets;
}

// ============================================================
// Client —— 多交易所 REST 客户端
// ============================================================

export class Client extends BaseRestClient {
    exchange: string;

    /**
     * @param exchange - 交易所 ID（如 "binance"、"okx"、"bybit"）
     *                   内部根据 ID 分发到对应的 SDK 实现
     */
    constructor(exchange: string) {
        super();
        this.exchange = exchange;
    }

    /**
     * 拉取历史 K 线数据，返回标准化 Kline 数组。
     *
     * 根据交易所 ID 分发到各自的 SDK 拉取函数。
     *
     * @param symbol    - 交易对符号（如 BTCUSDT）
     * @param interval  - K 线周期
     * @param startTime - 起始时间（Unix ms）
     * @param endTime   - 结束时间（Unix ms），可选
     * @param limit     - 最大返回条数，默认取自 config.defaultLimit
     */
    async fetchKlines(
        symbol: string,
        interval: KlineInterval,
        startTime: number,
        limit?: number,
        endTime?: number,
    ): Promise<Kline[]> {
        const effectiveLimit = limit ?? this.config.defaultLimit;
        switch (this.exchange) {
            case "binance":
                return fetchBinanceKlines(symbol, interval, startTime, endTime, effectiveLimit);
            // TODO: 新增交易所在此添加 case
            // case "okx":
            //     return fetchOkxKlines(symbol, interval, startTime, endTime, effectiveLimit);
            default:
                throw new Error(
                    `[Client] 不支持的交易所: "${this.exchange}"，` +
                    `当前仅支持: binance`,
                );
        }
    }

    async fetchMarkets(): Promise<MarketInfo[]> {
        // TODO: 各交易所实现
        return [];
    }
}
