import { onMounted, ref, computed, watch, markRaw, inject } from "vue";
import { getDashboardDevice } from "@/apis/dashboard";
import useSearchParams from "@/hooks/useSearchParam";
import useSystemHeatmap from "./useSystemHeatmap";

export default function useSystemStatusByBaja(updateHeatBarIsShow) {
  const rawData = ref([]);
  const forgeViewer = ref(null);
  const urn = ref("");
  const lightColorMap = {
    1: "#009100",
    2: "#c9bf00",
    4: "#ff1212",
  };
  const { searchParams } = useSearchParams();

  // DataVisualization 擴充套件的全域變數
  let dataVizExtn = null;
  let spriteAnimations = new Map(); // 用 Map 來追蹤多個動畫 {dbId: {interval, arrow, viewable}}
  let cameraEventAdded = false; // 追蹤是否已經添加了相機事件監聽器

  const initialData = ref(null);

  const updateInitialData = (data = false) => {
    initialData.value = data;
  };

  const { updateHeatMapData, updateTemp, initHeatMap } =
    useSystemHeatmap(updateHeatBarIsShow);

  const updateForgeViewer = (viewer) => {
    if (!viewer) {
      forgeViewer.value = null;
      return;
    }
    forgeViewer.value = markRaw(viewer);
    initHeatMap(viewer);
  };

  const getSubPoint = (normal, close, error, sub_points = []) => {
    let points = {
      ...Object.fromEntries(sub_points.map((p) => [p, ""])),
    };
    if (normal) points[normal] = "";
    if (close) points[close] = "";
    if (error) points[error] = "";
    return points;
  };
  const subscribeData = ref({});

  watch(rawData, () => {
    let sub_data = {};

    rawData.value.forEach((d) => {
      sub_data = {
        ...sub_data,
        ...Object.fromEntries(
          (d.device || []).map((dev) => [
            dev.device_number,
            {
              ...dev,
              labelText: d.labelText,
              show_value: d.labelText,
              device_normal_point_name: d.device_normal_point_name,
              device_close_point_name: d.device_close_point_name,
              device_error_point_name: d.device_error_point_name,
              device_normal_point_value: d.device_normal_point_value,
              device_close_point_value: d.device_close_point_value,
              device_error_point_value: d.device_error_point_value,
              device_normal_color: d.device_normal_color,
              device_close_color: d.device_close_color,
              device_error_color: d.device_error_color,
              forge_dbid: parseInt(dev.forge_dbid),
              device_coordinate_3d: dev.device_coordinate_3d
                ? JSON.parse(dev.device_coordinate_3d)
                : { x: 0, y: 0 },
              points: getSubPoint(
                d.device_normal_point_name,
                d.device_close_point_name,
                d.device_error_point_name,
                d.points || []
              ),
              subSys: d.subSys,
              is_show: true,
              currentColor: d.device_normal_color,
            },
          ])
        ),
      };
    });
    subscribeData.value = sub_data;
    updateHeatMapData(sub_data);
    // updateSubscribeDataFromBaja(sub_data);
  });

  const visibleDbid = computed(() => {
    let visible = [];

    rawData.value.forEach((d) => {
      visible = [
        ...visible,
        ...d.device.map((dev) => parseInt(dev.forge_dbid)),
      ];
    });

    // return visible;
    return [
      26, 32, 38, 43, 48, 53, 58, 63, 70, 76, 879, 1068, 1011, 1065, 855, 1109,
      867, 963, 1044, 966, 1139, 936, 1136, 957, 1133, 954, 610, 1130, 951,
      1041, 939, 1145, 987, 999, 1148, 1002, 1151, 981, 813, 1088, 825, 1091,
      804,
    ];
  });

  const getDevice = async (option = 1) => {
    const res = await getDashboardDevice({
      option: parseInt(option),
    });
    rawData.value = res.data.map((d) => ({
      ...d,
      key: d.subSys,
    }));
  };

  // subscribe from baja
  const booleanPointFacets = ref({});
  const updateFacets = (point, facets) => {
    booleanPointFacets.value = {
      ...booleanPointFacets,
      [point]: facets,
    };
  };
  const updateDeviceData = (device_number, point, value) => {
    // 檢查 subscribeData.value[device_number] 是否存在
    if (!subscribeData.value[device_number]) {
      console.log(`Device ${device_number} is not initialized.`);
      return;
    }

    const correspondPoint = initialData.value.points.find(
      ({ name }) => name === point
    );
    // console.log("sub 回傳值 ", typeof value)
    const text = correspondPoint
      ? correspondPoint.values.find(
          ({ value: pValue }) => pValue === parseInt(value)
        )?.text || ""
      : value;

    // console.log("baja update data sub", correspondPoint, device_number, point, value);
    subscribeData.value[device_number].points[point] = text;
    if (
      point.toLowerCase() === "temp" &&
      parseInt(searchParams.value.option) > 1
    ) {
      updateTemp(device_number, value);
    }
    // 當點位是 "light"，且目前顏色不是錯誤顏色時，從 lightColorMap 取顏色
    if (
      point.toLowerCase() === "light" &&
      subscribeData.value[device_number].currentColor !==
        subscribeData.value[device_number].device_error_color
    ) {
      subscribeData.value[device_number].currentColor =
        lightColorMap[Number(value)] ||
        subscribeData.value[device_number].device_normal_color;
    }

    // 當點位是錯誤點時，判斷值是否等於錯誤值，決定是否使用錯誤顏色
    if (point === subscribeData.value[device_number].device_error_point_name) {
      subscribeData.value[device_number].currentColor =
        value === subscribeData.value[device_number].device_error_point_value
          ? subscribeData.value[device_number].device_error_color
          : subscribeData.value[device_number].device_normal_color;
    }

    updateLabelText(device_number, point, text);
  };

  const transformDeviceNumber = (device_number) => {
    const transformed = device_number.replaceAll("_", "/");
    // 找到最後一個 / 的位置，並截取到該位置之前
    const lastSlashIndex = transformed.lastIndexOf("/");
    return lastSlashIndex !== -1
      ? transformed.substring(0, lastSlashIndex)
      : transformed;
  };

  const updateLabelText = (key, point, value) => {
    let text = subscribeData.value[key].labelText.replace(`%${point}`, value);
    Object.keys(subscribeData.value[key].points)
      .filter((p) => p !== point)
      .forEach((p) => {
        text = text.replace(`%${p}`, subscribeData.value[key].points[p]);
      });
    subscribeData.value[key].show_value = text;
  };

  const subComponents = ref(null);
  const updateSubscribeDataFromBaja = (data) => {
    for (let [key, value] of Object.entries(data)) {
      window.require &&
        window.requirejs(["baja!"], (baja) => {
          console.log("進入 bajaSubscriber 準備執行BQL訂閱");

          const ordKey = key;
          const ordPath = transformDeviceNumber(key);
          console.log("ordPath", ordPath, "ordKey", ordKey, value);
          const fullOrdPath = `local:|foxs:|station:|slot:/Drivers/NiagaraNetwork/PCCV/points/${ordPath}/${ordKey}`; // 完整路徑
          console.log("嘗試訪問路徑:", fullOrdPath); // 打印完整路徑

          baja.Ord.make(fullOrdPath)
            .get()
            .then((folder) => {
              console.log("成功獲取 folder:", folder);
              const batch = new baja.comm.Batch();
              const sub = new baja.Subscriber();
              sub.attach({
                changed: function (prop, cx) {
                  console.log("數據變更觸發:", prop.$getDisplayName());
                  if (prop.$getDisplayName() !== "Out") return;
                  if (
                    Object.hasOwn(
                      booleanPointFacets.value,
                      prop.$complex.$propInParent.$slotName
                    )
                  ) {
                    const facets =
                      booleanPointFacets.value[
                        prop.$complex.$propInParent.$slotName
                      ];

                    for (let [facetKey, facetValue] of Object.entries(facets)) {
                      if (facetValue === prop.$getValue().getValueDisplay()) {
                        updateDeviceData(
                          key,
                          prop.$complex.$propInParent.$slotName,
                          facetKey
                        );
                      }
                    }
                  } else {
                    updateDeviceData(
                      key,
                      prop.$complex.$propInParent.$slotName,
                      prop.$getValue().getValueDisplay()
                    );
                  }
                },
              });
              console.log("開始遍歷控制點");
              folder
                .getSlots()
                .is("control:ControlPoint")
                .eachValue((point) => {
                  console.log("找到控制點:", point.getDisplayName());
                  console.log("配置的點位:", Object.keys(value.points));

                  if (
                    Object.keys(value.points).includes(point.getDisplayName())
                  ) {
                    console.log(
                      "匹配到點位，開始訂閱:",
                      point.getDisplayName()
                    );
                    baja.Ord.make(
                      `local:|foxs:|station:|slot:/Drivers/NiagaraNetwork/PCCV/points/${ordPath}/${ordKey}/${point.getDisplayName()}`
                    )
                      .get()
                      .then((component) => {
                        console.log("獲取到 component:", component);
                        if (
                          point.getType().getTypeSpec() ===
                          "control:BooleanWritable"
                        ) {
                          const facets = component.getFacets1().toObject();
                          updateFacets(point.getDisplayName(), facets);
                          for (let [facetKey, facetValue] of Object.entries(
                            facets
                          )) {
                            if (
                              facetValue ===
                              component.getOut().getValue().toString()
                            ) {
                              updateDeviceData(
                                key,
                                point.getDisplayName(),
                                facetKey
                              );
                            }
                          }
                        } else {
                          updateDeviceData(
                            key,
                            point.getDisplayName(),
                            component.getOut().getValue()
                          );
                        }

                        sub
                          .subscribe({
                            comps: component, // Can also just be an singular Component instance
                            batch, // if defined, any network calls will be batched into this object (optional)
                          })
                          .then(() => {
                            console.log("subscribed successfully");
                            subComponents.value = sub;
                          })
                          .catch(function (err) {
                            baja.error(
                              "some components failed to subscribe: " + err
                            );
                          });
                      });
                  }
                });
            });
        });
    }
  };

  const updateDbidPosition = (viewer, data) => {
    if (!viewer) return;
    if (!forgeViewer.value) forgeViewer.value = markRaw(viewer);
    const tree = viewer.model.getData().instanceTree;
    const fragList = viewer.model.getFragmentList();
    for (let [key, value] of Object.entries(data)) {
      const nodebBox = new window.THREE.Box3();

      // for each fragId on the list, get the bounding box
      tree.enumNodeFragments(
        value.forge_dbid,
        (fragId) => {
          const fragbBox = new window.THREE.Box3();
          fragList.getWorldBounds(fragId, fragbBox);
          nodebBox.union(fragbBox); // create a unifed bounding box
        },
        true
      );
      subscribeData.value[key].device_coordinate_3d = viewer.worldToClient(
        nodebBox.getCenter()
      );
      subscribeData.value[key].is_show = viewer.isNodeVisible(value.forge_dbid);
    }
  };

  const fitToView = () => {
    const { x, y, z } = JSON.parse(searchParams.value.camera_position);
    const newPosition = new THREE.Vector3(x, y, z); //!<<< 相机的新位置

    const {
      x: x1,
      y: y1,
      z: z1,
    } = JSON.parse(searchParams.value.target_position); //!<<< 计算新焦点位置
    const newTarget = new THREE.Vector3(x1, y1, z1); //!<<< 焦點的新位置

    forgeViewer.value.navigation.getCamera().setView({
      position: newPosition.clone(),
      target: newTarget.clone(),
    });
    setTimeout(() => {
      updateDbidPosition(forgeViewer.value, subscribeData.value);
    }, 700);
  };

  const hideAllObjects = (instanceTree, filDbids = []) => {
    if (!forgeViewer.value || !instanceTree) return;
    const allDbIds = Object.keys(instanceTree.nodeAccess.dbIdToIndex).map(
      Number
    );

    forgeViewer.value.hide(allDbIds);
    if (filDbids.length > 0) {
      forgeViewer.value.show(filDbids);
    }
    fitToView();
    forgeViewer.value.impl.invalidate(true);
  };

  const loadModel = (viewer, filePath) => {
    return new Promise(function (resolve, reject) {
      // 使用 loadModel 直接加載本地 .svf 文件
      viewer.loadModel(
        filePath,
        {},
        (model) => {
          viewer.setGroundShadow(false);
          viewer.setLightPreset(18);

          viewer.impl.renderer().setClearAlpha(0); // 清除 alpha 通道
          viewer.impl.glrenderer().setClearColor(0xffffff, 0); // 設置透明背景，顏色碼無關緊要
          viewer.impl.invalidate(true); // 觸發渲染
          resolve(model);
        },
        (error) => {
          console.error("模型加載失敗: ", error);
          reject(error);
        }
      );
    });
  };

  const reloadModal = () => {};

  let themingInterval = null;
  let blinkingInterval = null; // 計時器 ID 的引用

  // 增強版：停止閃爍並徹底清理
  const stopBlinking = () => {
    console.log("正在停止基於 Selection 的閃爍效果...");
    if (blinkingInterval) {
      clearInterval(blinkingInterval);
      blinkingInterval = null;
    }
    if (forgeViewer.value) {
      // 核心清理：取消所有選取
      forgeViewer.value.clearSelection();
    }
  };

  const startBlinking = (dbidToBlink) => {
    console.log(`正在為 dbId: ${dbidToBlink} 啟動基於 Selection 的閃爍...`);
    if (!forgeViewer.value) return;

    // 步驟 1: 設定一次我們希望的閃爍顏色和樣式
    const blinkColor = new THREE.Color(0x00aaff); // 水藍色
    forgeViewer.value.setSelectionColor(
      blinkColor,
      Autodesk.Viewing.SelectionType.OVERLAYED
    );

    let isSelected = false; // 用一個旗標來追蹤目前的選取狀態

    blinkingInterval = setInterval(() => {
      // 在計時器內部也要檢查 viewer 是否還存在
      if (!forgeViewer.value) {
        stopBlinking();
        return;
      }

      if (isSelected) {
        // 如果已選取，就取消選取 (閃爍 OFF)
        forgeViewer.value.clearSelection();
      } else {
        // 如果未選取，就選取目標物件 (閃爍 ON)
        // 注意：select 方法需要傳入 model 物件
        forgeViewer.value.select(
          dbidToBlink,
          forgeViewer.value.model,
          Autodesk.Viewing.SelectionType.OVERLAYED
        );
      }

      // 切換狀態，為下一次循環做準備
      isSelected = !isSelected;
    }, 700); // 我們可以稍微調整一下頻率，例如 700 毫秒
  };

  watch([forgeViewer, visibleDbid], ([viewer, dbids]) => {
    console.log("監聽到 forgeViewer 或 visibleDbid 的變化", viewer, dbids);
    stopBlinking();
    clearSprites(); // 清理之前的 Sprites

    if (viewer) {
      hideAllObjects(viewer.model.getData().instanceTree, dbids);
    }

    // 判斷新狀態是否需要閃爍和 Sprites
    if (viewer && dbids.includes(879)) {
      // startBlinking([879, 1068, 1011, 1065, 855, 1109, 867,963,1044,966,1139,936,1136,957,1133,954,610,1130,951,1041,939,1145,987,999,1148,1002,1151,981,813,1088,825,1091,804]);
      const spriteConfigs = [
        { dbId: 879, reverse: false },
        { dbId: 1011, reverse: false },
        { dbId: 855, reverse: true },
        { dbId: 867, reverse: true },
        { dbId: 963, reverse: false },
        { dbId: 966, reverse: true },
        { dbId: 936, reverse: true },
        { dbId: 957, reverse: true },
        { dbId: 954, reverse: true },
        { dbId: 951, reverse: true },
        { dbId: 939, reverse: true },
        { dbId: 987, reverse: true },
        { dbId: 999, reverse: true },
        { dbId: 1002, reverse: true },
        { dbId: 981, reverse: true },
        { dbId: 813, reverse: true },
        { dbId: 825, reverse: false },
        { dbId: 804, reverse: false },
      ];
      spriteConfigs.forEach(({ dbId, reverse }) => {
        createSprites(viewer, dbId, reverse);
      });
    }
  });

  watch(initialData, (newValue) => {
    if (newValue) {
      getDevice(searchParams.value.option);
    }
  });

  watch(
    searchParams,
    (newValue) => {
      getDevice(newValue.option);
    },
    {
      deep: true,
    }
  );

  // 動態Sprites創建函數
  const createSprites = async (viewer, dbId, reverse = false) => {
    try {
      // 先清理這個 dbId 的舊 Sprite（如果存在）
      clearSingleSprite(dbId);

      // 1. 載入 DataVisualization 擴充套件
      if (!dataVizExtn) {
        dataVizExtn = await viewer.loadExtension("Autodesk.DataVisualization");
      }

      // 2. 建立 ViewableStyle
      const DataVizCore = Autodesk.DataVisualization.Core;
      const viewableType = DataVizCore.ViewableType.SPRITE;
      const spriteColor = new THREE.Color(0x00ffff);
      const spriteIconUrl = "spot.svg";
      const style = new DataVizCore.ViewableStyle(
        viewableType,
        spriteColor,
        spriteIconUrl
      );
      const viewableData = new DataVizCore.ViewableData();
      viewableData.spriteSize = 12;

      // 3. 取得 dbId 的 3D 位置
      viewer.getObjectTree(function (instanceTree) {
        const fragList = viewer.model.getFragmentList();
        let fragIds = [];
        instanceTree.enumNodeFragments(dbId, function (fragId) {
          fragIds.push(fragId);
        });

        if (fragIds.length > 0) {
          let box = new THREE.Box3();
          fragIds.forEach((fragId) => {
            let fragBox = new THREE.Box3();
            fragList.getWorldBounds(fragId, fragBox);
            box.union(fragBox);
          });

          // 算出主軸方向
          const size = new THREE.Vector3();
          box.getSize(size);
          // 找出最長的軸
          let axis = "x";
          if (size.y >= size.x && size.y >= size.z) axis = "y";
          if (size.z >= size.x && size.z >= size.y) axis = "z";

          // 取得所有 fragment 的中心點
          let centers = fragIds.map((fragId) => {
            let fragBox = new THREE.Box3();
            fragList.getWorldBounds(fragId, fragBox);
            return fragBox.getCenter(new THREE.Vector3());
          });

          // 找出距離最遠的兩點作為動畫路徑
          let maxDist = 0;
          let animStart = centers[0],
            animEnd = centers[0];
          for (let i = 0; i < centers.length; i++) {
            for (let j = i + 1; j < centers.length; j++) {
              let dist = centers[i].distanceTo(centers[j]);
              if (dist > maxDist) {
                maxDist = dist;
                animStart = centers[i];
                animEnd = centers[j];
              }
            }
          }

          // fallback: 若只有一個 fragment 或所有中心點重疊，改用主軸方向的端點
          if (centers.length < 2 || maxDist === 0) {
            const boxCenter = box.getCenter(new THREE.Vector3());
            const halfSize = size.clone().multiplyScalar(0.5);

            animStart = boxCenter.clone();
            animEnd = boxCenter.clone();

            // 根據主軸設定起點和終點
            if (axis === "x") {
              animStart.x -= halfSize.x;
              animEnd.x += halfSize.x;
            } else if (axis === "y") {
              animStart.y -= halfSize.y;
              animEnd.y += halfSize.y;
            } else {
              // z軸
              animStart.z -= halfSize.z;
              animEnd.z += halfSize.z;
            }
          }

          // === 依主軸方向排序，確保動畫方向一致 ===
          // 根據主軸來排序起點和終點，確保動畫方向一致
          if (axis === "x" && animStart.x > animEnd.x) {
            const tmp = animStart;
            animStart = animEnd;
            animEnd = tmp;
          }
          if (axis === "y" && animStart.y > animEnd.y) {
            const tmp = animStart;
            animStart = animEnd;
            animEnd = tmp;
          }
          if (axis === "z" && animStart.z > animEnd.z) {
            const tmp = animStart;
            animStart = animEnd;
            animEnd = tmp;
          }

          // === 若 reverse 為 true，則反向 ===
          if (reverse) {
            const tmp = animStart;
            animStart = animEnd;
            animEnd = tmp;
          }

          console.log(`dbId ${dbId} 動畫資訊:`, {
            axis: axis,
            size: size,
            animStart: animStart,
            animEnd: animEnd,
            distance: animStart.distanceTo(animEnd),
            reverse: reverse,
          });

          // 4. 建立 SpriteViewable，初始在 animStart
          const viewable = new DataVizCore.SpriteViewable(
            animStart.clone(),
            style,
            dbId
          );
          viewableData.addViewable(viewable);
          viewableData.finish().then(() => {
            dataVizExtn.addViewables(viewableData);

            // 5. 單向從起點移動到終點，然後重頭再來
            // === 新增 HTML箭頭動畫，會跟隨Sprite移動並指向終點 ===
            let arrow = document.createElement("img");
            arrow.src = "arrow.png";
            arrow.style.position = "absolute";
            arrow.style.width = "22px";
            arrow.style.height = "22px";
            arrow.style.pointerEvents = "none";
            arrow.style.zIndex = 10;
            arrow.className = `custom-3d-arrow-${dbId}`; // 加上 dbId 作為唯一識別
            viewer.container.appendChild(arrow);

            function updateArrow(pos) {
              const pos2d = viewer.worldToClient(pos.clone());
              const end2d = viewer.worldToClient(animEnd.clone());
              arrow.style.left = `${pos2d.x - 16}px`;
              arrow.style.top = `${pos2d.y - 16}px`;
              // 算角度：pos 指向 animEnd
              const dx = end2d.x - pos2d.x;
              const dy = end2d.y - pos2d.y;
              const angle = (Math.atan2(dy, dx) * 180) / Math.PI;
              arrow.style.transform = `rotate(${angle}deg)`;
            }

            // camera 移動時也要更新所有箭頭（只添加一次事件監聽器）
            if (!cameraEventAdded) {
              viewer.addEventListener(
                Autodesk.Viewing.CAMERA_CHANGE_EVENT,
                () => {
                  spriteAnimations.forEach((animation) => {
                    animation.updateArrow(animation.currentPos);
                  });
                }
              );
              cameraEventAdded = true;
            }

            // 動畫主程式
            let t = 0;
            let currentPos = animStart.clone();
            const animationInterval = setInterval(() => {
              t += 0.01;
              if (t > 1) t = 0; // 到終點就重頭
              currentPos = animStart.clone().lerp(animEnd, t);
              dataVizExtn.invalidateViewables([dbId], (v) => {
                return {
                  position: {
                    x: currentPos.x,
                    y: currentPos.y,
                    z: currentPos.z,
                  },
                };
              });
              updateArrow(currentPos);

              // 更新保存的 currentPos
              if (spriteAnimations.has(dbId)) {
                spriteAnimations.get(dbId).currentPos = currentPos;
              }
            }, 30);

            // 保存這個 dbId 的動畫資訊
            spriteAnimations.set(dbId, {
              interval: animationInterval,
              arrow: arrow,
              viewable: viewable,
              updateArrow: updateArrow,
              currentPos: currentPos,
            });
          });
        }
      });
    } catch (error) {
      console.error("創建動態Sprites時發生錯誤:", error);
    }
  };

  // 清理單個 Sprite 的函數
  const clearSingleSprite = (dbId) => {
    if (spriteAnimations.has(dbId)) {
      const animation = spriteAnimations.get(dbId);

      // 清理動畫計時器
      if (animation.interval) {
        clearInterval(animation.interval);
      }

      // 清理箭頭元素
      if (animation.arrow && animation.arrow.parentNode) {
        animation.arrow.parentNode.removeChild(animation.arrow);
      }

      // 移除這個 dbId 的記錄
      spriteAnimations.delete(dbId);

      console.log(`已清理 dbId ${dbId} 的 Sprite 資源`);
    }
  };

  // 清理所有 Sprites 和相關資源的函數
  const clearSprites = () => {
    // 清理所有動畫
    spriteAnimations.forEach((animation, dbId) => {
      clearSingleSprite(dbId);
    });

    // 清理所有 viewables
    if (dataVizExtn) {
      dataVizExtn.removeAllViewables();
    }

    // 重置相機事件標記
    cameraEventAdded = false;

    console.log("已清理所有 Sprites 和相關資源");
  };

  return {
    subscribeData,
    visibleDbid,
    updateDbidPosition,
    hideAllObjects,
    updateForgeViewer,
    forgeViewer,
    loadModel,
    urn,
    updateInitialData,
    subComponents,
    createSprites,
    clearSprites,
    clearSingleSprite,
  };
}
