import { state }        from './state.js';
import * as dragDrop     from './handlers/drag-drop.js';
import * as hoverPreview from './handlers/image-preview.js';
import * as selection    from './handlers/selection.js';
import { buildSequentialName, getNameFormat } from './utils/file-names.js';

const grid          = document.getElementById('grid');
const emptyState    = document.getElementById('empty-state');
const gridContainer = document.getElementById('grid-container');
const gridScrollbar = document.getElementById('grid-scrollbar');
const gridScrollbarThumb = document.getElementById('grid-scrollbar-thumb');
const fileCount     = document.getElementById('file-count');
const selCount      = document.getElementById('sel-count');
const inputPrefix   = document.getElementById('input-prefix');
const inputDigits   = document.getElementById('input-digits');
const inputPostfix  = document.getElementById('input-postfix');

let _cardObserver = null;
let _scrollStateRaf = null;
let _scrollbarDrag = null;

function updateGridScrollState() {
  if (_scrollStateRaf) cancelAnimationFrame(_scrollStateRaf);
  _scrollStateRaf = requestAnimationFrame(() => {
    _scrollStateRaf = null;
    const isScrollable = gridContainer.scrollHeight > gridContainer.clientHeight;
    gridContainer.classList.toggle('is-scrollable', isScrollable);
    updateGridScrollbarFrame();
    updateGridScrollbar();
  });
}

function updateGridScrollbarFrame() {
  if (!gridScrollbar) return;

  const rect = gridContainer.getBoundingClientRect();
  const top = rect.top + 20;
  const height = Math.max(0, rect.height - 40);

  gridScrollbar.style.setProperty('--grid-scrollbar-top', `${top}px`);
  gridScrollbar.style.setProperty('--grid-scrollbar-height', `${height}px`);
}

function updateGridScrollbar() {
  if (!gridScrollbar || !gridScrollbarThumb) return;

  const { clientHeight, scrollHeight, scrollTop } = gridContainer;
  const maxScrollTop = scrollHeight - clientHeight;
  if (maxScrollTop <= 0) {
    gridScrollbarThumb.style.height = '';
    gridScrollbarThumb.style.transform = '';
    return;
  }

  const trackHeight = gridScrollbar.clientHeight;
  const thumbHeight = Math.max(36, Math.round((clientHeight / scrollHeight) * trackHeight));
  const maxThumbTop = trackHeight - thumbHeight;
  const thumbTop = Math.round((scrollTop / maxScrollTop) * maxThumbTop);

  gridScrollbarThumb.style.height = `${thumbHeight}px`;
  gridScrollbarThumb.style.transform = `translateY(${thumbTop}px)`;
}

function scrollGridFromPointer(clientY) {
  const trackRect = gridScrollbar.getBoundingClientRect();
  const thumbHeight = gridScrollbarThumb.offsetHeight;
  const maxThumbTop = gridScrollbar.clientHeight - thumbHeight;
  const maxScrollTop = gridContainer.scrollHeight - gridContainer.clientHeight;
  if (maxThumbTop <= 0 || maxScrollTop <= 0) return;

  const targetThumbTop = Math.min(
    maxThumbTop,
    Math.max(0, clientY - trackRect.top - _scrollbarDrag.thumbOffset),
  );
  gridContainer.scrollTop = (targetThumbTop / maxThumbTop) * maxScrollTop;
}

const _gridResizeObserver = new ResizeObserver(updateGridScrollState);
_gridResizeObserver.observe(gridContainer);
_gridResizeObserver.observe(grid);

gridContainer.addEventListener('scroll', updateGridScrollbar);
window.addEventListener('resize', updateGridScrollState);

gridScrollbar.addEventListener('pointerdown', (e) => {
  if (!gridContainer.classList.contains('is-scrollable')) return;

  const thumbRect = gridScrollbarThumb.getBoundingClientRect();
  const isThumbPointer = e.target === gridScrollbarThumb;
  _scrollbarDrag = {
    thumbOffset: isThumbPointer ? e.clientY - thumbRect.top : thumbRect.height / 2,
  };

  gridScrollbar.classList.add('dragging');
  gridScrollbar.setPointerCapture(e.pointerId);
  scrollGridFromPointer(e.clientY);
  e.preventDefault();
});

gridScrollbar.addEventListener('pointermove', (e) => {
  if (!_scrollbarDrag) return;
  scrollGridFromPointer(e.clientY);
});

gridScrollbar.addEventListener('pointerup', (e) => {
  _scrollbarDrag = null;
  gridScrollbar.classList.remove('dragging');
  gridScrollbar.releasePointerCapture(e.pointerId);
});

gridScrollbar.addEventListener('pointercancel', (e) => {
  _scrollbarDrag = null;
  gridScrollbar.classList.remove('dragging');
  gridScrollbar.releasePointerCapture(e.pointerId);
});

export function renderGrid() {
  if (_cardObserver) { _cardObserver.disconnect(); _cardObserver = null; }

  grid.innerHTML = '';
  emptyState.style.display = 'none';
  grid.style.display = 'grid';
  fileCount.textContent = `${state.files.length}개 파일`;
  updateSelCount();

  state.files.forEach((file, idx) => grid.appendChild(createCard(file, idx)));

  const observer = new IntersectionObserver((entries) => {
    entries.forEach(async (entry) => {
      if (!entry.isIntersecting) return;
      observer.unobserve(entry.target);
      const img = entry.target.querySelector('img.card-thumb');
      if (!img || img.src) return;
      const dataUrl = await window.api.readImage(entry.target.dataset.filepath);
      if (dataUrl && entry.target.isConnected) img.src = dataUrl;
    });
  }, { root: gridContainer, rootMargin: '400px 0px', threshold: 0 });

  Array.from(grid.children).forEach(card => observer.observe(card));
  _cardObserver = observer;

  selection.resetAnchor();
  refreshCardSelection();
  updateNumberBadges();
  updateNewNameLabels();
  updateGridScrollState();
}

export function createCard(file, idx) {
  const card = document.createElement('div');
  card.className = 'card';
  card.dataset.idx = idx;
  card.dataset.filepath = file.path;
  card.draggable = true;

  const numBadge = document.createElement('div');
  numBadge.className = 'card-num';
  numBadge.textContent = `#${idx + 1}`;

  const checkBadge = document.createElement('div');
  checkBadge.className = 'card-check';
  checkBadge.textContent = '✓';

  const deleteBtn = document.createElement('button');
  deleteBtn.className = 'card-delete';
  deleteBtn.title = '삭제';
  deleteBtn.textContent = '×';
  deleteBtn.draggable = false;

  // src 없이 생성 — IntersectionObserver가 뷰포트 진입 시 로딩
  const thumb = document.createElement('img');
  thumb.className = 'card-thumb';
  thumb.alt = file.name;
  thumb.draggable = false;

  const body      = document.createElement('div');
  body.className  = 'card-body';
  const nameEl    = document.createElement('div');
  nameEl.className = 'card-name';
  nameEl.title    = file.name;
  nameEl.textContent = file.name;
  const newNameEl = document.createElement('div');
  newNameEl.className = 'card-new-name';

  body.appendChild(nameEl);
  body.appendChild(newNameEl);
  card.appendChild(numBadge);
  card.appendChild(checkBadge);
  card.appendChild(deleteBtn);
  card.appendChild(thumb);
  card.appendChild(body);

  dragDrop.setupCard(card);
  hoverPreview.setupCard(card);
  card.addEventListener('click', selection.onCardClick);

  return card;
}

export function refreshCardSelection() {
  Array.from(grid.children).forEach((card, i) => {
    card.classList.toggle('selected', state.selectedIdxs.has(i));
    card.classList.toggle('keyboard-target', selection.isKeyboardTarget(i));
  });
}

export function updateNumberBadges() {
  Array.from(grid.children).forEach((card, i) => {
    const badge = card.querySelector('.card-num');
    if (badge) badge.textContent = `#${i + 1}`;
    card.dataset.idx = i;
  });
}

export function updateNewNameLabels() {
  const format = getNameFormat(inputPrefix, inputDigits, 3, inputPostfix);
  Array.from(grid.children).forEach((card, i) => {
    const el   = card.querySelector('.card-new-name');
    const file = state.files[i];
    if (!el || !file) return;
    el.textContent = `→ ${buildSequentialName(file.name, i, format)}`;
  });
}

export function updateSelCount() {
  const n = state.selectedIdxs.size;
  if (n > 0) {
    selCount.textContent = `${n}개 선택됨`;
    selCount.classList.add('visible');
  } else {
    selCount.textContent = '';
    selCount.classList.remove('visible');
  }
}

export function showEmptyGrid() {
  grid.innerHTML = '';
  grid.style.display = 'none';
  emptyState.style.display = '';
  fileCount.textContent = '0개 파일';
  updateSelCount();
  updateGridScrollState();
}
