<section
  th:fragment="promotion-widget"
  class="widget"
  th:if="${theme.config.aside.promotion?.items != null and not #lists.isEmpty(theme.config.aside.promotion.items)}"
  th:with="totalCount = ${#lists.size(theme.config.aside.promotion.items)}, displayCount = ${T(java.lang.Integer).parseInt(theme.config.aside.promotion?.display_count ?: '6')}, itemCount = ${T(java.lang.Math).min(totalCount, displayCount)}"
>
  <hgroup class="widget-title text-creative" th:text="${theme.config.aside.promotion?.title} ?: '推广'">推广</hgroup>
  <div
    class="widget-body widget-card promotion-carousel"
    th:classappend="${itemCount <= 1} ? 'single' : ''"
    th:style="|--promotion-height: ${theme.config.aside.promotion?.height ?: 120}px; --promotion-count: ${itemCount}|"
    th:data-interval="${theme.config.aside.promotion?.interval ?: 5}"
  >
    <div class="promotion-track">
      <th:block th:each="slide, iterStat : ${theme.config.aside.promotion.items}" th:if="${iterStat.index < itemCount}">
        <a
          th:if="${not #strings.isEmpty(slide.url)}"
          th:href="${slide.url}"
          target="_blank"
          rel="noopener noreferrer sponsored"
          class="promotion-slide"
        >
          <img th:src="${slide.image}" th:alt="${slide.tip}" loading="lazy" draggable="false" />
          <span th:if="${not #strings.isEmpty(slide.tip)}" class="promotion-tip" th:text="${slide.tip}"></span>
        </a>
        <div th:unless="${not #strings.isEmpty(slide.url)}" class="promotion-slide">
          <img th:src="${slide.image}" th:alt="${slide.tip}" loading="lazy" draggable="false" />
          <span th:if="${not #strings.isEmpty(slide.tip)}" class="promotion-tip" th:text="${slide.tip}"></span>
        </div>
      </th:block>
    </div>
    <div class="promotion-dots" th:if="${itemCount > 1}">
      <span
        th:each="slide, iterStat : ${theme.config.aside.promotion.items}"
        th:if="${iterStat.index < itemCount}"
        class="promotion-dot"
        th:classappend="${iterStat.first} ? 'active' : ''"
        th:data-index="${iterStat.index}"
      ></span>
    </div>
  </div>
  <script>
    (function () {
      const carousel = document.querySelector(".promotion-carousel");
      if (!carousel) return;

      const track = carousel.querySelector(".promotion-track");
      const dots = carousel.querySelectorAll(".promotion-dot");
      const count = carousel.querySelectorAll(".promotion-slide").length;
      if (count <= 1) return;

      const interval = parseInt(carousel.dataset.interval) || 5;
      let current = 0;
      let timer = null;

      // 每次使用时实时获取宽度，避免初始不可见时为 0
      function getWidth() {
        return carousel.offsetWidth;
      }

      // 拖拽状态
      let isDragging = false;
      let allowClick = true;
      let startX = 0;
      let startY = 0;
      let direction = null; // "h" 水平 | "v" 垂直 | null 未定
      let currentTranslate = 0;
      let dragOffset = 0;

      // 动画参数
      const TRANSITION = "transform 0.35s cubic-bezier(0.25, 0.46, 0.45, 0.94)";
      const DRAG_THRESHOLD = 0.2;
      const DAMPING = 0.3;

      function goToSlide(index, animate) {
        const w = getWidth();
        current = index;
        currentTranslate = -current * w;
        track.style.transition = animate ? TRANSITION : "none";
        track.style.transform = `translateX(${currentTranslate}px)`;
        dots.forEach((d, i) => d.classList.toggle("active", i === index));
      }

      function next() {
        goToSlide((current + 1) % count, true);
      }

      function startAuto() {
        if (interval > 0 && !timer) {
          timer = setInterval(next, interval * 1000);
        }
      }

      function stopAuto() {
        if (timer) {
          clearInterval(timer);
          timer = null;
        }
      }

      function getPointer(e) {
        const t = e.touches ? e.touches[0] : e;
        return { x: t.clientX, y: t.clientY };
      }

      function dragStart(e) {
        if (e.button && e.button !== 0) return;

        // 仅对 mouse 事件阻止原生拖拽（Swiper 对 touchstart 直接 return，不 preventDefault）
        // touchstart 上 preventDefault 会阻止浏览器合成 click，导致链接点不了
        if (!e.touches && e.cancelable) e.preventDefault();

        const p = getPointer(e);
        isDragging = true;
        allowClick = true;
        startX = p.x;
        startY = p.y;
        direction = null;
        dragOffset = 0;

        track.style.transition = "none";
        carousel.classList.add("dragging");
        stopAuto();
      }

      function dragMove(e) {
        if (!isDragging) return;

        const p = getPointer(e);
        const dx = p.x - startX;
        const dy = p.y - startY;

        // 方向锁定：移动距离² > 25 时判断方向（与 Swiper touchAngle 逻辑一致）
        if (!direction) {
          if (dx * dx + dy * dy < 25) return;
          direction = Math.abs(dx) >= Math.abs(dy) ? "h" : "v";
        }

        // 垂直滑动：放弃拖拽，交给浏览器处理滚动
        if (direction === "v") {
          isDragging = false;
          carousel.classList.remove("dragging");
          startAuto();
          return;
        }

        // 水平拖拽：标记不允许点击，阻止默认行为
        allowClick = false;
        if (e.cancelable) e.preventDefault();

        // 边界阻尼
        const w = getWidth();
        const target = currentTranslate + dx;
        if (target > 0) {
          dragOffset = dx * DAMPING;
        } else if (target < -(count - 1) * w) {
          const overflow = target + (count - 1) * w;
          dragOffset = dx - overflow + overflow * DAMPING;
        } else {
          dragOffset = dx;
        }

        // 直接更新 transform（Swiper 同样在 onTouchMove 中直接 setTranslate）
        track.style.transform = `translateX(${currentTranslate + dragOffset}px)`;
      }

      function dragEnd() {
        if (!isDragging) return;
        isDragging = false;

        carousel.classList.remove("dragging");

        const moved = dragOffset;
        const w = getWidth();
        dragOffset = 0;

        if (Math.abs(moved) > w * DRAG_THRESHOLD) {
          if (moved < 0 && current < count - 1) {
            goToSlide(current + 1, true);
          } else if (moved > 0 && current > 0) {
            goToSlide(current - 1, true);
          } else {
            goToSlide(current, true);
          }
        } else {
          goToSlide(current, true);
        }

        startAuto();
      }

      // Swiper 模式：click 捕获阶段检查 allowClick 标志
      function onClick(e) {
        if (!allowClick) {
          if (e.cancelable) e.preventDefault();
          e.stopPropagation();
          e.stopImmediatePropagation();
          return false;
        }
      }

      // mouse 事件
      carousel.addEventListener("mousedown", dragStart);
      document.addEventListener("mousemove", dragMove);
      document.addEventListener("mouseup", dragEnd);

      // touch 事件（touchstart 无需 preventDefault，可 passive）
      carousel.addEventListener("touchstart", dragStart, { passive: true });
      carousel.addEventListener("touchmove", dragMove, { passive: false });
      carousel.addEventListener("touchend", dragEnd);

      // 点击拦截（捕获阶段）
      carousel.addEventListener("click", onClick, true);

      // 圆点点击
      dots.forEach((dot) => {
        dot.addEventListener("click", (e) => {
          e.stopPropagation();
          goToSlide(parseInt(dot.dataset.index), true);
          stopAuto();
          startAuto();
        });
      });

      // 鼠标悬停暂停
      carousel.addEventListener("mouseenter", stopAuto);
      carousel.addEventListener("mouseleave", () => {
        if (!isDragging) startAuto();
      });

      // resize 重新定位
      window.addEventListener("resize", () => goToSlide(current, false));

      // 初始定位
      goToSlide(0, false);
      startAuto();
    })();
  </script>
</section>
