<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"
      :latestTransactionData="latestTransactionData"
      :orderPlacement="orderPlacement" />
  </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.0,
    high: "0.00",
    low: "0.00",
    vol: "0",
    amount: "0",
  });

  const orderPlacement = ref();
  const latestTransactionData = ref();

  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;
      // console.log(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);
          }
        }
      } else if (rawData.stream == String(route.query.type) + "@depth20@1000ms") {
        orderPlacement.value = rawData.data;
      } else if (rawData.stream == String(route.query.type) + "@aggTrade") {
        latestTransactionData.value = rawData.data;
      }
    } 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;
    margin-bottom: 50px;
    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>
