<template>
  <echart-base :options="[options]" :empty="isEmpty" v-model:loading="loading"></echart-base>
</template>

<script lang="ts">
import { defineComponent, ref, watch, PropType, onMounted, computed } from 'vue';
import { QueryTSDataRsp, QueryQuoteDayRsp } from '@/services/go/quote/interface';
import { QueryTSData } from '@/services/go/quote';
import { debounce, getRangeTime } from '@/utils/time';
import { toDecimalFull } from '@/utils/number';
import EchartBase from '../echart-base/index.vue';
import { handleEchart } from './setup';
import moment from 'moment';

export default defineComponent({
    name: 'EchartTime',
    emits: ['change'],
    components: {
        EchartBase,
    },
    props: {
        // 实时行情数据
        quoteData: {
            type: Object as PropType<QueryQuoteDayRsp>,
            required: true,
        },
    },
    setup(props, { emit }) {
        const loading = ref(false);
        const isEmpty = ref(false);
        const historyIndexs: number[] = []; // 行情历史数据中所有非补充数据的索引位置(用于计算均线)
        const { chartData, options, updateOptions, initOptions } = handleEchart();

        // 处理图表数据
        const handleData = (rawData: QueryTSDataRsp): void => {
            historyIndexs.length = 0; // 清空数据
            const datas: number[] = [],
                times: string[] = [],
                xAxisTimes: string[] = [],
                yestclose = rawData.preSettle,
                decimal = rawData.decimalPlace;

            // 历史行情日期
            rawData.historyDatas.forEach((item, index) => {
                const { c, ts } = item;
                datas.push(c);
                times.push(moment(ts).format('YYYY-MM-DD HH:mm:ss'));
                if (!item.f) historyIndexs.push(index);
            });

            // 时间轴(开盘交易时间)
            rawData.runSteps.forEach((item) => {
                const { start, end } = item;
                const rangeTime = getRangeTime(start, end, 'HH:mm', 'm');
                xAxisTimes.push(...rangeTime);
            });

            chartData.value = {
                yestclose,
                decimal,
                rawTime: times,
                ...calcDataLine(datas, yestclose),
                source: {
                    time: xAxisTimes,
                    data: datas,
                    ma5: calcMA(datas, 5, decimal),
                },
            };
        };

        // 计算图表最高低指标线
        const calcDataLine = (datas: number[], yestclose: number) => {
            let max = Math.max(...datas); // 取历史行情最高价
            let min = Math.min(...datas); // 取历史行情最低价

            const last = datas[datas.length - 1], // 历史行情最后收盘价
                a = yestclose - min, // 计算收盘价和最低价的差值
                b = max - yestclose; // 计算收盘价和最高价的差值

            // 比较差值大小
            if (a > b) {
                max = yestclose + a;
                if (last > max) {
                    const c = last - max;
                    max += c;
                    min -= c;
                }
            } else {
                min = min - (b - a);
                if (min > last) {
                    const c = min - last;
                    max += c;
                    min -= c;
                }
            }

            return {
                max: max + max * 0.01,
                min: min - min * 0.01,
            };
        };

        // 计算平均线
        const calcMA = (data: number[], count: number, decimal: number) => {
            const result: string[] = [];
            if (data.length >= count) {
                // 均线起始位置
                const startIndex = historyIndexs[count - 1];
                for (let i = 0; i < data.length; i++) {
                    if (startIndex === undefined || i < startIndex) {
                        result.push('-');
                    } else {
                        const j = historyIndexs.findIndex((val) => val === i);
                        // 判断是否补充数据
                        if (j === -1) {
                            // 取上个平均值
                            result.push(result[result.length - 1]);
                        } else {
                            // 向后取MA数
                            const maIndexs = historyIndexs.slice(j - (count - 1), j + 1);
                            // 计算总价
                            const total = maIndexs.reduce((sum, val) => sum + data[val], 0);
                            // 计算均线
                            const ma = toDecimalFull(total / count, decimal);
                            result.push(ma);
                        }
                    }
                }
            }
            return result;
        };

        // 更新分时数据
        const updateChartData = () => {
            const { source, rawTime } = chartData.value,
                lastIndex = source.data.length - 1, // 历史行情最后索引位置
                lastTime = moment(rawTime[rawTime.length - 1]), // 历史行情最后时间
                newTime = moment(props.quoteData.lasttime), // 实时行情最新时间
                newPrice = props.quoteData.last; // 实时行情最新价

            const cycleMilliseconds = 60 * 1000; // 周期毫秒数

            const diffTime = newTime.valueOf() - lastTime.valueOf(); // 计算时间差
            // 判断时间差是否大于周期时间
            if (diffTime > cycleMilliseconds) {
                lastTime.add(cycleMilliseconds, 'ms');
                rawTime.push(lastTime.format('YYYY-MM-DD HH:mm:ss')); // 添加历史行情时间
                source.data.push(newPrice); // 添加历史行情数据
                historyIndexs.push(lastIndex + 1); // 添加历史行情索引
            } else {
                source.data[lastIndex] = newPrice; // 更新历史行情数据
            }

            source.ma5 = calcMA(source.data, 5, chartData.value.decimal);

            const { min, max } = calcDataLine(source.data, chartData.value.yestclose);
            chartData.value.min = min;
            chartData.value.max = max;

            // 延迟图表更新，减少卡顿
            debounce(() => {
                updateOptions();
            }, 1000);
        };

        onMounted(() => {
            loading.value = true;
            // 查询分时数据
            QueryTSData(props.quoteData.goodscode).then((res) => {
                loading.value = false;
                if (res.historyDatas.length) {
                    isEmpty.value = false;
                    handleData(res);
                    // 调用父级函数查询tik数据 (不合理的逻辑处理，待优化)
                    emit('change', res.startTime, res.endTime);
                } else {
                    isEmpty.value = true;
                }
                initOptions();
            });
        });

        watch(
            () => props.quoteData.last,
            () => {
                if (!loading.value) {
                    updateChartData();
                }
            }
        );

        return {
            loading,
            isEmpty,
            options,
        };
    },
});
</script>