// ─── 대시보드 뷰 (자유 배치 + 드래그/리사이즈 + 스냅 가이드) ───
const { useState: dUseState, useRef: dUseRef, useEffect: dUseEffect } = React;

const GRID = 40;        // 스냅 그리드
const SNAP_TH = 7;      // 자석 스냅 임계값(px)
const snap = (v) => Math.round(v / GRID) * GRID;

// 후보값 중 임계값 내 가장 가까운 값
function snapAxis(val, cands) {
  let best = null;
  for (const c of cands) {
    if (Math.abs(val - c) <= SNAP_TH && (best === null || Math.abs(val - c) < Math.abs(val - best))) best = c;
  }
  return best;
}

// 카드 메뉴
function CardMenu({ comp, onClose, onSettings, onChangeView, onDuplicate, onRemove }) {
  dUseEffect(() => {
    const h = () => onClose();
    window.addEventListener('click', h);
    return () => window.removeEventListener('click', h);
  }, []);
  return (
    <div className="card-menu" onClick={(e) => e.stopPropagation()}>
      <button onClick={() => { onSettings(); onClose(); }}>{I.settings}컴포넌트 설정</button>
      <div className="sep"></div>
      {['bar', 'line', 'pie', 'table'].map((v) => (
        <button key={v} onClick={() => { onChangeView(v); onClose(); }}>
          {VIEW_ICON[v]}{({ bar: '막대 차트로', line: '선 차트로', pie: '파이 차트로', table: '표로' })[v]}{comp.view === v && <span style={{ marginLeft: 'auto', color: 'var(--blue-600)' }}>{I.check}</span>}
        </button>
      ))}
      <div className="sep"></div>
      <button onClick={() => { onDuplicate(); onClose(); }}>{I.copy}복제</button>
      <button className="danger" onClick={() => { onRemove(); onClose(); }}>{I.trash}대시보드에서 제거</button>
    </div>
  );
}

function DashCard({ item, others, editing, onChange, onSettings, onRemove, onDuplicate, canvasW, setGuides }) {
  const [menu, setMenu] = dUseState(false);
  const ref = dUseRef(null);
  const drag = dUseRef(null);

  const startDrag = (e) => {
    if (!editing) return;
    if (e.target.closest('.dash-card-tools') || e.target.closest('.resize-handle')) return;
    e.preventDefault();
    drag.current = { type: 'move', sx: e.clientX, sy: e.clientY, ox: item.x, oy: item.y };
    document.body.style.cursor = 'grabbing';
    window.addEventListener('mousemove', onMove);
    window.addEventListener('mouseup', endDrag);
    onChange({ ...item, _drag: true });
  };
  const startResize = (e) => {
    e.preventDefault(); e.stopPropagation();
    drag.current = { type: 'resize', sx: e.clientX, sy: e.clientY, ow: item.w, oh: item.h };
    window.addEventListener('mousemove', onMove);
    window.addEventListener('mouseup', endDrag);
    onChange({ ...item, _resize: true });
  };
  const onMove = (e) => {
    const d = drag.current; if (!d) return;
    // 다른 카드 가장자리 → 자석 후보
    const vc = [0], hc = [0];
    (others || []).forEach((o) => { vc.push(o.x, o.x + o.w); hc.push(o.y, o.y + o.h); });
    const g = [];
    if (d.type === 'move') {
      let nx = d.ox + (e.clientX - d.sx), ny = d.oy + (e.clientY - d.sy);
      nx = Math.max(0, Math.min(nx, (canvasW || 1200) - item.w));
      ny = Math.max(0, ny);
      const sl = snapAxis(nx, vc), sr = snapAxis(nx + item.w, vc);
      if (sl !== null && (sr === null || Math.abs(nx - sl) <= Math.abs(nx + item.w - sr))) { nx = sl; g.push({ dir: 'v', pos: sl }); }
      else if (sr !== null) { nx = sr - item.w; g.push({ dir: 'v', pos: sr }); }
      const st = snapAxis(ny, hc), sb = snapAxis(ny + item.h, hc);
      if (st !== null && (sb === null || Math.abs(ny - st) <= Math.abs(ny + item.h - sb))) { ny = st; g.push({ dir: 'h', pos: st }); }
      else if (sb !== null) { ny = sb - item.h; g.push({ dir: 'h', pos: sb }); }
      setGuides && setGuides(g);
      onChange({ ...item, x: nx, y: ny, _drag: true });
    } else {
      let nw = Math.max(280, d.ow + (e.clientX - d.sx));
      let nh = Math.max(200, d.oh + (e.clientY - d.sy));
      nw = Math.min(nw, (canvasW || 1200) - item.x);
      const sr = snapAxis(item.x + nw, vc);
      if (sr !== null && sr - item.x >= 280) { nw = sr - item.x; g.push({ dir: 'v', pos: sr }); }
      const sb = snapAxis(item.y + nh, hc);
      if (sb !== null && sb - item.y >= 200) { nh = sb - item.y; g.push({ dir: 'h', pos: sb }); }
      setGuides && setGuides(g);
      onChange({ ...item, w: nw, h: nh, _resize: true });
    }
  };
  const endDrag = () => {
    const d = drag.current; drag.current = null;
    document.body.style.cursor = '';
    window.removeEventListener('mousemove', onMove);
    window.removeEventListener('mouseup', endDrag);
    setGuides && setGuides([]);
    onChange((prev) => ({ ...prev, x: snap(prev.x), y: snap(prev.y), w: snap(prev.w), h: snap(prev.h), _drag: false, _resize: false }));
  };

  const period = item.config && item.config.period;
  return (
    <div ref={ref} className={'dash-card' + (editing ? ' editing' : '') + (item._drag ? ' dragging' : '') + (item._resize ? ' resizing' : '')}
      style={{ left: item.x, top: item.y, width: item.w, height: item.h }}>
      <div className="dash-card-head" onMouseDown={startDrag}>
        {editing && <span className="grip">{I.drag}</span>}
        <span className="vic">{VIEW_ICON[item.view]}</span>
        <div style={{ minWidth: 0, flex: 1 }}>
          <div className="t">{item.title}</div>
          <div className="d">{item.desc}</div>
        </div>
        <div className="dash-card-tools">
          <button className="ico-btn" title="설정" onClick={() => onSettings(item)}>{I.settings}</button>
          <button className="ico-btn" title="더보기" onClick={(e) => { e.stopPropagation(); setMenu((m) => !m); }}>{I.more}</button>
        </div>
        {menu && <CardMenu comp={item} onClose={() => setMenu(false)} onSettings={() => onSettings(item)}
          onChangeView={(v) => onChange({ ...item, view: v })} onDuplicate={() => onDuplicate(item)} onRemove={() => onRemove(item)} />}
      </div>
      {period && <div className="dash-card-period">{I.calendar}{period}</div>}
      <div className="dash-card-body">
        <ComponentView comp={item} />
      </div>
      {editing && <div className="resize-handle" onMouseDown={startResize}>
        <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round"><path d="M22 22L12 22M22 22L22 12M22 22L9 9"></path></svg>
      </div>}
    </div>
  );
}

// 컴포넌트 추가 drawer
function AddDrawer({ library, onAdd, onClose, placedIds }) {
  const [q, setQ] = dUseState('');
  const filtered = library.filter((c) => c.title.includes(q) || c.source.includes(q));
  return (
    <>
      <div className="add-drawer-scrim" onClick={onClose}></div>
      <div className="add-drawer">
        <div className="add-drawer-head">
          <span style={{ color: 'var(--blue-600)', display: 'flex' }}>{I.layers}</span>
          <h3>컴포넌트 추가</h3>
          <button className="x" onClick={onClose}>{I.x}</button>
        </div>
        <div className="add-drawer-search">
          <input className="input" placeholder="컴포넌트 검색…" value={q} onChange={(e) => setQ(e.target.value)} />
        </div>
        <div className="add-drawer-list">
          {filtered.map((c) => {
            const added = placedIds.includes(c.id);
            return (
              <div className={'add-comp-card' + (added ? ' added' : '')} key={c.id}>
                <span className="vic">{VIEW_ICON[c.view]}</span>
                <div className="info">
                  <div className="t">{c.title}</div>
                  <div className="m">{({ bar: '막대', line: '선', pie: '파이', table: '표' })[c.view]} · {c.source}</div>
                </div>
                <button className="add-btn" onClick={() => onAdd(c)} title={added ? '이미 추가됨 (다시 추가 가능)' : '추가'}>{added ? I.check : I.plus}</button>
              </div>
            );
          })}
          {filtered.length === 0 && <div style={{ textAlign: 'center', color: 'var(--ink-3)', fontSize: 13, padding: 30 }}>검색 결과가 없습니다.</div>}
        </div>
      </div>
    </>
  );
}

function DashboardView({ library, dashboards, activeId, onSelectDash, items, setItems, editing, setEditing, openSettings, push, showAdd, setShowAdd }) {
  const scrollRef = dUseRef(null);
  const [canvasW, setCanvasW] = dUseState(1200);
  const [guides, setGuides] = dUseState([]);
  dUseEffect(() => {
    const update = () => { if (scrollRef.current) setCanvasW(scrollRef.current.clientWidth - 44); };
    update();
    window.addEventListener('resize', update);
    return () => window.removeEventListener('resize', update);
  }, []);

  const maxBottom = items.reduce((m, it) => Math.max(m, it.y + it.h), 0);
  const canvasH = Math.max(maxBottom + 80, 600);

  const updateItem = (id, updater) => {
    setItems((arr) => arr.map((it) => it.id === id ? (typeof updater === 'function' ? updater(it) : updater) : it));
  };

  const addComponent = (c) => {
    const id = c.id + '-' + Date.now().toString(36).slice(-4);
    const ny = items.length ? snap(items.reduce((m, it) => Math.max(m, it.y + it.h), 0)) + GRID : 0;
    setItems((arr) => [...arr, { ...c, id, srcId: c.id, x: 0, y: ny, w: 560, h: 360 }]);
    push(`'${c.title}' 컴포넌트를 추가했어요`);
  };

  const placedSourceIds = items.map((it) => it.srcId).filter(Boolean);

  return (
    <div className="dash-view">
      <div className="dash-toolbar">
        <div className="dash-tabs">
          {dashboards.map((d) => (
            <button key={d.id} className={'dash-tab' + (d.id === activeId ? ' active' : '')} onClick={() => onSelectDash(d.id)}>
              {d.star && <span className="star">★</span>}{d.name}
            </button>
          ))}
          <button className="dash-tab" style={{ color: 'var(--ink-4)' }} title="새 대시보드" onClick={() => push('데모에서는 새 대시보드 생성을 제공하지 않아요')}>{I.plus}</button>
        </div>
        <div className="dash-meta">컴포넌트 {items.length}개</div>
        <div style={{ flex: 1 }}></div>
        <button className={'btn ' + (editing ? 'btn-primary' : 'btn-ghost')} onClick={() => setEditing(!editing)}>
          {editing ? <>{I.check}편집 완료</> : <>{I.drag}레이아웃 편집</>}
        </button>
        <button className="btn btn-ghost" onClick={() => setShowAdd(true)}>{I.plus}컴포넌트 추가</button>
      </div>

      <div className="dash-canvas-scroll" ref={scrollRef}>
        {items.length === 0 ? (
          <div className="dash-empty">
            <div className="ic">{I.grid}</div>
            <h3>아직 컴포넌트가 없어요</h3>
            <p>Smart BI에서 저장한 컴포넌트를 추가해 나만의 대시보드를 구성해 보세요.</p>
            <button className="btn btn-primary" onClick={() => setShowAdd(true)}>{I.plus}컴포넌트 추가</button>
          </div>
        ) : (
          <div className={'dash-canvas' + (editing ? ' editing' : '')} style={{ width: canvasW + 44, height: canvasH, position: 'relative' }}>
            <div className="dash-grid-bg"></div>
            {editing && guides.map((g, i) => (
              <div key={i} className={'snap-guide ' + g.dir} style={g.dir === 'v' ? { left: g.pos } : { top: g.pos }}></div>
            ))}
            {items.map((it) => (
              <DashCard key={it.id} item={it} others={items.filter((x) => x.id !== it.id)} editing={editing} canvasW={canvasW} setGuides={setGuides}
                onChange={(u) => updateItem(it.id, u)}
                onSettings={openSettings}
                onRemove={(c) => { setItems((arr) => arr.filter((x) => x.id !== c.id)); push('컴포넌트를 제거했어요'); }}
                onDuplicate={(c) => { const id = c.id + '-copy' + Date.now().toString(36).slice(-3); setItems((arr) => [...arr, { ...c, id, x: snap(c.x + GRID), y: snap(c.y + GRID) }]); push('컴포넌트를 복제했어요'); }} />
            ))}
          </div>
        )}
      </div>

      {showAdd && <AddDrawer library={library} placedIds={placedSourceIds} onClose={() => setShowAdd(false)} onAdd={addComponent} />}
    </div>
  );
}

window.DashboardView = DashboardView;
