import { createAutoScroller } from './drag/auto-scroll.js';
import { createDragGhost } from './drag/ghost.js';

let _grid, _gridContainer, _fileExplorerEl;
let _state, _cb, _onFolderDrop;
let _dropSeparator      = null;
let _originPlaceholders = [];
let _dropTargetIdx      = null;
let _autoScroller       = null;
let _ghost              = null;

let dragMode    = null;
let dragSrcIdx  = null;
let _hoveredTreeRow = null;

export function init({ grid, gridContainer, dragGhostEl, state, callbacks, fileExplorerEl, onFolderDrop }) {
  _grid           = grid;
  _gridContainer  = gridContainer;
  _state          = state;
  _cb             = callbacks;
  _fileExplorerEl = fileExplorerEl;
  _onFolderDrop   = onFolderDrop;
  _autoScroller    = createAutoScroller(_gridContainer, () => Boolean(dragMode));
  _ghost           = createDragGhost(dragGhostEl);

  _dropSeparator = document.createElement('div');
  _dropSeparator.className = 'drop-separator';
  _gridContainer.appendChild(_dropSeparator);

  window.addEventListener('dragover', _onWindowDragOver);
  grid.addEventListener('dragover', _onGridDragOver);
  grid.addEventListener('drop',     _onGridDrop);

  if (fileExplorerEl) {
    fileExplorerEl.addEventListener('dragover',  _onExplorerDragOver);
    fileExplorerEl.addEventListener('dragleave', _onExplorerDragLeave);
    fileExplorerEl.addEventListener('drop',      _onExplorerDrop);
  }
}

export function setupCard(card) {
  card.addEventListener('dragstart', _onDragStart);
  card.addEventListener('dragover',  _onDragOver);
  card.addEventListener('dragenter', _onDragEnter);
  card.addEventListener('dragleave', _onDragLeave);
  card.addEventListener('drop',      _onDrop);
  card.addEventListener('dragend',   _onDragEnd);
}

// ── window-level dragover: 고스트 위치 + 자동 스크롤 ──

function _onWindowDragOver(e) {
  if (!dragMode) return;
  _ghost.move(e);
  _autoScroller.track(e.clientY);
}

// ── 카드 드래그 핸들러 ─────────────────────────────

function _onDragStart(e) {
  const idx = parseInt(this.dataset.idx);

  if (_state.selectedIdxs.has(idx) && _state.selectedIdxs.size > 1) {
    dragMode   = 'multi';
    dragSrcIdx = idx;

    const count  = _state.selectedIdxs.size;
    const toHide = [..._state.selectedIdxs].sort((a, b) => a - b);

    const img = new Image();
    img.src = 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7';
    e.dataTransfer.setDragImage(img, 0, 0);
    _ghost.show(e, `📦 ${count}개 이동 중`);

    // 브라우저가 드래그 이미지를 캡처한 뒤 카드 숨기고 원본 위치 표시
    requestAnimationFrame(() => {
      toHide.forEach(i => {
        const c = _getCardEl(i);
        if (c) {
          const originPh = _createOriginPlaceholder(i);
          _grid.insertBefore(originPh, c);
          _originPlaceholders.push(originPh);
          c.classList.add('drag-hidden');
        }
      });
    });
  } else {
    dragMode   = 'single';
    dragSrcIdx = idx;
    _state.selectedIdxs.clear();
    _cb.refreshCardSelection();

    const srcCard = this;
    requestAnimationFrame(() => {
      const originPh = _createOriginPlaceholder(idx);
      _grid.insertBefore(originPh, srcCard);
      _originPlaceholders.push(originPh);
      srcCard.classList.add('drag-hidden');
    });
  }

  e.dataTransfer.effectAllowed = 'move';
  e.dataTransfer.setData('text/plain', String(idx));
}

function _onDragOver(e) {
  e.preventDefault();
  e.dataTransfer.dropEffect = 'move';
  _updateDropTarget(e);
}

function _onDragEnter(e) {
  e.preventDefault();
  _updateDropTarget(e);
}

function _onDragLeave() {
  // separator 위치는 다음 dragover에서 갱신한다.
}

function _onDrop(e) {
  e.preventDefault();
  _updateDropTarget(e);
  _performDrop();
}

function _performDrop() {
  if (_dropTargetIdx === null) return;
  if (dragMode === 'single') {
    if (dragSrcIdx !== null && dragSrcIdx !== _dropTargetIdx) {
      _moveSingle(dragSrcIdx, _dropTargetIdx);
    }
  } else if (dragMode === 'multi') {
    if (!_state.selectedIdxs.has(_dropTargetIdx)) {
      _moveMulti(_dropTargetIdx);
    }
  }
}

function _onDragEnd() {
  _autoScroller.stop();
  _ghost.hide();
  _hideDropSeparator();
  _clearTreeHover();
  _clearOriginPlaceholders();
  _dropTargetIdx = null;
  document.querySelectorAll('.card').forEach(c =>
    c.classList.remove('drag-hidden', 'drag-over')
  );
  dragMode   = null;
  dragSrcIdx = null;
}

// ── 폴더 탐색기 드롭 핸들러 ─────────────────────────

function _onExplorerDragOver(e) {
  if (!dragMode) return;
  e.preventDefault();
  e.dataTransfer.dropEffect = 'move';

  const row = e.target.closest('.tree-row');
  if (row !== _hoveredTreeRow) {
    _clearTreeHover();
    _hoveredTreeRow = row;
    if (_hoveredTreeRow) _hoveredTreeRow.classList.add('drop-target');
  }

  if (dragMode === 'single' && row && _ghost.isHidden()) {
    _ghost.show(e, '📁 1개 폴더로 이동');
  }
  if (dragMode === 'multi' && row) {
    _ghost.setText(`📁 ${_state.selectedIdxs.size}개 폴더로 이동`);
  }
}

function _onExplorerDragLeave(e) {
  if (!_fileExplorerEl.contains(e.relatedTarget)) {
    _clearTreeHover();
    if (dragMode === 'single') _ghost.hide();
    if (dragMode === 'multi') _ghost.setText(`📦 ${_state.selectedIdxs.size}개 이동 중`);
  }
}

function _onExplorerDrop(e) {
  e.preventDefault();
  const row = e.target.closest('.tree-row');
  _clearTreeHover();

  if (!row || !_onFolderDrop) return;
  const folderNode = row.closest('.tree-node');
  if (!folderNode) return;

  const targetPath = folderNode.dataset.path;
  const indices    = _getDraggedFileIndices();
  _onFolderDrop(targetPath, indices);
}

function _getDraggedFileIndices() {
  if (dragMode === 'multi') return [..._state.selectedIdxs];
  if (dragMode === 'single' && dragSrcIdx !== null) return [dragSrcIdx];
  return [];
}

function _clearTreeHover() {
  if (_hoveredTreeRow) {
    _hoveredTreeRow.classList.remove('drop-target');
    _hoveredTreeRow = null;
  }
}

// ── 그리드 빈 공간 드래그 핸들러 ─────────────────────

function _onGridDragOver(e) {
  if (!dragMode) return;
  if (e.target.closest('.card')) return;
  e.preventDefault();
  e.dataTransfer.dropEffect = 'move';
  _updateDropTarget(e);
}

function _onGridDrop(e) {
  if (!dragMode) return;
  if (e.target.closest('.card')) return;
  e.preventDefault();
  _updateDropTarget(e);
  _performDrop();
}

// ── 이동 로직 ─────────────────────────────────────

function _moveSingle(srcIdx, targetIdx) {
  const [moved] = _state.files.splice(srcIdx, 1);
  const insertAt = srcIdx < targetIdx ? targetIdx - 1 : targetIdx;
  _state.files.splice(insertAt, 0, moved);

  _hideDropSeparator();
  _clearOriginPlaceholders();
  const srcCard    = _getCardEl(srcIdx);
  const targetCard = _getCardEl(targetIdx);
  if (srcCard) srcCard.classList.remove('drag-hidden');

  if (srcCard) {
    if (targetCard) {
      _grid.insertBefore(srcCard, targetCard);
    } else {
      _grid.appendChild(srcCard);
    }
  }

  Array.from(_grid.children).forEach((c, i) => { c.dataset.idx = i; });
  _cb.updateNumberBadges();
  _cb.updateNewNameLabels();
  _cb.pushHistory?.([moved.path]);
}

function _moveMulti(targetIdx) {
  const sorted     = [..._state.selectedIdxs].sort((a, b) => a - b);
  const movedFiles = sorted.map(i => _state.files[i]);

  const removedBefore  = sorted.filter(i => i < targetIdx).length;
  const adjustedTarget = targetIdx - removedBefore;

  for (let i = sorted.length - 1; i >= 0; i--) {
    _state.files.splice(sorted[i], 1);
  }
  _state.files.splice(adjustedTarget, 0, ...movedFiles);

  const newSel = new Set();
  for (let i = 0; i < movedFiles.length; i++) newSel.add(adjustedTarget + i);
  _state.selectedIdxs = newSel;

  _hideDropSeparator();
  _clearOriginPlaceholders();
  const byPath = new Map();
  Array.from(_grid.children).forEach(c => {
    c.classList.remove('drag-hidden');
    byPath.set(c.dataset.filepath, c);
  });
  _grid.innerHTML = '';
  _state.files.forEach((f, idx) => {
    const c = byPath.get(f.path);
    if (c) { c.dataset.idx = idx; _grid.appendChild(c); }
  });

  _cb.updateNumberBadges();
  _cb.updateNewNameLabels();
  _cb.refreshCardSelection();
  _cb.updateSelCount();
  _cb.pushHistory?.(movedFiles.map(f => f.path));
}

function _getCardEl(idx) {
  return _grid.querySelector(`.card[data-idx="${idx}"]`);
}

// ── 드롭 위치 표시 ────────────────────────────────

function _updateDropTarget(e) {
  const target = _getDropTargetFromPoint(e.clientX, e.clientY);
  if (!target) {
    _dropTargetIdx = null;
    _hideDropSeparator();
    return;
  }

  _dropTargetIdx = target.idx;
  _showDropSeparator(target);
}

function _getDropTargetFromPoint(clientX, clientY) {
  const items = _getVisualGridItems();
  if (items.length === 0) return null;
  const rows = _getGridRows(items);

  const hovered = items.find(item =>
    clientX >= item.rect.left &&
    clientX <= item.rect.right &&
    clientY >= item.rect.top &&
    clientY <= item.rect.bottom
  );
  if (hovered) {
    const hoveredRow = _getRowForItem(rows, hovered);
    if (!hoveredRow) return null;

    if (clientX <= hovered.rect.left + hovered.rect.width / 2) {
      return _makeBoundaryDropTarget(hoveredRow, hovered.idx);
    }
    return _makeBoundaryDropTarget(hoveredRow, hovered.idx + 1);
  }

  const row = rows.find(r => clientY >= r.top && clientY <= r.bottom) ||
              _getNearestRow(rows, clientY);
  if (!row) return null;

  if (clientX <= row.items[0].rect.left) {
    return _makeBoundaryDropTarget(row, row.items[0].idx);
  }

  const last = row.items[row.items.length - 1];
  if (clientX >= last.rect.right) {
    return _makeBoundaryDropTarget(row, last.idx + 1);
  }

  for (let i = 0; i < row.items.length - 1; i++) {
    const left = row.items[i];
    const right = row.items[i + 1];
    if (clientX > left.rect.right && clientX < right.rect.left) {
      return _makeBoundaryDropTarget(row, right.idx);
    }
  }

  return null;
}

function _getVisualGridItems() {
  return Array.from(_grid.children)
    .filter(el => !_isHiddenDragCard(el))
    .map(el => {
      const idx = el.classList.contains('card-origin-placeholder')
        ? parseInt(el.dataset.originIdx)
        : parseInt(el.dataset.idx);
      if (!Number.isFinite(idx)) return null;
      return { el, idx, rect: el.getBoundingClientRect() };
    })
    .filter(Boolean)
    .sort((a, b) => (a.rect.top - b.rect.top) || (a.rect.left - b.rect.left));
}

function _isHiddenDragCard(el) {
  return el.classList.contains('drag-hidden') || getComputedStyle(el).display === 'none';
}

function _getGridRows(items) {
  const rows = [];
  for (const item of items) {
    const row = rows.find(r => Math.abs(r.top - item.rect.top) < 4);
    if (row) {
      row.items.push(item);
      row.top = Math.min(row.top, item.rect.top);
      row.bottom = Math.max(row.bottom, item.rect.bottom);
    } else {
      rows.push({
        top: item.rect.top,
        bottom: item.rect.bottom,
        items: [item]
      });
    }
  }

  return rows.map(row => {
    row.items.sort((a, b) => a.rect.left - b.rect.left);
    row.rect = {
      top: row.top,
      bottom: row.bottom,
      height: row.bottom - row.top
    };
    return row;
  });
}

function _getNearestRow(rows, clientY) {
  if (rows.length === 0) return null;
  return rows.reduce((nearest, row) => {
    const rowDistance = clientY < row.top ? row.top - clientY : clientY - row.bottom;
    const nearestDistance = clientY < nearest.top ? nearest.top - clientY : clientY - nearest.bottom;
    return rowDistance < nearestDistance ? row : nearest;
  });
}

function _getRowForItem(rows, item) {
  return rows.find(row => row.items.includes(item));
}

function _makeBoundaryDropTarget(row, idx) {
  const clampedIdx = Math.max(0, Math.min(_state.files.length, idx));
  const rightIndex = row.items.findIndex(item => item.idx >= clampedIdx);
  const gap = _getRowGap(row);

  let clientX;
  if (rightIndex === -1) {
    const last = row.items[row.items.length - 1];
    clientX = last.rect.right + gap / 2;
  } else if (rightIndex <= 0) {
    clientX = row.items[0].rect.left - gap / 2;
  } else if (rightIndex >= row.items.length) {
    const last = row.items[row.items.length - 1];
    clientX = last.rect.right + gap / 2;
  } else {
    const left = row.items[rightIndex - 1];
    const right = row.items[rightIndex];
    clientX = (left.rect.right + right.rect.left) / 2;
  }

  return _makeDropTarget(clampedIdx, clientX, row.rect);
}

function _getRowGap(row) {
  const gaps = [];
  for (let i = 0; i < row.items.length - 1; i++) {
    const gap = row.items[i + 1].rect.left - row.items[i].rect.right;
    if (gap > 0) gaps.push(gap);
  }
  if (gaps.length > 0) return Math.min(...gaps);

  const style = getComputedStyle(_grid);
  return parseFloat(style.columnGap || style.gap) || 14;
}

function _makeDropTarget(idx, clientX, rect) {
  return {
    idx: Math.max(0, Math.min(_state.files.length, idx)),
    clientX,
    clientY: rect.top,
    height: rect.height || (rect.bottom - rect.top)
  };
}

function _showDropSeparator(target) {
  const containerRect = _gridContainer.getBoundingClientRect();
  _dropSeparator.style.display = 'block';
  _dropSeparator.style.left = `${target.clientX - containerRect.left - 1}px`;
  _dropSeparator.style.top = `${target.clientY - containerRect.top + _gridContainer.scrollTop}px`;
  _dropSeparator.style.height = `${target.height}px`;
}

function _hideDropSeparator() {
  if (_dropSeparator) _dropSeparator.style.display = 'none';
}

function _createOriginPlaceholder(idx) {
  const ph = document.createElement('div');
  ph.className = 'card card-origin-placeholder';
  ph.dataset.originIdx = idx;
  ph.addEventListener('dragover',  e => {
    e.preventDefault();
    e.dataTransfer.dropEffect = 'move';
    _updateDropTarget(e);
  });
  ph.addEventListener('dragenter', e => {
    e.preventDefault();
    _updateDropTarget(e);
  });
  ph.addEventListener('drop', e => {
    e.preventDefault();
    _updateDropTarget(e);
    _performDrop();
  });
  return ph;
}

function _clearOriginPlaceholders() {
  _originPlaceholders.forEach(ph => {
    if (ph.parentNode) ph.parentNode.removeChild(ph);
  });
  _originPlaceholders = [];
}
