document.addEventListener("DOMContentLoaded", async () => {
  if (!window.zenbaxCustomizer) return;

  const cfg = zenbaxCustomizer.config || {};
  const productId = parseInt(zenbaxCustomizer.product_id || 0, 10);

  const $ = (id) => document.getElementById(id);

  const el = {
    mount: $("zb3d-mount"),
    status: $("zb-status"),

    size: $("zb-size"),
    fabric: $("zb-fabric"),

    zipper: $("zb-front-zipper"),
    rope: $("zb-rope"),
    keyhook: $("zb-back-keyhook"),

    labelLogo: $("zb-label-logo"),
    imprintLogo: $("zb-imprint-logo"),
    liningLogo: $("zb-lining-logo"),

    backLabelText: $("zb-back-label-text"),
    hangTag: $("zb-hang-tag"),
    wrapPaper: $("zb-wrap-paper"),
    colorBox: $("zb-color-box"),

    embroideryText: $("zb-embroidery-text"),
    embroideryZone: $("zb-embroidery-zone"),

    decalX: $("zb-decal-x"),
    decalY: $("zb-decal-y"),
    decalScale: $("zb-decal-scale"),

    downloadBtn: $("zb-download"),
    addBtn: $("zb-add"),
    saveBtn: $("zb-save"),
    shareBox: $("zb-share"),
    loadHint: $("zb-load-hint"),
  };

  const setStatus = (t) => { if (el.status) el.status.textContent = t || ""; };

  // ---- scene ----
  const scene = new THREE.Scene();
  scene.background = new THREE.Color(0xf7f7f7);

  const camera = new THREE.PerspectiveCamera(35, 1, 0.1, 1000);
  camera.position.set(0, 1.1, 2.6);

  const renderer = new THREE.WebGLRenderer({ antialias: true, preserveDrawingBuffer: true });
  renderer.setPixelRatio(Math.min(window.devicePixelRatio || 1, 2));
  el.mount.appendChild(renderer.domElement);

  const controls = new THREE.OrbitControls(camera, renderer.domElement);
  controls.enableDamping = true;
  controls.dampingFactor = 0.08;
  controls.minDistance = 1.4;
  controls.maxDistance = 4.5;

  scene.add(new THREE.HemisphereLight(0xffffff, 0x444444, 1.0));
  const dir = new THREE.DirectionalLight(0xffffff, 1.0);
  dir.position.set(2, 3, 2);
  scene.add(dir);

  function resize() {
    const w = el.mount.clientWidth;
    const h = el.mount.clientHeight;
    renderer.setSize(w, h, false);
    camera.aspect = w / h;
    camera.updateProjectionMatrix();
  }
  window.addEventListener("resize", resize);

  // ---- load glb ----
  let modelRoot = null;

  // decal plane (logo贴片)
  let decalPlane = null;
  let decalMat = null;

  // caches of matched meshes/materials
  const matched = {
    body: [],
    zipper: [],
    rope: [],
    keyhook: [],
  };

  function findTargets() {
    matched.body = [];
    matched.zipper = [];
    matched.rope = [];
    matched.keyhook = [];

    if (!modelRoot) return;

    modelRoot.traverse((obj) => {
      if (!obj.isMesh) return;

      const n = (obj.name || "").toLowerCase();
      const mn = (obj.material && obj.material.name ? obj.material.name : "").toLowerCase();

      const key = (n + " " + mn);

      // 这里是“自动匹配规则”，匹配不到也不会报错
      if (key.includes("body") || key.includes("shell") || key.includes("panel") || key.includes("bag")) matched.body.push(obj);
      if (key.includes("zip") || key.includes("zipper") || key.includes("slider")) matched.zipper.push(obj);
      if (key.includes("rope") || key.includes("pull") || key.includes("cord")) matched.rope.push(obj);
      if (key.includes("hook") || key.includes("key") || key.includes("strap")) matched.keyhook.push(obj);
    });

    // 帮你调试：你可以在控制台看到所有mesh名
    // console.log("Zenbax Mesh List:", modelRoot);
  }

  function hexToColor(hex) {
    try { return new THREE.Color(hex); } catch { return new THREE.Color("#111111"); }
  }

  function applyColor(meshes, hex) {
    if (!meshes || !meshes.length) return;

    meshes.forEach((m) => {
      // 只对可改材质的mesh改颜色
      if (!m.material) return;

      // 如果是多材质数组
      if (Array.isArray(m.material)) {
        m.material.forEach((mat) => {
          if (mat && mat.color) mat.color = hexToColor(hex);
          if (mat) mat.needsUpdate = true;
        });
      } else {
        if (m.material.color) m.material.color = hexToColor(hex);
        m.material.needsUpdate = true;
      }
    });
  }

  function getSelectedHex(selectEl) {
    const key = (selectEl && selectEl.value) ? String(selectEl.value) : "black";
    const colors = cfg.colors || [];
    const hit = colors.find(c => c.key === key);
    return hit ? hit.hex : "#111111";
  }

  function ensureDecalPlane() {
    if (!modelRoot) return;

    if (!decalMat) {
      decalMat = new THREE.MeshBasicMaterial({
        color: 0xffffff,
        transparent: true,
        opacity: 1.0,
        depthTest: true,
      });
    }

    if (!decalPlane) {
      const geo = new THREE.PlaneGeometry(0.6, 0.35);
      decalPlane = new THREE.Mesh(geo, decalMat);
      decalPlane.name = "ZENBAX_DECAL_PLANE";

      // 初始放在模型前面一点
      decalPlane.position.set(0.15, 0.1, 0.55);
      decalPlane.rotation.set(0, 0, 0);

      modelRoot.add(decalPlane);
    }
  }

  function updateDecalTransform() {
    if (!decalPlane) return;

    const dx = parseFloat(el.decalX?.value ?? "0.15");
    const dy = parseFloat(el.decalY?.value ?? "0.10");
    const sc = parseFloat(el.decalScale?.value ?? "1.00");

    decalPlane.position.x = dx;
    decalPlane.position.y = dy;
    decalPlane.scale.set(sc, sc, sc);
  }

  async function fileToDataURL(file) {
    return await new Promise((resolve, reject) => {
      if (!file) return resolve("");
      const reader = new FileReader();
      reader.onload = () => resolve(String(reader.result || ""));
      reader.onerror = reject;
      reader.readAsDataURL(file);
    });
  }

  async function setDecalFromFile(file) {
    if (!file) return;

    const dataUrl = await fileToDataURL(file);
    if (!dataUrl) return;

    // 创建纹理
    const img = new Image();
    img.crossOrigin = "anonymous";
    img.onload = () => {
      const tex = new THREE.Texture(img);
      tex.needsUpdate = true;
      tex.flipY = false;

      decalMat.map = tex;
      decalMat.needsUpdate = true;
      ensureDecalPlane();
      updateDecalTransform();
      setStatus("Logo applied on 3D preview.");
    };
    img.onerror = () => setStatus("Logo image load failed.");
    img.src = dataUrl;
  }

  function getPreviewPNG() {
    try {
      return renderer.domElement.toDataURL("image/png");
    } catch (e) {
      console.error(e);
      return "";
    }
  }

  function bomFromConfig(configObj) {
    // 工厂可读 BOM：把关键选择都列出来
    const bom = {
      model_code: cfg.model_code || "Zenbax Festibax Xline",
      size: configObj.size,
      fabric: configObj.fabric,
      outside: {
        front_zipper_color: configObj.front_zipper_color,
        zipper_pulls_rope_color: configObj.zipper_pulls_rope_color,
      },
      inside: {
        back_inner_label_text: configObj.back_inner_label_text,
        back_keyhook_color: configObj.back_keyhook_color,
      },
      add_ons: {
        hang_tag_custom: !!configObj.hang_tag_custom,
        wrap_paper_custom: !!configObj.wrap_paper_custom,
        color_box_custom: !!configObj.color_box_custom,
      },
      personalization: {
        embroidery_text: configObj.embroidery_text,
        embroidery_zone: configObj.embroidery_zone,
      },
      assets: {
        front_panel_label_logo: configObj.front_panel_label_logo ? "(embedded_base64)" : "",
        front_panel_imprint_logo: configObj.front_panel_imprint_logo ? "(embedded_base64)" : "",
        lining_imprint_logo: configObj.lining_imprint_logo ? "(embedded_base64)" : "",
      },
      decal: {
        x: configObj.decal_x,
        y: configObj.decal_y,
        scale: configObj.decal_scale,
      }
    };
    return bom;
  }

  async function getConfigSnapshot() {
    const obj = {
      model_code: cfg.model_code || "Zenbax Festibax Xline",

      size: el.size?.value || "Medium",
      fabric: el.fabric?.value || "oxford",

      front_zipper_color: el.zipper?.value || "black",
      zipper_pulls_rope_color: el.rope?.value || "black",
      back_keyhook_color: el.keyhook?.value || "black",

      back_inner_label_text: (el.backLabelText?.value || "").trim(),

      hang_tag_custom: !!el.hangTag?.checked,
      wrap_paper_custom: !!el.wrapPaper?.checked,
      color_box_custom: !!el.colorBox?.checked,

      embroidery_text: (el.embroideryText?.value || "").trim(),
      embroidery_zone: el.embroideryZone?.value || (cfg.default_zone || "strap_logo_zone"),

      // decal sliders
      decal_x: parseFloat(el.decalX?.value ?? "0.15"),
      decal_y: parseFloat(el.decalY?.value ?? "0.10"),
      decal_scale: parseFloat(el.decalScale?.value ?? "1.00"),

      // uploads as base64（MVP：先写入 config/bom；后续你要“上传到媒体库再存URL”，我也能一次性升级）
      front_panel_label_logo: "",
      front_panel_imprint_logo: "",
      lining_imprint_logo: "",
    };

    // 注意：base64 太大会拖慢下单，所以建议你先用小logo图测
    obj.front_panel_label_logo  = await fileToDataURL(el.labelLogo?.files?.[0] || null);
    obj.front_panel_imprint_logo = await fileToDataURL(el.imprintLogo?.files?.[0] || null);
    obj.lining_imprint_logo = await fileToDataURL(el.liningLogo?.files?.[0] || null);

    return obj;
  }

  async function applyConfigToScene() {
    if (!modelRoot) return;

    // apply colors
    const zipperHex = getSelectedHex(el.zipper);
    const ropeHex   = getSelectedHex(el.rope);
    const hookHex   = getSelectedHex(el.keyhook);

    // body color：先用 fabric 代表 body 色（MVP：你后面可以换为材质贴图）
    // 这里先用一个固定映射：不同fabric给不同底色（你可随时改）
    const fabricKey = el.fabric?.value || "oxford";
    const fabricColorMap = {
      oxford: "#1f2937",
      rpet_cationic: "#0f766e",
      club_leather: "#5a3e2b",
      cordura: "#111111",
      reflective_mesh: "#9ca3af",
    };
    const bodyHex = fabricColorMap[fabricKey] || "#111111";

    applyColor(matched.body, bodyHex);
    applyColor(matched.zipper, zipperHex);
    applyColor(matched.rope, ropeHex);
    applyColor(matched.keyhook, hookHex);

    updateDecalTransform();
  }

  // bind UI changes
  const bindChange = (node, fn) => {
    if (!node) return;
    node.addEventListener("change", fn);
    node.addEventListener("input", fn);
  };

  bindChange(el.fabric, () => applyConfigToScene());
  bindChange(el.zipper, () => applyConfigToScene());
  bindChange(el.rope, () => applyConfigToScene());
  bindChange(el.keyhook, () => applyConfigToScene());

  bindChange(el.decalX, () => updateDecalTransform());
  bindChange(el.decalY, () => updateDecalTransform());
  bindChange(el.decalScale, () => updateDecalTransform());

  // use label logo as default decal
  el.labelLogo?.addEventListener("change", async () => {
    const f = el.labelLogo.files?.[0] || null;
    if (f) {
      ensureDecalPlane();
      await setDecalFromFile(f);
    }
  });

  // ---- load model ----
  setStatus("Loading 3D model...");
  const loader = new THREE.GLTFLoader();

  loader.load(
    cfg.glb_url,
    (gltf) => {
      modelRoot = gltf.scene;
      scene.add(modelRoot);

      // center + scale
      const box = new THREE.Box3().setFromObject(modelRoot);
      const size = new THREE.Vector3();
      const center = new THREE.Vector3();
      box.getSize(size);
      box.getCenter(center);

      modelRoot.position.sub(center);
      const maxAxis = Math.max(size.x, size.y, size.z);
      const scale = 1.2 / (maxAxis || 1);
      modelRoot.scale.setScalar(scale);

      // match meshes
      findTargets();

      // decal plane
      ensureDecalPlane();
      updateDecalTransform();

      applyConfigToScene();
      setStatus("Ready.");
      resize();
    },
    (xhr) => {
      const p = xhr.total ? Math.round((xhr.loaded / xhr.total) * 100) : 0;
      setStatus(`Loading 3D model... ${p ? p + "%" : ""}`);
    },
    (err) => {
      console.error(err);
      setStatus("Failed to load GLB. Please confirm the GLB URL is public.");
    }
  );

  function animate() {
    requestAnimationFrame(animate);
    controls.update();
    renderer.render(scene, camera);
  }
  resize();
  animate();

  // ---- Load design from share link (?design=ID) ----
  async function maybeLoadDesignFromURL() {
    const params = new URLSearchParams(window.location.search || "");
    const id = params.get("design");
    if (!id) return;

    try {
      setStatus("Loading shared design...");
      const url = `${zenbaxCustomizer.ajax_url}?action=zenbax_get_design&id=${encodeURIComponent(id)}`;
      const res = await fetch(url);
      const json = await res.json();
      if (!json.success) {
        setStatus("Shared design not found.");
        return;
      }

      const configJson = json.config_json || "";
      const obj = JSON.parse(configJson);

      // apply to UI
      if (obj.size && el.size) el.size.value = obj.size;
      if (obj.fabric && el.fabric) el.fabric.value = obj.fabric;
      if (obj.front_zipper_color && el.zipper) el.zipper.value = obj.front_zipper_color;
      if (obj.zipper_pulls_rope_color && el.rope) el.rope.value = obj.zipper_pulls_rope_color;
      if (obj.back_keyhook_color && el.keyhook) el.keyhook.value = obj.back_keyhook_color;

      if (el.backLabelText) el.backLabelText.value = obj.back_inner_label_text || "";
      if (el.hangTag) el.hangTag.checked = !!obj.hang_tag_custom;
      if (el.wrapPaper) el.wrapPaper.checked = !!obj.wrap_paper_custom;
      if (el.colorBox) el.colorBox.checked = !!obj.color_box_custom;

      if (el.embroideryText) el.embroideryText.value = obj.embroidery_text || "";
      if (el.embroideryZone && obj.embroidery_zone) el.embroideryZone.value = obj.embroidery_zone;

      if (el.decalX && typeof obj.decal_x === "number") el.decalX.value = String(obj.decal_x);
      if (el.decalY && typeof obj.decal_y === "number") el.decalY.value = String(obj.decal_y);
      if (el.decalScale && typeof obj.decal_scale === "number") el.decalScale.value = String(obj.decal_scale);

      // decal logo from embedded base64 (front_panel_label_logo)
      if (obj.front_panel_label_logo) {
        ensureDecalPlane();
        const img = new Image();
        img.crossOrigin = "anonymous";
        img.onload = () => {
          const tex = new THREE.Texture(img);
          tex.needsUpdate = true;
          tex.flipY = false;
          decalMat.map = tex;
          decalMat.needsUpdate = true;
          applyConfigToScene();
          setStatus("Shared design loaded.");
        };
        img.src = obj.front_panel_label_logo;
      } else {
        applyConfigToScene();
        setStatus("Shared design loaded.");
      }

      if (el.loadHint) el.loadHint.textContent = `Loaded design #${id}`;
    } catch (e) {
      console.error(e);
      setStatus("Failed to load shared design.");
    }
  }
  await maybeLoadDesignFromURL();

  // ---- buttons ----
  el.downloadBtn?.addEventListener("click", () => {
    const png = getPreviewPNG();
    if (!png) return alert("Preview export failed.");
    const a = document.createElement("a");
    a.href = png;
    a.download = "zenbax-preview.png";
    document.body.appendChild(a);
    a.click();
    a.remove();
  });

  el.saveBtn?.addEventListener("click", async () => {
    setStatus("Saving design...");

    const configObj = await getConfigSnapshot();
    const preview = getPreviewPNG();
    const form = new FormData();
    form.append("action", "zenbax_save_design");
    form.append("nonce", zenbaxCustomizer.nonce);
    form.append("config_json", JSON.stringify(configObj));
    form.append("preview_png", preview);

    const res = await fetch(zenbaxCustomizer.ajax_url, { method: "POST", body: form });
    const json = await res.json();

    if (!json.success) {
      setStatus("Save failed: " + (json.message || "unknown"));
      return;
    }

    if (el.shareBox) {
      el.shareBox.value = json.share_url || "";
      el.shareBox.select();
    }
    setStatus("Design saved. Share link is ready.");
  });

  el.addBtn?.addEventListener("click", async () => {
    if (!productId) {
      alert("Missing product ID. Please set ZENBAX_CUSTOMIZER_PRODUCT_ID in PHP.");
      return;
    }

    setStatus("Preparing order...");

    const configObj = await getConfigSnapshot();
    const bomObj = bomFromConfig(configObj);

    const configJson = JSON.stringify(configObj);
    const bomJson = JSON.stringify(bomObj);
    const preview = getPreviewPNG();

    const form = document.createElement("form");
    form.method = "POST";
    form.action = "/?add-to-cart=" + encodeURIComponent(String(productId));

    const fields = [
      { name: "quantity", value: "1" },
      { name: "zenbax_custom_config_json", value: configJson },
      { name: "zenbax_custom_bom_json", value: bomJson },
      { name: "zenbax_custom_preview", value: preview },
    ];

    fields.forEach((f) => {
      const input = document.createElement("input");
      input.type = "hidden";
      input.name = f.name;
      input.value = String(f.value || "");
      form.appendChild(input);
    });

    document.body.appendChild(form);
    form.submit();
  });
});