import { QueryHistoryDatasRsp } from '@/services/go/quote/interface';
import { Dataset, Candlestick, MACD, VOL, KDJ, CCI } from './type'
import moment from 'moment';

export function useDataset() {
    // 行情历史数据中所有补充数据的索引位置(用于指标计算)
    const invalidData: number[] = [];

    // K线数据
    const klineData: Dataset<Candlestick> = {
        dimensions: ['date', 'open', 'close', 'lowest', 'highest', 'ma5', 'ma10', 'ma15'],
        source: [],
    }

    // MACD数据
    const macdData: Dataset<MACD> = {
        dimensions: ['date', 'macd', 'dif', 'dea'],
        source: [],
    }

    // VOL数据
    const volData: Dataset<VOL> = {
        dimensions: ['date', 'vol'],
        source: [],
    }

    // KDJ线数据
    const kdjData: Dataset<KDJ> = {
        dimensions: ['date', 'k', 'd', 'j'],
        source: [],
    }

    // VOL线数据
    const cciData: Dataset<CCI> = {
        dimensions: ['date', 'cci'],
        source: [],
    }

    // 处理行情数据
    const handleData = (rawData: QueryHistoryDatasRsp[], onReady?: () => void) => {
        invalidData.length = 0;
        for (let i = 0; i < rawData.length; i++) {
            const { o, c, h, l, ts, f, tv } = rawData[i];
            const date = moment(ts).format('YYYY-MM-DD HH:mm:ss');

            if (f) invalidData.push(i); // 添加补充数据的索引位置

            klineData.source.push({
                date,
                open: o,
                close: c,
                lowest: l,
                highest: h,
                ma5: '-',
                ma10: '-',
                ma15: '-',
            })

            macdData.source.push({
                date,
                ema12: 0,
                ema26: 0,
                dif: 0,
                dea: 0,
                macd: 0,
            })

            volData.source.push({
                date,
                vol: tv,
            })

            kdjData.source.push({
                date,
                k: '-',
                d: '-',
                j: '-'
            })

            cciData.source.push({
                date,
                cci: '-',
            })
        }
        // 计算各种指标
        calcIndicator();
        onReady && onReady();
    }

    // 计算MA
    const calcMA = (key: keyof Candlestick, count: number, startIndex = 0) => {
        type T = Candlestick[keyof Candlestick];
        const klineSource = klineData.source;

        for (let i = startIndex; i < klineSource.length; i++) {
            // 判断是否补充数据
            if (invalidData.includes(i)) {
                const value = klineSource.length > 0 ? klineSource[i - 1][key].toString() : '-'; // 如果存在，取上一条记录的MA值
                (<T>klineSource[i][key]) = value;
            } else {
                let n = 0;
                const tmpList: Candlestick[] = [];

                for (let j = i; j >= 0; j--) {
                    if (invalidData.includes(j)) continue; // 如果是补充数据，跳过本次循环
                    if (n === count) break; // 如果 n 等于计数，结束循环
                    tmpList.push(klineSource[j]);
                    n++;
                }

                if (n === count) {
                    // 计算总价(收盘价总和)
                    const total = tmpList.reduce((res, e) => res + e.close, 0);
                    // 计算均价
                    (<T>klineSource[i][key]) = (total / count).toFixed(2);
                } else {
                    (<T>klineSource[i][key]) = '-';
                }
            }
        }
    }

    // 计算EMA
    const calcEMA = (key: keyof MACD, count: number, startIndex = 0) => {
        type T = MACD[keyof MACD];
        const macdSource = macdData.source;
        const klineSource = klineData.source;
        const a = 2 / (count + 1); // 平滑系数

        for (let i = startIndex; i < klineSource.length; i++) {
            const close = klineSource[i].close; // 收盘价

            if (i === 0) {
                (<T>macdSource[i][key]) = close; // 初始EMA取收盘价
            } else {
                const prevEMA = macdSource[i - 1][key]; // 昨日EMA
                const value = a * close + (1 - a) * Number(prevEMA);

                (<T>macdSource[i][key]) = value;
            }
        }
    }

    // 计算MACD
    const calcMACD = (startIndex = 0) => {
        // 先计算EMA
        calcEMA('ema12', 12, startIndex); // EMA(12) = 2 ÷ 13 * 今日收盘价(12) + 11 ÷ 13 * 昨日EMA(12)
        calcEMA('ema26', 26, startIndex); // EMA(12) = 2 ÷ 13 * 今日收盘价(12) + 11 ÷ 13 * 昨日EMA(12)

        const macdSource = macdData.source;

        for (let i = startIndex; i < macdSource.length; i++) {
            const { ema12, ema26 } = macdSource[i];
            const prevDEA = i > 0 ? macdSource[i - 1].dea : 0; // 昨日DEA

            const dif = ema12 - ema26; // DIF = 今日EMA(12) - 今日EMA(26)
            const dea = prevDEA * 8 / 10 + dif * 2 / 10; // DEA = 昨日DEA × 8 ÷ 10 + 今日DIF × 2 ÷ 10
            const macd = (dif - dea) * 2; // MACD = (DIFF - DEA) × 2

            macdSource[i].dif = Number(dif.toFixed(2));
            macdSource[i].dea = Number(dea.toFixed(2));
            macdSource[i].macd = Number(macd.toFixed(2));
        }
    }

    // 计算KDJ
    const calcKDJ = (startIndex = 0) => {
        const count = 9; // 以9日周期计算
        const klineSource = klineData.source;
        const kdjSource = kdjData.source;

        for (let i = startIndex; i < klineSource.length; i++) {
            let n = 0;
            const tmpList: Candlestick[] = [];

            for (let j = i; j >= 0; j--) {
                if (n === count) break; // 如果 n 等于计数，结束循环
                tmpList.push(klineSource[j]);
                n++;
            }

            if (n === count) {
                const close = klineSource[i].close, // 收盘价
                    n9 = tmpList.map((item) => item.close),
                    h9 = Math.max(...n9), // 9天内最高价
                    l9 = Math.min(...n9); // 9天内最低价

                const rsv = (close - l9) / (h9 - l9) * 100, // RSV = (Ct-L9) ÷ (H9-L9) * 100
                    prevK = Number(kdjSource[i - 1].k), // 昨日K值
                    prevD = Number(kdjSource[i - 1].d); // 昨日D值

                const kValue = isNaN(prevK) ? 50 : prevK; // 若无昨日K值，则可用50来代替
                const dValue = isNaN(prevD) ? 50 : prevD; // 若无昨日D值，则可用50来代替

                const k = (2 / 3) * kValue + (1 / 3) * rsv, // K = 2 ÷ 3 × 昨日K值 ＋ 1 ÷ 3 × RSV
                    d = (2 / 3) * dValue + (1 / 3) * k, // D = 2 ÷ 3 × 昨日D值 ＋ 1 ÷ 3 × K值
                    j = 3 * k - 2 * d; // J = 3 * K值 - 2 * D值

                kdjSource[i].k = k.toFixed(2);
                kdjSource[i].d = d.toFixed(2);
                kdjSource[i].j = j.toFixed(2);
            }
        }
    }

    // 计算CCI
    const calcCCI = (startIndex = 0) => {
        const count = 14; // 以14日周期计算
        const klineSource = klineData.source;
        const cciSource = cciData.source;

        for (let i = startIndex; i < klineSource.length; i++) {
            let n = 0;
            const tmpList: Candlestick[] = [];

            for (let j = i; j >= 0; j--) {
                if (n === count) break; // 如果 n 等于计数，结束循环
                tmpList.push(klineSource[j]);
                n++;
            }

            if (n === count) {
                const calcTP = (item: Candlestick) => (item.highest + item.lowest + item.close) / 3; // TP = (最高价 + 最低价 + 收盘价) ÷ 3
                const tp = calcTP(klineSource[i]),
                    ma = tmpList.reduce((res, e) => res + calcTP(e), 0) / count, // MA = 近N日TP的累计之和 ÷ N
                    md = tmpList.reduce((res, e) => res + Math.abs(calcTP(e) - ma), 0) / count; // MD = 近N日TP的绝对值的累计之和 ÷ N

                const cci = (tp - ma) / (md * 0.015);
                cciSource[i].cci = cci.toFixed(2);
            }
        }
    }

    // 计算各种指标
    const calcIndicator = (startIndex = 0) => {
        calcMA('ma5', 5, startIndex);
        calcMA('ma10', 10, startIndex);
        calcMA('ma15', 15, startIndex);
        calcMACD(startIndex);
        calcKDJ(startIndex);
        calcCCI(startIndex);
    }

    return {
        klineData,
        macdData,
        volData,
        kdjData,
        cciData,
        handleData,
        calcIndicator,
    }
}