// Main app: Home list, Song view, Editor modal
// (React hooks are imported from window in components.jsx scope; redeclare locally per Babel transpile context)

// ===== Storage =====
const STORAGE_KEY = 'uke.app.v1';

function loadState() {
  try {
    const raw = localStorage.getItem(STORAGE_KEY);
    if (raw) return JSON.parse(raw);
  } catch (e) {}
  return null;
}
function saveState(state) {
  try { localStorage.setItem(STORAGE_KEY, JSON.stringify(state)); } catch (e) {}
}

// ===== Sample data =====
function uid() { return Math.random().toString(36).slice(2, 10); }

const SAMPLE_SONGS = [
  {
    id: 'song-25-21',
    title: '스물다섯, 스물하나',
    artist: '자우림',
    timeSignature: '4/4',
    folder: '발라드',
    favorite: true,
    chords: 'Cadd2/G/F/G/Cadd2/G/F/G/F,G/Am,D7/F,G/Am/F,G/Am,D7/F,G/Cadd2',
    verses: [
      '바람에 날려 꽃이/지는 계절엔/아직도 너의 손을/잡은듯 그런 듯해/그때는 아직 꽃이/아름다운 걸/지금처럼 사무치게/알지 못했어/우~ 너의/향기가/바람에 실려 오네/ /우~ 영원할/줄 알았던/스물다섯 스물하나/ ',
      '그날의 바다는 퍽/다정했었지/아직도 나의 손에/잡힐듯 그런 듯해/부서지는 햇살속에/너와 내가 있어/가슴 시리도록 행복/한 꿈을 꾸었지/우~ 그날의/노래가/바람에 실려 오네/ /우~ 영원할/줄 알았던/지난 날의 너와 나/ ',
    ],
    bars: { 15: { right: 'final' } },
    createdAt: Date.now() - 86400000 * 3,
  },
  {
    id: 'song-bicycle',
    title: '비행기',
    artist: '동요',
    timeSignature: '4/4',
    folder: '동요',
    favorite: false,
    chords: 'C/G7/C/C/C/G7/C/C',
    verses: [
      '떴다 떴다/비행기/날아라/날아라/높이 높이/날아라/우리 비행기/ ',
    ],
    bars: { 7: { right: 'final' } },
    createdAt: Date.now() - 86400000 * 1,
  },
  {
    id: 'song-island',
    title: '섬집 아기',
    artist: '한인현 작사 · 이흥렬 작곡',
    timeSignature: '6/8',
    folder: '동요',
    favorite: true,
    chords: 'C/Am/F/G7/C/Am/Dm,G7/C',
    verses: [
      '엄마가 섬그늘에/굴 따러 가면/아기가 혼자 남아/집을 보다가/바다가 불러주는/자장 노래에/팔 베고 스르르르/잠이 듭니다',
    ],
    bars: { 7: { right: 'final' } },
    createdAt: Date.now() - 86400000 * 2,
  },
];

const DEFAULT_FOLDERS = ['전체', '즐겨찾기', '발라드', '동요'];

function initialState() {
  return {
    songs: SAMPLE_SONGS,
    folders: DEFAULT_FOLDERS,
    activeFolder: '전체',
  };
}

// ===== App =====
function App() {
  const [state, setState] = useState(() => loadState() || initialState());
  const [route, setRoute] = useState({ view: 'home', songId: null }); // 'home' | 'song'
  const [editor, setEditor] = useState(null); // null | { mode: 'create' } | { mode: 'edit', songId }
  const [toast, setToast] = useState(null);

  // ----- Firebase auth + sync -----
  const [user, setUser] = useState(() => (window.fb && window.fb.user) || null);
  const [syncStatus, setSyncStatus] = useState('local'); // 'local' | 'syncing' | 'synced' | 'error'
  const isApplyingRemoteRef = useRef(false);
  const initialSyncDoneRef = useRef(false);
  const saveTimerRef = useRef(null);

  // Listen for Firebase auth state changes
  useEffect(() => {
    const onAuth = (e) => {
      setUser(e.detail.user || null);
      initialSyncDoneRef.current = false;
      if (!e.detail.user) setSyncStatus('local');
    };
    window.addEventListener('fb-auth-changed', onAuth);
    // In case Firebase finished initializing before we mounted
    if (window.fb && window.fb.user) setUser(window.fb.user);
    return () => window.removeEventListener('fb-auth-changed', onAuth);
  }, []);

  // When user logs in, pull cloud state (or upload local if cloud is empty), then subscribe
  useEffect(() => {
    if (!user || !window.fb) return;
    let unsub = null;
    let cancelled = false;
    setSyncStatus('syncing');

    (async () => {
      try {
        const remote = await window.fb.loadOnce();
        if (cancelled) return;
        if (remote && Array.isArray(remote.songs)) {
          // Cloud has data → adopt it
          isApplyingRemoteRef.current = true;
          setState({
            songs: remote.songs,
            folders: remote.folders || DEFAULT_FOLDERS,
            activeFolder: remote.activeFolder || '\uc804\uccb4',
          });
        } else {
          // First-time login → upload current local state
          await window.fb.saveState(state);
        }
        initialSyncDoneRef.current = true;
        setSyncStatus('synced');

        unsub = window.fb.subscribe((data) => {
          if (!data || !Array.isArray(data.songs)) return;
          isApplyingRemoteRef.current = true;
          setState({
            songs: data.songs,
            folders: data.folders || DEFAULT_FOLDERS,
            activeFolder: data.activeFolder || '\uc804\uccb4',
          });
        });
      } catch (e) {
        console.warn('Firestore sync error', e);
        setSyncStatus('error');
      }
    })();

    return () => {
      cancelled = true;
      if (unsub) unsub();
    };
    // eslint-disable-next-line
  }, [user]);

  // Persist on every state change: localStorage always; Firestore if logged in
  useEffect(() => {
    saveState(state);
    if (!user || !window.fb || !initialSyncDoneRef.current) return;
    if (isApplyingRemoteRef.current) {
      // This change came from a remote snapshot — don't echo back
      isApplyingRemoteRef.current = false;
      return;
    }
    if (saveTimerRef.current) clearTimeout(saveTimerRef.current);
    setSyncStatus('syncing');
    saveTimerRef.current = setTimeout(() => {
      window.fb.saveState(state)
        .then(() => setSyncStatus('synced'))
        .catch((e) => { console.warn('Firestore save failed', e); setSyncStatus('error'); });
    }, 600);
  }, [state, user]);

  // simple toast
  const showToast = useCallback((msg) => {
    setToast(msg);
    setTimeout(() => setToast(null), 1800);
  }, []);

  // CRUD
  const upsertSong = (song) => {
    setState(s => {
      const idx = s.songs.findIndex(x => x.id === song.id);
      if (idx >= 0) {
        const next = s.songs.slice();
        next[idx] = song;
        return { ...s, songs: next };
      }
      return { ...s, songs: [song, ...s.songs] };
    });
  };
  const deleteSong = (id) => {
    setState(s => ({ ...s, songs: s.songs.filter(x => x.id !== id) }));
    if (route.songId === id) setRoute({ view: 'home', songId: null });
  };
  const toggleFavorite = (id) => {
    setState(s => ({ ...s, songs: s.songs.map(x => x.id === id ? { ...x, favorite: !x.favorite } : x) }));
  };
  const updateSong = (id, patch) => {
    setState(s => ({ ...s, songs: s.songs.map(x => x.id === id ? { ...x, ...patch } : x) }));
  };
  const addFolder = (name) => {
    setState(s => s.folders.includes(name) ? s : { ...s, folders: [...s.folders, name] });
  };
  const removeFolder = (name) => {
    if (['전체', '즐겨찾기'].includes(name)) return;
    setState(s => ({
      ...s,
      folders: s.folders.filter(f => f !== name),
      songs: s.songs.map(x => x.folder === name ? { ...x, folder: '' } : x),
      activeFolder: s.activeFolder === name ? '전체' : s.activeFolder,
    }));
  };
  const setActiveFolder = (name) => setState(s => ({ ...s, activeFolder: name }));

  const currentSong = useMemo(
    () => state.songs.find(x => x.id === route.songId) || null,
    [state.songs, route.songId]
  );

  return (
    <div className="app">
      {route.view === 'home' && (
        <HomeView
          state={state}
          user={user}
          syncStatus={syncStatus}
          onOpen={(id) => setRoute({ view: 'song', songId: id })}
          onToggleFav={toggleFavorite}
          onAdd={() => setEditor({ mode: 'create' })}
          onSetFolder={setActiveFolder}
          onAddFolder={addFolder}
          onRemoveFolder={removeFolder}
          onEdit={(id) => setEditor({ mode: 'edit', songId: id })}
          onDelete={deleteSong}
          onSetSongFolder={(id, folder) => updateSong(id, { folder })}
          onSignIn={() => window.fb && window.fb.signInGoogle().catch((e) => showToast('\ub85c\uadf8\uc778 \uc2e4\ud328'))}
          onSignOut={() => window.fb && window.fb.signOut()}
        />
      )}
      {route.view === 'song' && currentSong && (
        <SongView
          song={currentSong}
          folders={state.folders}
          onBack={() => setRoute({ view: 'home', songId: null })}
          onEdit={() => setEditor({ mode: 'edit', songId: currentSong.id })}
          onDelete={() => { if (confirm('이 악보를 삭제할까요?')) { deleteSong(currentSong.id); showToast('삭제됨'); } }}
          onToggleFav={() => toggleFavorite(currentSong.id)}
          onUpdate={(patch) => updateSong(currentSong.id, patch)}
        />
      )}

      {editor && (
        <EditorModal
          mode={editor.mode}
          song={editor.mode === 'edit' ? state.songs.find(s => s.id === editor.songId) : null}
          folders={state.folders}
          onClose={() => setEditor(null)}
          onSave={(song) => {
            upsertSong(song);
            setEditor(null);
            showToast(editor.mode === 'create' ? '악보 추가됨' : '저장됨');
            if (editor.mode === 'create') setRoute({ view: 'song', songId: song.id });
          }}
          onAddFolder={addFolder}
        />
      )}

      {toast && <div className="toast">{toast}</div>}
    </div>
  );
}

// ===== Auth UI =====
function AuthButton({ user, onSignIn, onSignOut }) {
  // For anonymous sign-in flow: no UI needed — sync happens automatically.
  if (!user || user.isAnonymous) return null;

  const [menuOpen, setMenuOpen] = useState(false);
  const ref = useRef(null);
  useEffect(() => {
    if (!menuOpen) return;
    const onDoc = (e) => { if (ref.current && !ref.current.contains(e.target)) setMenuOpen(false); };
    setTimeout(() => document.addEventListener('mousedown', onDoc), 0);
    return () => document.removeEventListener('mousedown', onDoc);
  }, [menuOpen]);

  if (!user) {
    return (
      <button className="signin-btn" onClick={onSignIn} aria-label="Google 로그인">
        <svg viewBox="0 0 24 24" width="14" height="14" aria-hidden>
          <path fill="#4285F4" d="M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92a5.06 5.06 0 0 1-2.2 3.32v2.76h3.56c2.08-1.92 3.28-4.74 3.28-8.09z"/>
          <path fill="#34A853" d="M12 23c2.97 0 5.46-.98 7.28-2.66l-3.56-2.76c-.99.66-2.25 1.05-3.72 1.05-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84A11 11 0 0 0 12 23z"/>
          <path fill="#FBBC05" d="M5.84 14.1A6.6 6.6 0 0 1 5.48 12c0-.73.13-1.44.36-2.1V7.07H2.18A11 11 0 0 0 1 12c0 1.78.43 3.46 1.18 4.93z"/>
          <path fill="#EA4335" d="M12 5.38c1.62 0 3.06.56 4.2 1.65l3.15-3.15C17.45 2.1 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07L5.84 9.9C6.71 7.31 9.14 5.38 12 5.38z"/>
        </svg>
        로그인
      </button>
    );
  }

  const initial = (user.displayName || user.email || '?').trim().charAt(0).toUpperCase();
  return (
    <div style={{ position: 'relative' }} ref={ref}>
      <button className="avatar-btn" onClick={() => setMenuOpen(o => !o)} aria-label="계정 메뉴">
        {user.photoURL
          ? <img src={user.photoURL} alt="" referrerPolicy="no-referrer"/>
          : <span>{initial}</span>}
      </button>
      {menuOpen && (
        <div className="dropdown" style={{ right: 0, top: '100%', marginTop: 6, minWidth: 220 }}>
          <div style={{ padding: '8px 10px', borderBottom: '1px solid var(--border)', marginBottom: 4 }}>
            <div style={{ fontSize: 13, fontWeight: 600 }}>{user.displayName || '사용자'}</div>
            <div style={{ fontSize: 11, color: 'var(--ink-3)', marginTop: 1, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{user.email}</div>
          </div>
          <button onClick={() => { setMenuOpen(false); onSignOut(); }}>로그아웃</button>
        </div>
      )}
    </div>
  );
}

function SyncBadge({ status, user }) {
  if (!user) return <span style={{ color: 'var(--ink-3)' }}>연결 중…</span>;
  if (status === 'syncing') return <span style={{ color: 'var(--ink-3)' }}>동기화 중…</span>;
  if (status === 'synced') return <span style={{ color: 'var(--ink-3)' }}>☁︎ 동기화됨</span>;
  if (status === 'error') return <span style={{ color: 'var(--danger)' }}>동기화 오류</span>;
  return null;
}

// ===== Home =====
function HomeView({ state, user, syncStatus, onOpen, onToggleFav, onAdd, onSetFolder, onAddFolder, onRemoveFolder, onEdit, onDelete, onSetSongFolder, onSignIn, onSignOut }) {
  const [query, setQuery] = useState('');
  const [menuFor, setMenuFor] = useState(null);

  const filtered = useMemo(() => {
    let list = state.songs;
    if (state.activeFolder === '즐겨찾기') list = list.filter(s => s.favorite);
    else if (state.activeFolder !== '전체') list = list.filter(s => s.folder === state.activeFolder);
    if (query.trim()) {
      const q = query.trim().toLowerCase();
      list = list.filter(s => s.title.toLowerCase().includes(q) || (s.artist || '').toLowerCase().includes(q));
    }
    return list;
  }, [state.songs, state.activeFolder, query]);

  const folderCount = (name) => {
    if (name === '전체') return state.songs.length;
    if (name === '즐겨찾기') return state.songs.filter(s => s.favorite).length;
    return state.songs.filter(s => s.folder === name).length;
  };

  return (
    <>
      <header className="topbar">
        <div className="topbar-inner">
          <h1>
            우쿨렐레 악보
            <small>{state.songs.length}곡 · <SyncBadge status={syncStatus} user={user}/></small>
          </h1>
          <AuthButton user={user} onSignIn={onSignIn} onSignOut={onSignOut}/>
        </div>
      </header>

      <main className="home">
        <div className="search">
          <Icon.Search/>
          <input
            type="text"
            placeholder="제목 또는 아티스트 검색"
            value={query}
            onChange={(e) => setQuery(e.target.value)}
          />
        </div>

        <FolderTabs
          folders={state.folders}
          active={state.activeFolder}
          counts={folderCount}
          onSelect={onSetFolder}
          onAdd={onAddFolder}
          onRemove={onRemoveFolder}
        />

        <div className="song-list">
          {filtered.length === 0 ? (
            <div className="empty-state">
              <Icon.Music/>
              <h3>악보가 없어요</h3>
              <p>오른쪽 아래 + 버튼을 눌러 첫 악보를 추가해보세요</p>
            </div>
          ) : filtered.map((song, i) => (
            <SongRow
              key={song.id}
              song={song}
              index={i + 1}
              onOpen={() => onOpen(song.id)}
              onToggleFav={() => onToggleFav(song.id)}
              onMenu={(e) => { e.stopPropagation(); setMenuFor(song.id); }}
              menuOpen={menuFor === song.id}
              onMenuClose={() => setMenuFor(null)}
              folders={state.folders}
              onEdit={() => onEdit(song.id)}
              onDelete={() => { if (confirm(`"${song.title}" 악보를 삭제할까요?`)) onDelete(song.id); }}
              onSetFolder={(folder) => onSetSongFolder(song.id, folder)}
            />
          ))}
        </div>
      </main>

      <button className="fab" onClick={onAdd} aria-label="악보 추가">
        <Icon.Plus/>
      </button>
    </>
  );
}

function FolderTabs({ folders, active, counts, onSelect, onAdd, onRemove }) {
  return (
    <div className="folder-tabs" role="tablist">
      {folders.map(f => (
        <button
          key={f}
          className={`folder-tab ${active === f ? 'is-active' : ''}`}
          onClick={() => onSelect(f)}
          onContextMenu={(e) => {
            if (['전체', '즐겨찾기'].includes(f)) return;
            e.preventDefault();
            if (confirm(`'${f}' 폴더를 삭제할까요? (악보는 유지)`)) onRemove(f);
          }}
        >
          {f === '즐겨찾기' ? <Icon.Star style={{ width: 12, height: 12 }}/> : null}
          {f}
          <span className="count">{counts(f)}</span>
        </button>
      ))}
      <button
        className="folder-tab add"
        onClick={() => {
          const name = prompt('새 폴더 이름');
          if (name && name.trim()) onAdd(name.trim());
        }}
      >
        <Icon.Plus style={{ width: 14, height: 14 }}/>
      </button>
    </div>
  );
}

function SongRow({ song, index, onOpen, onToggleFav, onMenu, menuOpen, onMenuClose, folders, onEdit, onDelete, onSetFolder }) {
  const menuRef = useRef(null);
  useEffect(() => {
    if (!menuOpen) return;
    const onDoc = (e) => { if (menuRef.current && !menuRef.current.contains(e.target)) onMenuClose(); };
    setTimeout(() => document.addEventListener('mousedown', onDoc), 0);
    return () => document.removeEventListener('mousedown', onDoc);
  }, [menuOpen]);

  const folderOptions = folders.filter(f => !['전체', '즐겨찾기'].includes(f));

  return (
    <div className="song-row" onClick={onOpen} role="button" style={{ position: 'relative' }}>
      <span className="num">{String(index).padStart(2, '0')}</span>
      <div className="info">
        <div className="title">{song.title}</div>
        <div className="meta">
          {song.artist || '아티스트 미상'}
          {song.timeSignature && <span className="key-tag">{song.timeSignature}</span>}
          {song.folder && <span className="key-tag" style={{ background: 'transparent', border: '1px solid var(--border)' }}>{song.folder}</span>}
        </div>
      </div>
      <button
        className={`star ${song.favorite ? 'is-on' : ''}`}
        onClick={(e) => { e.stopPropagation(); onToggleFav(); }}
        aria-label="즐겨찾기"
      >
        {song.favorite ? <Icon.Star style={{ width: 18, height: 18 }}/> : <Icon.StarOutline style={{ width: 18, height: 18 }}/>}
      </button>
      <button
        className="icon-btn"
        onClick={onMenu}
        aria-label="더보기"
        style={{ width: 32, height: 32 }}
      >
        <Icon.More/>
      </button>
      {menuOpen && (
        <div ref={menuRef} className="dropdown" style={{ right: 0, top: '100%', marginTop: 4 }} onClick={(e) => e.stopPropagation()}>
          <button onClick={() => { onEdit(); onMenuClose(); }}><Icon.Edit/> 편집</button>
          {folderOptions.length > 0 && <>
            <hr/>
            <div style={{ padding: '4px 10px', fontSize: 11, color: 'var(--ink-3)', fontWeight: 600 }}>폴더로 이동</div>
            <button onClick={() => { onSetFolder(''); onMenuClose(); }}>
              <Icon.Folder/> (없음)
            </button>
            {folderOptions.map(f => (
              <button key={f} onClick={() => { onSetFolder(f); onMenuClose(); }}>
                <Icon.Folder/> {f}
              </button>
            ))}
          </>}
          <hr/>
          <button className="danger" onClick={() => { onDelete(); onMenuClose(); }}><Icon.Trash/> 삭제</button>
        </div>
      )}
    </div>
  );
}

// ===== Song View =====
function SongView({ song, folders, onBack, onEdit, onDelete, onToggleFav, onUpdate }) {
  const [transposeBy, setTransposeBy] = useState(0);
  const [popover, setPopover] = useState(null); // { measureIdx, side, rect }

  const chordsArr = useMemo(() => parseChords(song.chords), [song.chords]);
  const verseArrs = useMemo(() => (song.verses || []).map(parseLyrics), [song.verses]);

  // Pad each verse to match measure count
  const paddedVerses = verseArrs.map(v => {
    const out = v.slice();
    while (out.length < chordsArr.length) out.push('');
    return out;
  });
  const safeVerses = paddedVerses.length ? paddedVerses : [Array(chordsArr.length).fill('')];

  const handleBarClick = (measureIdx, side, anchorEl) => {
    const rect = anchorEl.getBoundingClientRect();
    setPopover({ measureIdx, side, rect });
  };

  const setBar = (measureIdx, side, value) => {
    const next = { ...(song.bars || {}) };
    const entry = { ...(next[measureIdx] || {}) };
    if (value === undefined) {
      delete entry[side];
    } else {
      entry[side] = value;
    }
    if (Object.keys(entry).length === 0) {
      delete next[measureIdx];
    } else {
      next[measureIdx] = entry;
    }
    onUpdate({ bars: next });
  };

  const addVerse = () => {
    const placeholder = Array(chordsArr.length).fill('').join('/');
    onUpdate({ verses: [...(song.verses || []), placeholder] });
  };
  const removeVerse = (i) => {
    if ((song.verses || []).length <= 1) return;
    onUpdate({ verses: song.verses.filter((_, idx) => idx !== i) });
  };

  const transposeKey = useMemo(() => {
    // Derive a rough "key" by transposing first chord
    const first = chordsArr[0] && chordsArr[0][0];
    if (!first) return null;
    return window.transposeChord(first, transposeBy);
  }, [chordsArr, transposeBy]);

  return (
    <>
      <header className="topbar">
        <div className="topbar-inner">
          <button className="icon-btn" onClick={onBack} aria-label="뒤로">
            <Icon.Back/>
          </button>
          <h1>{song.title}<small>{song.artist}</small></h1>
          <button className={`icon-btn ${song.favorite ? 'is-active' : ''}`} onClick={onToggleFav} aria-label="즐겨찾기">
            {song.favorite ? <Icon.Star/> : <Icon.StarOutline/>}
          </button>
          <button className="icon-btn" onClick={onEdit} aria-label="편집">
            <Icon.Edit/>
          </button>
          <button className="icon-btn" onClick={onDelete} aria-label="삭제">
            <Icon.Trash/>
          </button>
        </div>
      </header>

      <main className="score-view">
        <div className="score-header">
          <h2>{song.title}</h2>
          <div className="artist">{song.artist || '아티스트 미상'}</div>
          <div className="controls">
            <span className="chip"><Icon.Music/>{song.timeSignature || '4/4'}</span>
            {song.folder && <span className="chip"><Icon.Folder/>{song.folder}</span>}
            <div className="transpose">
              <button onClick={() => setTransposeBy(t => t - 1)} aria-label="반음 내림"><Icon.Minus/></button>
              <span className="val">{transposeBy === 0 ? '원곡' : (transposeBy > 0 ? `+${transposeBy}` : transposeBy)}</span>
              <button onClick={() => setTransposeBy(t => t + 1)} aria-label="반음 올림"><Icon.Plus style={{ width: 16, height: 16 }}/></button>
              {transposeBy !== 0 && (
                <button onClick={() => setTransposeBy(0)} aria-label="초기화" title="초기화"><Icon.Reset/></button>
              )}
            </div>
          </div>
        </div>

        <Sheet
          chords={chordsArr}
          verses={safeVerses}
          timeSignature={song.timeSignature}
          bars={song.bars}
          onBarClick={handleBarClick}
          transposeBy={transposeBy}
        />

        <div className="verse-add">
          <button onClick={addVerse}>
            <Icon.Plus style={{ width: 12, height: 12 }}/> 절 추가
          </button>
        </div>

        {/* Verse remove buttons under each verse */}
        {(song.verses || []).length > 1 && (
          <div style={{ marginTop: 8, fontSize: 11, color: 'var(--ink-3)' }}>
            {(song.verses || []).map((_, i) => (
              <span key={i} style={{ marginRight: 12 }}>
                <button
                  onClick={() => { if (confirm(`${i + 1}절 가사를 삭제할까요?`)) removeVerse(i); }}
                  style={{ color: 'var(--ink-3)', textDecoration: 'underline' }}
                >
                  {i + 1}절 삭제
                </button>
              </span>
            ))}
          </div>
        )}
      </main>

      {popover && (
        <BarPopover
          anchorRect={popover.rect}
          side={popover.side}
          value={(song.bars && song.bars[popover.measureIdx] && song.bars[popover.measureIdx][popover.side])}
          onChange={(val) => setBar(popover.measureIdx, popover.side, val)}
          onClose={() => setPopover(null)}
        />
      )}
    </>
  );
}

// ===== Editor Modal =====
function EditorModal({ mode, song, folders, onClose, onSave, onAddFolder }) {
  const [title, setTitle] = useState(song?.title || '');
  const [artist, setArtist] = useState(song?.artist || '');
  const [timeSignature, setTimeSignature] = useState(song?.timeSignature || '4/4');
  const [folder, setFolder] = useState(song?.folder || '');
  const [chordsInput, setChordsInput] = useState(song?.chords || '');
  const [versesInput, setVersesInput] = useState(song?.verses?.length ? song.verses : ['']);

  // Validate chord names
  const unknown = useMemo(() => {
    const arr = parseChords(chordsInput);
    const names = new Set();
    arr.forEach(m => m.forEach(c => { if (c && !window.CHORDS[c]) names.add(c); }));
    return Array.from(names);
  }, [chordsInput]);

  const measureCount = useMemo(() => parseChords(chordsInput).length, [chordsInput]);

  const canSave = title.trim() && chordsInput.trim();

  const submit = () => {
    if (!canSave) return;
    const saved = {
      id: song?.id || uid(),
      title: title.trim(),
      artist: artist.trim(),
      timeSignature: timeSignature.trim() || '4/4',
      folder: folder.trim(),
      favorite: song?.favorite || false,
      chords: chordsInput.trim(),
      verses: versesInput.map(v => v.trim()),
      bars: song?.bars || {},
      createdAt: song?.createdAt || Date.now(),
      updatedAt: Date.now(),
    };
    onSave(saved);
  };

  const otherFolders = folders.filter(f => !['전체', '즐겨찾기'].includes(f));

  return (
    <div className="modal-backdrop" onClick={onClose}>
      <div className="modal" onClick={(e) => e.stopPropagation()}>
        <div className="modal-header">
          <h3>{mode === 'create' ? '새 악보' : '악보 편집'}</h3>
          <button className="icon-btn" onClick={onClose} aria-label="닫기"><Icon.Close/></button>
        </div>

        <div className="modal-body">
          <div className="field-row">
            <div className="field">
              <label>제목</label>
              <input type="text" value={title} onChange={(e) => setTitle(e.target.value)} placeholder="곡 제목" autoFocus/>
            </div>
            <div className="field">
              <label>아티스트</label>
              <input type="text" value={artist} onChange={(e) => setArtist(e.target.value)} placeholder="아티스트"/>
            </div>
          </div>

          <div className="field-row">
            <div className="field">
              <label>박자</label>
              <select value={timeSignature} onChange={(e) => setTimeSignature(e.target.value)}>
                <option value="4/4">4/4</option>
                <option value="3/4">3/4</option>
                <option value="6/8">6/8</option>
                <option value="2/4">2/4</option>
                <option value="12/8">12/8</option>
              </select>
            </div>
            <div className="field">
              <label>폴더</label>
              <select value={folder} onChange={(e) => {
                if (e.target.value === '__new__') {
                  const name = prompt('새 폴더 이름');
                  if (name && name.trim()) { onAddFolder(name.trim()); setFolder(name.trim()); }
                } else {
                  setFolder(e.target.value);
                }
              }}>
                <option value="">(없음)</option>
                {otherFolders.map(f => <option key={f} value={f}>{f}</option>)}
                <option value="__new__">+ 새 폴더…</option>
              </select>
            </div>
          </div>

          <div className="field">
            <label>
              코드
              <span className="hint">마디는 <code>/</code>로 구분, 같은 마디 안 여러 코드는 <code>,</code>로 (4마디씩 자동 줄바꿈)</span>
            </label>
            <textarea
              value={chordsInput}
              onChange={(e) => setChordsInput(e.target.value)}
              placeholder="예: Cadd2/G/F/G/F,G/Am,D7/F,G/Am"
              rows={3}
            />
            {chordsInput && (
              <div style={{ fontSize: 11.5, color: 'var(--ink-3)', marginTop: 6 }}>
                마디 {measureCount}개 · 줄 {Math.ceil(measureCount / 4)}개
                {unknown.length > 0 && (
                  <span style={{ color: 'var(--danger)', marginLeft: 8 }}>
                    알 수 없는 코드: {unknown.join(', ')}
                  </span>
                )}
              </div>
            )}
          </div>

          {versesInput.map((v, i) => (
            <div key={i} className="verse-block">
              <div className="verse-label">
                <label style={{ margin: 0 }}>
                  가사 {i + 1}절
                  {i === 0 && <span className="hint">마디는 <code>/</code>로 구분</span>}
                </label>
                {versesInput.length > 1 && (
                  <button className="remove-verse" onClick={() => setVersesInput(versesInput.filter((_, idx) => idx !== i))}>
                    삭제
                  </button>
                )}
              </div>
              <textarea
                value={v}
                onChange={(e) => {
                  const next = versesInput.slice();
                  next[i] = e.target.value;
                  setVersesInput(next);
                }}
                placeholder={i === 0 ? '예: 바람에 날려 꽃이/지는 계절엔/아직도 너의 손을/잡은듯 그런 듯해' : '2절 가사…'}
                rows={3}
              />
            </div>
          ))}

          <button
            className="add-verse-btn"
            onClick={() => setVersesInput([...versesInput, ''])}
          >
            <Icon.Plus style={{ width: 14, height: 14 }}/> {versesInput.length + 1}절 가사 추가
          </button>

          <div style={{ marginTop: 18, padding: 12, background: 'var(--surface)', borderRadius: 8, fontSize: 12, color: 'var(--ink-2)', lineHeight: 1.6 }}>
            <div style={{ fontWeight: 600, marginBottom: 4 }}>도돌이표·겹세로 등 특수 기호</div>
            저장 후 악보 화면에서 <strong>마디 세로선을 클릭</strong>하면 반복 시작·끝, 마지막 마디, 겹세로 등을 지정할 수 있어요.
          </div>
        </div>

        <div className="modal-footer">
          <button className="btn" onClick={onClose}>취소</button>
          <button className="btn primary" onClick={submit} disabled={!canSave}>
            {mode === 'create' ? '추가' : '저장'}
          </button>
        </div>
      </div>
    </div>
  );
}

// Mount
ReactDOM.createRoot(document.getElementById('root')).render(<App/>);
