<template>
  <div class="market-conditions">
    <div class="market-price">
      <div class="price-left">
        <div class="left-price pf400 fs14 fc333333">
          实时价格
        </div>
        <div class="left-number pf600 fs20 fc1F2937" :class="getPriceColor(marketInfo.change)">
          {{ formatNumber(marketInfo.price) }}
        </div>
        <div class="left-appro pf500 fs14 fcA8A8A8">
          ≈{{ formatNumber(marketInfo.fiatPrice) }}
          <span class="appro pf500 fs14" :class="getUpDownClass(marketInfo.change)">
            {{ marketInfo.change > 0 ? '+' : '' }}{{ marketInfo.change }}%
          </span>
        </div>
      </div>

      <div class="price-right">
        <div class="right-number-top">
          <div class="right-number-top-price">
            <div class="pf400 fs10 fcA8A8A8">24h 最高价</div>
            <div class="pf400 fs10 fc2C3131">{{ formatNumber(marketInfo.high) }}</div>
          </div>
          <div class="right-number-top-number">
            <div class="pf400 fs10 fcA8A8A8">24h 成交量</div>
            <div class="pf400 fs10 fc2C3131">{{ abbreviateNumber(marketInfo.vol) }}</div>
          </div>
        </div>
        <div class="right-number-bottom">
          <div class="right-number-top-price">
            <div class="pf400 fs10 fcA8A8A8">24h 最低价</div>
            <div class="pf400 fs10 fc2C3131">{{ formatNumber(marketInfo.low) }}</div>
          </div>
          <div class="right-number-top-number">
            <div class="pf400 fs10 fcA8A8A8">24h 成交额</div>
            <div class="pf400 fs10 fc2C3131">{{ abbreviateNumber(marketInfo.amount) }}</div>
          </div>
        </div>
      </div>
    </div>

    <!-- 周期切换 Tab -->
    <nav class="time-tabs">
      <div
        v-for="tab in tabs"
        :key="tab"
        class="tab-item"
        :style="currentTab === tab ? { backgroundColor: '#F6465D', color: '#fff' } : {}"
        @click="switchPeriod(tab)"
      >
        {{ tab }}
      </div>
      <div class="tab-item icon">更多 <span class="triangle">◢</span></div>
      <div class="tab-item icon">
        <img src="@/assets/icon/bitcoin/lishidingdan.svg" alt="">
      </div>
    </nav>

    <div class="k-line-main">
      <KlineChart
        ref="klineRef"
        :data="kLineData"
        height="100%"
        :precision="{ price: getPricePrecision(marketInfo.price), volume: 2 }"
      />
    </div>

    <div class="notifi-classifi">
      <div class="pf600 fs14" :class="current === 'entrustingOrder' ? 'fc121212' : 'fcA8A8A8'" @click="messageChange('entrustingOrder')">委托挂单</div>
      <div class="sys-notifi pf600 fs14" :class="current === 'latestTransactions' ? 'fc121212' : 'fcA8A8A8'" @click="messageChange('latestTransactions')">最新成交</div>
    </div>

    <component :is="currentComponent" :symbol-id="symbolId" />
  </div>
</template>

<script setup>
import { ref, computed, onMounted, onUnmounted, watch, onBeforeUnmount } from "vue";
import { useRoute } from "vue-router";
import { GetCandlestickChart } from "@/api/index.js";
import EntrustingOrder from "./EntrustingOrder.vue";
import LatestTransactions from "./LatestTransactions.vue";
import KlineChart from "@/views/bitcoin/lever/components/KLineChart.vue";

const route = useRoute();
const symbolId = computed(() => route.query.id || '6');

const currentTab = ref('1d');
const tabs = ['1h', '6h', '1d', '1w', '1m'];
const kLineData = ref([]);
const socket = ref(null);
const WS_BASE_URL = 'ws://backend.66linknow.com/ws/kline/';

// --- 生产级配置 ---
const HEARTBEAT_INTERVAL = 15000; // 心跳间隔 15s
const RECONNECT_DELAY = 3000;     // 重连延迟 3s
let heartbeatTimer = null;
let reconnectTimer = null;
let isUnmounted = false;

const marketInfo = ref({
  price: '0.00', fiatPrice: '0.00', change: 0.00, high: '0.00', low: '0.00', vol: '0', amount: '0'
});

const current = ref("entrustingOrder");
const componentsMap = { entrustingOrder: EntrustingOrder, latestTransactions: LatestTransactions };
const currentComponent = computed(() => componentsMap[current.value]);

// --- 1. 切换周期 ---
const switchPeriod = (period) => {
  if (currentTab.value === period) return;

  currentTab.value = period;

  // 清空数据，触发子组件重置，并重新请求
  kLineData.value = [];
  getKlineData();
};

// --- 2. HTTP 获取历史数据 ---
const getKlineData = async () => {
  if (typeof GetCandlestickChart !== 'function') return;

  try {
    const res = await GetCandlestickChart({
      symbol: symbolId.value,
      period: currentTab.value
    });

    let rawList = [];
    if (Array.isArray(res)) rawList = res;
    else if (res && Array.isArray(res.data)) rawList = res.data;

    if (rawList.length > 0) {
      const formattedData = rawList.map(item => ({
        timestamp: Number(item[0]),
        open: parseFloat(item[1]),
        high: parseFloat(item[2]),
        low: parseFloat(item[3]),
        close: parseFloat(item[4]),
        volume: parseFloat(item[5])
      }));
      formattedData.sort((a, b) => a.timestamp - b.timestamp);
      kLineData.value = formattedData;

      // 同步合并：如果 WS 已经有最新价，立即修正历史数据最后一根，防止回跳
      if (marketInfo.value.price !== '0.00') {
        const lastBar = formattedData[formattedData.length - 1];
        const realTimePrice = parseFloat(marketInfo.value.price);
        lastBar.close = realTimePrice;
        // 修正高低
        if (realTimePrice > lastBar.high) lastBar.high = realTimePrice;
        if (realTimePrice < lastBar.low) lastBar.low = realTimePrice;
      }

      updateMarketInfoFromKline(formattedData);
    }
  } catch (error) { console.error("API Error", error); }
};

const updateMarketInfoFromKline = (data) => {
  if (!data.length) return;
  const lastBar = data[data.length - 1];

  // 仅当没数据或 WS 未连接时使用历史数据兜底
  if (marketInfo.value.price === '0.00' || !socket.value || socket.value.readyState !== 1) {
    const firstBar = data[0];
    let maxHigh = -Infinity, minLow = Infinity, totalVol = 0;
    data.forEach(item => {
      if (item.high > maxHigh) maxHigh = item.high;
      if (item.low < minLow) minLow = item.low;
      totalVol += item.volume;
    });
    const changeRate = firstBar.open ? ((lastBar.close - firstBar.open) / firstBar.open) * 100 : 0;

    marketInfo.value = {
      price: lastBar.close,
      change: changeRate.toFixed(2),
      high: maxHigh, low: minLow, vol: totalVol, amount: totalVol * lastBar.close,
      fiatPrice: lastBar.close
    };
  }
};

// --- 3. WS 连接 (含心跳设计) ---
const connectWebSocket = () => {
  // 清理旧资源
  closeWebSocket();

  const symbolStr = String(route.query.type || 'btcusdt').toLowerCase();
  const url = `${WS_BASE_URL}?symbol=${symbolStr}`;

  console.log('WS 连接:', url);

  try {
    socket.value = new WebSocket(url);

    socket.value.onopen = () => {
      console.log('✅ WS Connected');
      startHeartbeat(); // 启动心跳
    };

    socket.value.onmessage = (event) => {
      handleSocketMessage(event.data);
    };

    socket.value.onclose = () => {
      stopHeartbeat(); // 停止心跳
      if (!isUnmounted) {
        console.log('⚠️ WS Closed, reconnecting...');
        clearTimeout(reconnectTimer);
        reconnectTimer = setTimeout(() => connectWebSocket(), RECONNECT_DELAY);
      }
    };

    socket.value.onerror = (err) => {
      // onerror 通常会触发 onclose，由 onclose 处理重连
      console.error('❌ WS Error', err);
    };
  } catch (e) {
    if (!isUnmounted) {
      reconnectTimer = setTimeout(() => connectWebSocket(), RECONNECT_DELAY);
    }
  }
};

const closeWebSocket = () => {
  if (socket.value) {
    socket.value.close();
    socket.value = null;
  }
  stopHeartbeat();
  clearTimeout(reconnectTimer);
};

// --- 心跳逻辑 ---
const startHeartbeat = () => {
  stopHeartbeat();
  heartbeatTimer = setInterval(() => {
    if (socket.value && socket.value.readyState === WebSocket.OPEN) {
      // 发送 ping，具体格式看后端要求，一般是字符串 'ping' 或 JSON
      socket.value.send("ping");
    }
  }, HEARTBEAT_INTERVAL);
};

const stopHeartbeat = () => {
  if (heartbeatTimer) {
    clearInterval(heartbeatTimer);
    heartbeatTimer = null;
  }
};

// --- 辅助：获取周期对应的毫秒数 ---
const getPeriodMs = (period) => {
  const map = {
    '1m': 60 * 1000,
    '5m': 5 * 60 * 1000,
    '15m': 15 * 60 * 1000,
    '30m': 30 * 60 * 1000,
    '1h': 60 * 60 * 1000,
    '4h': 4 * 60 * 60 * 1000,
    '6h': 6 * 60 * 60 * 1000,
    '1d': 24 * 60 * 60 * 1000,
    '1w': 7 * 24 * 60 * 60 * 1000,
    '1M': 30 * 24 * 60 * 60 * 1000
  };
  return map[period] || 60 * 60 * 1000;
}

// --- 4. 核心：处理实时消息 (24hrTicker) ---
const handleSocketMessage = (msgStr) => {
  try {
    // 忽略心跳响应
    if (msgStr === 'pong') return;

    const rawData = JSON.parse(msgStr);
    const msg = rawData.data || rawData;

    if (msg.e === "24hrTicker") {
      marketInfo.value = {
        price: msg.c, change: parseFloat(msg.P), high: msg.h, low: msg.l, vol: msg.v, amount: msg.q, fiatPrice: msg.c
      };

      if (kLineData.value.length > 0) {
        const lastIndex = kLineData.value.length - 1;
        const lastBar = kLineData.value[lastIndex];
        const newPrice = parseFloat(msg.c);
        const currentTime = Number(msg.E); // 事件时间
        const periodMs = getPeriodMs(currentTab.value);

        // 标准时间戳对齐算法: (当前时间 / 周期) * 周期
        const currentBarStart = Math.floor(currentTime / periodMs) * periodMs;

        // 如果计算出的起始时间 > 最后一根的起始时间，说明跨周期了，生成新 K 线
        if (currentBarStart > lastBar.timestamp) {
          const newBar = {
            timestamp: currentBarStart,
            open: newPrice,
            high: newPrice,
            low: newPrice,
            close: newPrice,
            volume: 0
          };
          // 扩展运算符触发更新
          kLineData.value = [...kLineData.value, newBar];

        } else {
          // 还在当前周期内，更新最后一根
          const updatedBar = {
            ...lastBar,
            close: newPrice,
            high: Math.max(lastBar.high, newPrice),
            low: Math.min(lastBar.low, newPrice)
          };
          kLineData.value.splice(lastIndex, 1, updatedBar);
        }
      }
    }
  } catch (e) {}
};

// --- 页面可见性监听 (切屏回来自动重连) ---
const handleVisibilityChange = () => {
  if (document.visibilityState === 'visible') {
    if (!socket.value || socket.value.readyState !== WebSocket.OPEN) {
      console.log('👀 Page Visible, reconnecting...');
      connectWebSocket();
    }
  }
};

onMounted(() => {
  isUnmounted = false;
  getKlineData();
  connectWebSocket();
  document.addEventListener('visibilitychange', handleVisibilityChange);
});

onBeforeUnmount(() => {
  isUnmounted = true;
  closeWebSocket();
  document.removeEventListener('visibilitychange', handleVisibilityChange);
});

watch(symbolId, () => {
  kLineData.value = [];
  getKlineData();
  connectWebSocket();
}, { immediate: false });

const messageChange = (key) => { current.value = key; };
const formatNumber = (num) => {
  if (!num) return '0.00';
  const n = Number(num);
  return n.toLocaleString('en-US', { minimumFractionDigits: 2, maximumFractionDigits: 6 });
};
const abbreviateNumber = (value) => {
  if (!value) return '0.00';
  let num = parseFloat(value);
  if (isNaN(num)) return '0.00';
  if (num >= 1e9) return (num / 1e9).toFixed(2) + 'B';
  if (num >= 1e6) return (num / 1e6).toFixed(2) + 'M';
  if (num >= 1e3) return (num / 1e3).toFixed(2) + 'K';
  return num.toFixed(2);
};
const getPricePrecision = (price) => price < 1 ? 6 : (price < 10 ? 4 : 2);
const getPriceColor = (change) => change >= 0 ? 'fc45B26B' : 'fcF6465D';
const getUpDownClass = (change) => change >= 0 ? 'fc45B26B' : 'fcF6465D';
</script>

<style lang="less" scoped>
.fc45B26B { color: #2EBD85 !important; }
.fcF6465D { color: #F6465D !important; }
.fc1F2937 { color: #1F2937; }

/* 保持之前的样式布局 */
.time-tabs {
  display: flex;
  align-items: center;
  justify-content: space-between;
  width: 100%;
  box-sizing: border-box;
  padding-top: 10px;
  padding-bottom: 0px;
  padding-left: 15px;
  padding-right: 15px;
}

.tab-item {
  font-size: 14px;
  color: #929AA5;
  padding: 4px 10px;
  border-radius: 6px;
  cursor: pointer;
  font-weight: 500;
  transition: all 0.2s;
  display: flex;
  align-items: center;
  justify-content: center;
}

.tab-item.icon {
  color: #929AA5;
  font-size: 12px;
  padding: 4px 4px;
}

.triangle {
  font-size: 8px;
  margin-left: 2px;
  transform: scale(0.9);
}

.tab-item img {
  display: block;
  height: 16px;
  width: auto;
}

.market-conditions {
  display: flex;
  flex-direction: column;
  justify-content: flex-start;
  align-items: center;
  width: 100%;

  .market-price {
    display: flex;
    flex-direction: row;
    justify-content: space-between;
    margin-top: 8px;
    width: 100%;
    height: 73px;
    padding: 0 15px;
    box-sizing: border-box;

    .price-left {
      display: flex;
      flex-direction: column;
      justify-content: flex-start;
      width: 144px;
      height: 69px;

      .left-price {
        display: flex;
        flex-direction: row;
        justify-content: flex-start;
        align-items: center;
        height: 18px;

        img {
          margin-left: 5px;
          width: 8px;
          height: 4px;
        }
      }

      .left-number {
        margin-top: 5px;
      }

      .left-appro {
        display: flex;
        flex-direction: row;
        justify-content: flex-start;
        align-items: center;
        margin-top: 3px;

        .appro {
          margin-left: 9px;
        }
      }
    }

    .price-right {
      display: flex;
      flex-direction: column;
      justify-content: flex-start;
      height: 100%;

      .right-number-top, .right-number-bottom {
        display: flex;
        flex-direction: row;
        justify-content: flex-end;
        width: 100%;
        height: 32px;

        .right-number-top-price, .right-number-top-number {
           margin-left: 10px;
           text-align: right;
            div {
              height: 16px;
              line-height: 16px;
              text-align: end;
            }
        }
      }
      .right-number-bottom {
        margin-top: 9px;
      }
    }
  }

  .k-line-main {
    height: 50vh;
    min-height: 350px;
    width: 100%;
    padding: 0 15px;
  }

  .notifi-classifi {
    display: flex;
    flex-direction: row;
    justify-content: flex-start;
    align-items: flex-end;
    margin-top: 15px;
    width: 100%;
    padding: 0 15px;
    box-sizing: border-box;
    height: 24px;

    .sys-notifi {
      margin-left: 47px;
    }
  }
}
</style>