<template>
  <div class="model-viewer">
    <div ref="containerRef" class="engine-container" :class="{ 'is-hidden': hideEngine }"></div>
    <div class="engine-state" v-if="stateText">{{ stateText }}</div>

    <button v-if="showCodeBtn" class="model-code-btn" type="button" :disabled="encoding" @click="onModelCodeClick">
      {{ encoding ? "编码中..." : "模型编码" }}
    </button>

    <div v-if="showConfirm" class="code-confirm-mask">
      <div class="code-confirm-panel">
        <div class="code-confirm-title">模型编码</div>
        <div class="code-confirm-body">
          当前模型尚未进行模型编码，是否立即开始编码？
        </div>
        <div class="code-confirm-actions">
          <button class="code-confirm-btn code-confirm-btn-secondary" type="button" @click="onConfirmCancel">
            稍后
          </button>
          <button class="code-confirm-btn code-confirm-btn-primary" type="button" @click="onConfirmStart">
            开始编码
          </button>
        </div>
      </div>
    </div>

    <div v-if="encoding" class="encoding-overlay">
      <div class="encoding-spinner"></div>
      <div class="encoding-text">正在进行模型编码，请稍候…</div>
    </div>

    <div v-if="toastText" class="encoding-toast">{{ toastText }}</div>

  </div>
</template>

<script setup>
import { computed, nextTick, onBeforeUnmount, onMounted, ref, watch } from "vue";
import { BimEngine } from "iflow-engin-xg";
import {progressApi} from "../../service/api/progress.js";
import { useModelCenterStore } from "../../stores/modelCenter.js";


const props = defineProps({
  modelUrl: {
    type: String,
    default: "",
  },
  showConstructTreeButton: {
    type: Boolean,
    default: false,
  },
});

const emit = defineEmits(["componentBindEncoding", "codeBindComponent", "modelLoaded"]);
const modelCenterStore = useModelCenterStore();

defineExpose({
  highlightModels,
  cancelLightModels,
  otherLightModels,
  getData,
  getConstructTreeData,
});

let positionIdsCache = [];

function highlightModels(ids) {
  // engine.engine?.setModelColor(
  //     ids,
  //     '#ff4d4f',
  //     0.6
  // );
   engine.engine?.highlightModel(ids);
  positionIdsCache = ids;
}
function cancelLightModels() {
  console.log(88888)
  engine.engine?.restoreAllModelsColor()
  engine.engine?.unhighlightAllModels();
}
function otherLightModels(data) {
  cancelLightModels();
  data.codeData.forEach( i => {
    engine.engine?.setModelColor(
        i.codeData,
        i.color,
        0.2
    );
  })
}

function getData() {
  return { positionIds: positionIdsCache };
}

function getConstructTreeData() {
  return engine?.engine?.getConstructTreeData?.() || { level: [], type: [], major: [] };
}

const containerRef = ref(null);
const loading = ref(true);
const errorText = ref("");
const pendingText = ref("");
const encoding = ref(false);
const showCodeBtn = ref(false);
const showConfirm = ref(false);
const toastText = ref("");
let engine = null;
let disposed = false;
let resizeObserver = null;
let unsubEvents = [];
let toastTimer = null;
let loadAttempt = 0;
let modelPollingTimer = null;
let loadedModelUrl = "";
let hasLoadedModel = false;
const prefetchedModelUrls = new Set();

const resolvedModelUrl = computed(() => {
  const fromProp = String(props.modelUrl || "").trim();
  if (fromProp) return fromProp;
  const activeModel = modelCenterStore.activeModel;
  if (activeModel) {
    const fromStore = String(modelCenterStore.activeModelUrl || "").trim();
    return fromStore;
  }
  const fromEnv = String(import.meta.env.VITE_BIM_MODEL_URL || "").trim();
  if (fromEnv) return fromEnv;
  return "https://pub-8092fd0db2e14822a9f742ad0050750c.r2.dev/d6f7d87b-1376-4443-9a2d-0ac061ac35b8";
});

const hideEngine = computed(() => Boolean(pendingText.value));

const stateText = computed(() => {
  if (errorText.value) return `模型加载失败：${errorText.value}`;
  if (pendingText.value) return pendingText.value;
  if (loading.value) return "模型加载中...";
  return "";
});

function getActiveModelConvertStatus() {
  const status = String(modelCenterStore.activeModel?.convertStatus || "").trim();
  return status || "PENDING";
}

function updatePendingModelText() {
  const modelName = modelCenterStore.activeModelName || "当前模型";
  const status = getActiveModelConvertStatus();
  const label = status === "FAILED" || status === "FAILD" ? "转换失败" : "模型转换中";
  pendingText.value = `${label}：${modelName}，正在等待转换后的模型地址...`;
}

function stopModelPolling() {
  if (modelPollingTimer) clearInterval(modelPollingTimer);
  modelPollingTimer = null;
}

function startModelPolling() {
  if (modelPollingTimer) return;
  const pollingModelId = String(modelCenterStore.activeId || "");
  updatePendingModelText();
  modelPollingTimer = setInterval(async () => {
    try {
      await modelCenterStore.loadModels({ preferredActiveId: pollingModelId });
      prefetchAvailableModels();
      const stillCurrent = String(modelCenterStore.activeId || "") === pollingModelId;
      if (stillCurrent && resolvedModelUrl.value) {
        stopModelPolling();
        pendingText.value = "";
        await loadResolvedModel();
      } else {
        updatePendingModelText();
      }
    } catch (error) {
      console.error("刷新模型转换状态失败:", error);
    }
  }, 5000);
}

function prefetchAvailableModels() {
  for (const model of modelCenterStore.models || []) {
    const url = String(model?.url || "").trim();
    if (!url || prefetchedModelUrls.has(url) || url === loadedModelUrl) continue;
    prefetchedModelUrls.add(url);
    prefetchModelUrl(url);
  }
}

function prefetchModelUrl(url) {
  ["info", "lod0"].forEach((path) => {
    fetch(`${url}/${path}`, { cache: "force-cache" }).catch((error) => {
      console.warn(`[model-placeholder] prefetch ${path} failed:`, url, error);
    });
  });
}

async function initializeEngineComponent() {
  await Promise.resolve(
    engine.engine.initialize({
      backgroundColor: 0x333333,
      showViewCube: true,
    })
  );
  engine.constructTreeBtn?.setVisible(props.showConstructTreeButton);
}

async function resetEngineForModelSwitch(modelUrl) {
  if (!hasLoadedModel || loadedModelUrl === modelUrl) return;
  loadedModelUrl = "";
  hasLoadedModel = false;
  await initializeEngineComponent();
}

function getEngineComponent() {
  return engine?.engine?.getEngineComponent?.();
}

function bindEncodingEvents() {
  if (!engine?.on) return;
  unsubEvents = [
    engine.on("encoding:start", () => {
      encoding.value = true;
      showConfirm.value = false;
    }),
    engine.on("encoding:complete", () => {
      encoding.value = false;
      showCodeBtn.value = false;
      showToast("编码完成");
    }),
    engine.on("encoding:error", () => {
      encoding.value = false;
    }),
    engine.on("engine:model-loading-completed", async () => {
      const componentWbsCodes =  await progressApi.getCodeWbsMappings()
      if (disposed) return;
      handleModelLoadingCompleted();
      emit("modelLoaded", getConstructTreeData());
      console.log("加载模型2")
      engine.engine?.setComponentWbsCodes(componentWbsCodes);
    }),
    engine.on("component:clicked", (data) => {
      emit("codeBindComponent", data);
    }),
    engine.on("component:bind-encoding", (data) => {
      emit("componentBindEncoding", data);
    }),
  ];
}
function unbindEncodingEvents() {
  unsubEvents.forEach((fn) => {
    if (typeof fn === "function") fn();
  });
  unsubEvents = [];
}

function handleModelLoadingCompleted() {
  const comp = getEngineComponent();
  if (!comp) return;
  comp.readModelCodeFormStoge?.();
  const hasCode = comp.hasModelCode?.();
  console.log("[model-placeholder] hasModelCode:", hasCode);
  if (hasCode) {
    showCodeBtn.value = false;
  } else {
    // showCodeBtn.value = true;
    // showConfirm.value = true;
  }
}

function startEncoding() {
  if (encoding.value) return;
  const comp = getEngineComponent();
  if (!comp) {
    console.warn("engine component unavailable");
    return;
  }
  comp.startOneClickEncoding?.();
}

function onModelCodeClick() {
  startEncoding();
}

function onConfirmStart() {
  startEncoding();
}

function onConfirmCancel() {
  showConfirm.value = false;
}

function showToast(text) {
  toastText.value = String(text || "");
  if (toastTimer) clearTimeout(toastTimer);
  toastTimer = setTimeout(() => {
    toastText.value = "";
  }, 2000);
}

async function loadResolvedModel() {
  if (!engine?.engine || disposed) return;
  const modelUrl = resolvedModelUrl.value;
  if (!modelUrl) {
    errorText.value = "";
    updatePendingModelText();
    startModelPolling();
    loading.value = false;
    return;
  }
  stopModelPolling();
  pendingText.value = "";
  if (modelUrl === loadedModelUrl) {
    loading.value = false;
    errorText.value = "";
    return;
  }
  const attempt = ++loadAttempt;
  loading.value = true;
  errorText.value = "";
  try {
    await resetEngineForModelSwitch(modelUrl);
    if (disposed || attempt !== loadAttempt) return;
    console.log("[model-placeholder] loadModel:", modelUrl);
    await Promise.resolve(
      engine.engine.loadModel([modelUrl], {
        position: [0, 0, 0],
        rotation: [0, 0, 0],
        scale: [1, 1, 1],
      })
    );
    if (disposed || attempt !== loadAttempt) return;
    loadedModelUrl = modelUrl;
    hasLoadedModel = true;
    loading.value = false;
  } catch (error) {
    if (disposed || attempt !== loadAttempt) return;
    const message = error instanceof Error ? error.message : "unknown error";
    errorText.value = `${message}：${modelUrl}`;
    loading.value = false;
  }
}

function onDebugCheck() {
  const comp = getEngineComponent();
  if (!comp) {
    console.warn("[debug] engine component unavailable");
    return;
  }
  const hasCode = comp.hasModelCode?.();
  console.log("[debug] hasModelCode:", hasCode);
  window.alert(`hasModelCode: ${hasCode}`);
}

function onDebugRead() {
  const comp = getEngineComponent();
  if (!comp) {
    console.warn("[debug] engine component unavailable");
    return;
  }
  comp.readModelCodeFormStoge?.();
  console.log("[debug] readModelCodeFormStoge called");
}

onMounted(async () => {
  if (!containerRef.value) return;
  disposed = false;

  await nextTick();

  let ready = false;
  for (let i = 0; i < 30; i++) {
    const rect = containerRef.value.getBoundingClientRect();
    if (rect.width > 0 && rect.height > 0) {
      ready = true;
      break;
    }
    await new Promise((resolve) => requestAnimationFrame(resolve));
  }

  if (!ready || disposed) {
    errorText.value = "container size is not ready";
    loading.value = false;
    return;
  }

  try {
    engine = new BimEngine(containerRef.value, {
      locale: "zh-CN",
      theme: "light",
    });

    if (!engine?.engine) {
      throw new Error("engine manager unavailable");
    }

    bindEncodingEvents();

    await initializeEngineComponent();

    if (disposed) return;

    await modelCenterStore.loadModels().catch((error) => {
      console.error("加载模型中心列表失败:", error);
    });
    prefetchAvailableModels();
    if (disposed) return;

    await loadResolvedModel();
    if (disposed) return;

    resizeObserver = new ResizeObserver(() => {
      engine?.engine?.resize?.();
      engine?.resize?.();
    });
    resizeObserver.observe(containerRef.value);
  } catch (error) {
    errorText.value = error instanceof Error ? error.message : "unknown error";
    loading.value = false;
  }
});

watch(resolvedModelUrl, (next, previous) => {
  if (!engine?.engine) return;
  if (!next) {
    startModelPolling();
    return;
  }
  if (next === previous) return;
  loadResolvedModel();
});

watch(
  () => [modelCenterStore.activeId, modelCenterStore.activeModel?.convertStatus, modelCenterStore.activeModelUrl],
  () => {
    if (!engine?.engine) return;
    loadResolvedModel();
  }
);

onBeforeUnmount(() => {
  disposed = true;
  if (toastTimer) clearTimeout(toastTimer);
  stopModelPolling();
  unbindEncodingEvents();
  resizeObserver?.disconnect?.();
  resizeObserver = null;
  try {
    engine?.destroy?.();
  } finally {
    engine = null;
  }
});
</script>

<style scoped>
.model-viewer {
  position: absolute;
  inset: 0;
}

.engine-container {
  position: absolute;
  inset: 0;
}

.engine-container.is-hidden {
  opacity: 0;
  pointer-events: none;
}

.engine-state {
  position: absolute;
  left: 50%;
  top: 52px;
  transform: translateX(-50%);
  z-index: 5;
  max-width: min(560px, calc(100vw - 48px));
  border-radius: 10px;
  border: 1px solid rgba(255, 255, 255, 0.18);
  background: rgba(20, 28, 34, 0.72);
  color: rgba(236, 246, 250, 0.9);
  font-size: 12px;
  font-weight: 700;
  padding: 6px 10px;
  text-align: center;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  pointer-events: none;
}

.model-code-btn {
  position: absolute;
  left: 16px;
  bottom: 16px;
  z-index: 5;
  border-radius: 10px;
  border: 1px solid rgba(83, 214, 206, 0.35);
  background: rgba(18, 29, 35, 0.85);
  color: rgba(236, 246, 250, 0.95);
  font-size: 12px;
  font-weight: 700;
  padding: 8px 14px;
  cursor: pointer;
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.28);
  transition: background 0.2s ease, transform 0.1s ease;
}

.model-code-btn:hover {
  background: rgba(28, 45, 54, 0.92);
  border-color: rgba(83, 214, 206, 0.55);
}

.model-code-btn:active {
  transform: translateY(1px);
}

.model-code-btn:disabled {
  opacity: 0.7;
  cursor: not-allowed;
}

.code-confirm-mask {
  position: absolute;
  inset: 0;
  z-index: 9999;
  display: grid;
  place-items: center;
  background: rgba(0, 0, 0, 0.45);
  backdrop-filter: blur(4px);
  -webkit-backdrop-filter: blur(4px);
}

.code-confirm-panel {
  width: min(360px, 86vw);
  border-radius: 16px;
  border: 1px solid rgba(83, 214, 206, 0.25);
  background:
    radial-gradient(260px 140px at 8% 0%, rgba(63, 203, 191, 0.12), transparent 62%) padding-box,
    linear-gradient(180deg, rgba(20, 31, 37, 0.95), rgba(15, 23, 29, 0.92)) padding-box;
  box-shadow:
    0 32px 80px rgba(0, 0, 0, 0.45),
    0 0 0 1px rgba(83, 214, 206, 0.1) inset;
  padding: 20px;
  color: rgba(236, 246, 250, 0.95);
}

.code-confirm-title {
  font-size: 18px;
  font-weight: 800;
  margin-bottom: 10px;
}

.code-confirm-body {
  font-size: 14px;
  line-height: 1.6;
  color: rgba(220, 238, 244, 0.85);
  margin-bottom: 18px;
}

.code-confirm-actions {
  display: flex;
  justify-content: flex-end;
  gap: 10px;
}

.code-confirm-btn {
  border-radius: 10px;
  padding: 8px 14px;
  font-size: 13px;
  font-weight: 700;
  cursor: pointer;
  border: 1px solid transparent;
  transition: background 0.2s ease, transform 0.1s ease;
}

.code-confirm-btn-secondary {
  background: rgba(255, 255, 255, 0.1);
  color: rgba(236, 246, 250, 0.9);
  border-color: rgba(255, 255, 255, 0.15);
}

.code-confirm-btn-secondary:hover {
  background: rgba(255, 255, 255, 0.16);
}

.code-confirm-btn-primary {
  background: rgba(83, 214, 206, 0.85);
  color: rgba(11, 27, 58, 0.95);
  border-color: rgba(83, 214, 206, 0.9);
}

.code-confirm-btn-primary:hover {
  background: rgba(83, 214, 206, 0.95);
}

.code-confirm-btn:active {
  transform: translateY(1px);
}

.encoding-overlay {
  position: absolute;
  inset: 0;
  z-index: 9999;
  display: grid;
  place-items: center;
  place-content: center;
  gap: 14px;
  background: rgba(0, 0, 0, 0.55);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
  color: rgba(236, 246, 250, 0.95);
  pointer-events: auto;
}

.encoding-spinner {
  width: 44px;
  height: 44px;
  border-radius: 50%;
  border: 3px solid rgba(83, 214, 206, 0.2);
  border-top-color: rgba(83, 214, 206, 0.95);
  animation: encoding-spin 1s linear infinite;
}

@keyframes encoding-spin {
  to {
    transform: rotate(360deg);
  }
}

.encoding-text {
  font-size: 14px;
  font-weight: 700;
}

.encoding-toast {
  position: absolute;
  left: 50%;
  top: 56px;
  transform: translateX(-50%);
  z-index: 100;
  border-radius: 10px;
  border: 1px solid rgba(83, 214, 206, 0.35);
  background: rgba(18, 29, 35, 0.92);
  color: rgba(83, 214, 206, 0.98);
  font-size: 13px;
  font-weight: 800;
  padding: 8px 16px;
  pointer-events: none;
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  box-shadow: 0 10px 30px rgba(0, 0, 0, 0.35);
}

.my-modal {
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  z-index: 100;
}

.my-modal-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 16px 18px;
  border-bottom: 1px solid rgba(83, 214, 206, 0.16);
  background: radial-gradient(360px 100px at 10% 0%, rgba(83, 214, 206, 0.18), transparent 68%),
    linear-gradient(180deg, rgba(28, 134, 122, 0.45), rgba(15, 60, 74, 0.32));
}

.modal-close-btn {
  width: 28px;
  height: 28px;
  border-radius: 8px;
  border: 1px solid rgba(255, 255, 255, 0.12);
  background: rgba(255, 255, 255, 0.08);
  color: rgba(203, 230, 236, 0.75);
  font-size: 14px;
  cursor: pointer;
  display: grid;
  place-items: center;
}

.modal-close-btn:hover {
  background: rgba(255, 255, 255, 0.14);
  color: rgba(236, 246, 250, 0.95);
}

.my-modal-body {
  padding: 18px;
  min-height: 200px;
}
</style>
