/**
 * Лёгкий сортировщик строк/плиток через pointer events. Без зависимостей.
 * Работает там, где HTML5 DnD капризничает (Chrome+input в строке).
 *
 * Использование:
 *   celenaSortable(container, {
 *     handle: '.cf-option-handle',    // элемент-ручка (обязательно)
 *     itemSelector: '.cf-option-row', // строка/плитка, которую двигаем
 *     onEnd: function (container) { ... }, // колбэк после отпускания (если порядок менялся)
 *     orientation: 'auto'             // 'vertical' | 'grid' | 'auto' (по умолчанию auto)
 *   });
 *
 * Ориентация:
 *   - vertical: учитываем только Y (ряды списков)
 *   - grid:     учитываем X+Y (плитки галереи и т.п.) — выбираем ближайший элемент
 *   - auto:     если высота контейнера < его ширины × 2 — grid, иначе vertical
 *
 * При drag добавляются классы:
 *   - .{itemSelector}.is-dragging — переносимая плитка
 *   - body.is-dragging-cursor — курсор «grabbing» на всё время сессии
 */
(function (global) {
  'use strict';

  function celenaSortable(container, opts) {
    if (!container) return;
    var handleSel = opts && opts.handle ? opts.handle : '.cf-option-handle';
    var itemSel   = opts && opts.itemSelector ? opts.itemSelector : '.cf-option-row';
    var onEnd     = opts && typeof opts.onEnd === 'function' ? opts.onEnd : null;
    var mode      = opts && opts.orientation ? opts.orientation : 'auto';

    container.style.touchAction = 'pan-y';
    container.addEventListener('pointerdown', onDown);

    function detectMode() {
      if (mode === 'vertical' || mode === 'grid') return mode;
      var r = container.getBoundingClientRect();
      // Эвристика: если контейнер визуально «широкий» — это grid.
      return r.height < r.width / 1.5 ? 'grid' : 'vertical';
    }

    function onDown(e) {
      if (e.button !== undefined && e.button !== 0) return;
      var handle = e.target.closest(handleSel);
      if (!handle || !container.contains(handle)) return;
      var row = handle.closest(itemSel);
      if (!row) return;

      e.preventDefault();

      var startX = e.clientX, startY = e.clientY;
      var moved = false;
      var orient = detectMode();
      var changed = false;

      function onMove(ev) {
        if (!moved) {
          if (Math.abs(ev.clientX - startX) < 4 && Math.abs(ev.clientY - startY) < 4) return;
          moved = true;
          row.classList.add('is-dragging');
          document.body.classList.add('is-dragging-cursor');
          try { handle.setPointerCapture(ev.pointerId); } catch (err) {}
        }

        var siblings = container.querySelectorAll(itemSel);
        if (orient === 'grid') {
          // Ищем ближайшую плитку по евклидовой дистанции до её центра.
          var best = null, bestDist = Infinity;
          for (var i = 0; i < siblings.length; i++) {
            var s = siblings[i];
            if (s === row) continue;
            var rect = s.getBoundingClientRect();
            var cx = rect.left + rect.width / 2;
            var cy = rect.top + rect.height / 2;
            var dx = ev.clientX - cx;
            var dy = ev.clientY - cy;
            var d = dx * dx + dy * dy;
            if (d < bestDist) { bestDist = d; best = { el: s, rect: rect, cx: cx }; }
          }
          if (best) {
            // Вставляем до или после ближайшего, исходя из X.
            var after = ev.clientX > best.cx;
            var ref = after ? best.el.nextSibling : best.el;
            if (ref !== row && ref !== row.nextSibling) {
              container.insertBefore(row, ref);
              changed = true;
            }
          }
        } else {
          for (var j = 0; j < siblings.length; j++) {
            var sv = siblings[j];
            if (sv === row) continue;
            var rv = sv.getBoundingClientRect();
            if (ev.clientY < rv.top || ev.clientY > rv.bottom) continue;
            var afterV = ev.clientY > rv.top + rv.height / 2;
            var refV = afterV ? sv.nextSibling : sv;
            if (refV !== row && refV !== row.nextSibling) {
              container.insertBefore(row, refV);
              changed = true;
            }
            break;
          }
        }
      }

      function onUp(ev) {
        document.removeEventListener('pointermove', onMove);
        document.removeEventListener('pointerup', onUp);
        document.removeEventListener('pointercancel', onUp);
        if (moved) {
          row.classList.remove('is-dragging');
          document.body.classList.remove('is-dragging-cursor');
          try { handle.releasePointerCapture(ev.pointerId); } catch (err) {}
          if (changed && onEnd) {
            try { onEnd(container); } catch (err) {}
          }
        }
      }

      document.addEventListener('pointermove', onMove);
      document.addEventListener('pointerup', onUp);
      document.addEventListener('pointercancel', onUp);
    }
  }

  global.celenaSortable = celenaSortable;
})(window);
