/**
 * Main Application Bootstrap & UI Controller
 */
document.addEventListener("DOMContentLoaded", async () => {
  const state = window.appState;
  const player = new window.KTVPlayer(state);
  const timeline = new window.KTVTimeline(state, player);
  const recorder = new window.KTVRecorder(state, player);
  const styleEditor = new window.StyleEditor(state);
  const titleCardEditor = new window.TitleCardEditor(state, player);
  const exportDialog = new window.ExportDialog(state);

  window.styleEditor = styleEditor;
  window.titleCardEditor = titleCardEditor;

  // 1. Automatically load user's previously saved settings from disk & localStorage
  await state.loadSavedSettings();

  // 2. Restore custom font if previously uploaded
  if (state.style.customFontUrl && state.style.customFontName) {
    try {
      const fontFace = new FontFace(state.style.customFontName, `url("${state.style.customFontUrl}")`);
      await fontFace.load();
      document.fonts.add(fontFace);
    } catch (e) {
      console.log("Could not restore custom font:", e);
    }
  }

  // Global blur on any button click so buttons never retain focus or steal Space/Enter
  document.addEventListener("click", (e) => {
    const btn = e.target.closest("button");
    if (btn) {
      btn.blur();
    }
  });

  // Initialize UI controls
  initInspectorTabs();
  initPanelResizer(player, timeline);
  initBottomModeToggle(state, recorder);
  initVideoControls(player);
  initHeaderActions(state, player, timeline);
  initLyricsManager(state, player);

  // Auto-load demo on first launch for immediate testing experience, preserving user's saved settings
  loadDemoProject(state, player, timeline);
});

function initPanelResizer(player, timeline) {
  const resizer = document.getElementById("panel-resizer-h");
  const inspector = document.querySelector(".inspector-panel");
  if (!resizer || !inspector) return;

  // Restore saved width from localStorage if present
  const savedWidth = localStorage.getItem("ktv_inspector_width");
  if (savedWidth) {
    const w = parseInt(savedWidth, 10);
    if (!isNaN(w) && w >= 460 && w <= window.innerWidth * 0.8) {
      inspector.style.width = `${w}px`;
      if (player && typeof player.resizeCanvas === "function") {
        player.resizeCanvas();
      }
    }
  }

  let isDragging = false;
  let startX = 0;
  let startWidth = 0;

  resizer.addEventListener("mousedown", (e) => {
    e.preventDefault();
    isDragging = true;
    startX = e.clientX;
    startWidth = inspector.getBoundingClientRect().width;
    resizer.classList.add("dragging");
    document.body.style.cursor = "col-resize";
    document.body.style.userSelect = "none";
  });

  window.addEventListener("mousemove", (e) => {
    if (!isDragging) return;
    // Dragging left widens the right inspector panel
    const deltaX = startX - e.clientX;
    const minW = 460;
    const maxW = Math.floor(window.innerWidth * 0.75);
    const newWidth = Math.max(minW, Math.min(maxW, startWidth + deltaX));
    inspector.style.width = `${newWidth}px`;

    if (player && typeof player.resizeCanvas === "function") {
      player.resizeCanvas();
    }
    if (timeline && typeof timeline.resizeCanvas === "function") {
      timeline.resizeCanvas();
    }
  });

  window.addEventListener("mouseup", () => {
    if (isDragging) {
      isDragging = false;
      resizer.classList.remove("dragging");
      document.body.style.cursor = "";
      document.body.style.userSelect = "";
      const finalWidth = Math.round(inspector.getBoundingClientRect().width);
      localStorage.setItem("ktv_inspector_width", finalWidth);
      if (player && typeof player.resizeCanvas === "function") {
        player.resizeCanvas();
      }
      if (timeline && typeof timeline.resizeCanvas === "function") {
        timeline.resizeCanvas();
      }
    }
  });
}

function initInspectorTabs() {
  const tabs = document.querySelectorAll(".tab-btn");
  tabs.forEach(tab => {
    tab.addEventListener("click", () => {
      tabs.forEach(t => t.classList.remove("active"));
      tab.classList.add("active");

      const targetId = tab.getAttribute("data-tab");
      document.querySelectorAll(".tab-content").forEach(tc => {
        tc.classList.remove("active");
      });
      const targetContent = document.getElementById(targetId);
      if (targetContent) targetContent.classList.add("active");
    });
  });
}

function initBottomModeToggle(state, recorder) {
  const btnTimeline = document.getElementById("mode-btn-timeline");
  const btnTap = document.getElementById("mode-btn-tap");
  const viewTimeline = document.querySelector(".timeline-view");
  const viewTap = document.querySelector(".tap-mode-view");

  if (btnTimeline && btnTap) {
    btnTimeline.addEventListener("click", () => {
      state.currentMode = "timeline";
      btnTimeline.classList.add("active");
      btnTap.classList.remove("active");
      viewTimeline.classList.add("active");
      viewTap.classList.remove("active");
      btnTimeline.blur();
    });

    btnTap.addEventListener("click", () => {
      state.currentMode = "tap";
      btnTap.classList.add("active");
      btnTimeline.classList.remove("active");
      viewTap.classList.add("active");
      viewTimeline.classList.remove("active");
      recorder.updateHUD();
      btnTap.blur();
    });
  }
}

function initVideoControls(player) {
  const btnPlay = document.getElementById("btn-play-toggle");
  const btnRewind = document.getElementById("btn-rewind-5s");
  const btnForward = document.getElementById("btn-forward-5s");
  const speedSelect = document.getElementById("player-playback-speed");

  if (btnPlay) {
    btnPlay.addEventListener("click", () => {
      player.togglePlay();
      btnPlay.blur();
    });
    player.state.on("playStateChange", (isPlaying) => {
      btnPlay.innerText = isPlaying ? "⏸ 暫停" : "▶ 播放";
    });
  }

  if (btnRewind) btnRewind.addEventListener("click", () => { player.seek(player.video.currentTime - 5); btnRewind.blur(); });
  if (btnForward) btnForward.addEventListener("click", () => { player.seek(player.video.currentTime + 5); btnForward.blur(); });

  if (speedSelect) {
    speedSelect.addEventListener("change", (e) => {
      player.setPlaybackRate(parseFloat(e.target.value));
      speedSelect.blur();
    });
  }
}

function initHeaderActions(state, player, timeline) {
  // Demo button
  const btnDemo = document.getElementById("btn-load-demo");
  if (btnDemo) {
    btnDemo.addEventListener("click", () => loadDemoProject(state, player, timeline));
  }

  // Project Title UI helper
  function updateProjectTitleUI(title) {
    const badge = document.getElementById("project-title-badge");
    if (badge) {
      badge.innerText = title || "未命名專案";
      badge.title = `目前專案名稱: ${title || '未命名'} (點擊可重新命名)`;
    }
    const exportFilenameInput = document.getElementById("export-filename");
    if (exportFilenameInput && title) {
      exportFilenameInput.value = `${title}_伴唱.mp4`;
    }
  }

  const titleBadge = document.getElementById("project-title-badge");
  if (titleBadge) {
    titleBadge.addEventListener("click", () => {
      const current = state.projectTitle || "未命名專案";
      const val = prompt("請輸入專案名稱：", current);
      if (val !== null && val.trim()) {
        state.projectTitle = val.trim();
        updateProjectTitleUI(state.projectTitle);
      }
    });
  }

  // Import video file button
  const videoFileInput = document.getElementById("video-file-input");
  const btnImportVideo = document.getElementById("btn-import-video");
  if (btnImportVideo && videoFileInput) {
    btnImportVideo.addEventListener("click", () => videoFileInput.click());
    videoFileInput.addEventListener("change", async (e) => {
      const file = e.target.files[0];
      if (!file) return;

      // ★ 預設專案名稱為 MP4 影片的檔名（去除副檔名）
      const rawName = file.name || "";
      const baseName = rawName.replace(/\.[^/.]+$/, "").trim();
      if (baseName) {
        state.videoOriginalName = baseName;
        state.projectTitle = baseName;
        updateProjectTitleUI(baseName);
      }

      const formData = new FormData();
      formData.append("file", file);

      btnImportVideo.innerText = "⏳ 上傳影音中...";
      try {
        const res = await fetch("/api/upload-video", {
          method: "POST",
          body: formData
        });
        const data = await res.json();
        btnImportVideo.innerText = "🎬 匯入 MP4 影片";

        state.videoUrl = data.videoUrl;
        state.videoLocalPath = data.localPath;
        player.video.src = data.videoUrl;
        player.video.load();

        // Extract waveform
        loadWaveform(state, data.localPath, timeline);
      } catch (err) {
        alert("上傳失敗: " + err);
        btnImportVideo.innerText = "🎬 匯入 MP4 影片";
      }
    });
  }

  // Save project (.ktv JSON file)
  const btnSave = document.getElementById("btn-save-project");
  if (btnSave) {
    btnSave.addEventListener("click", () => {
      // 1. Ensure all current inspector controls (Style, Layout, Title Cards) are freshly synchronized to state
      if (window.styleEditor && typeof window.styleEditor.syncToState === "function") {
        window.styleEditor.syncToState();
      }
      if (window.titleCardEditor && typeof window.titleCardEditor.syncToState === "function") {
        window.titleCardEditor.syncToState();
      }

      // ★ 保存專案時彈出名稱輸入框，預設為 MP4 檔名或當前專案名稱
      const defaultName = state.projectTitle || state.videoOriginalName || "KTV_Project";
      const enteredName = prompt("請輸入保存的專案名稱：", defaultName);
      if (enteredName === null) return; // 使用者按取消

      const finalName = enteredName.trim() || defaultName;
      state.projectTitle = finalName;
      updateProjectTitleUI(finalName);

      // Persist to current user settings as well
      state.saveSettings();

      const projectData = {
        version: "1.0.5",
        title: finalName,
        videoUrl: state.videoUrl,
        videoLocalPath: state.videoLocalPath,
        videoOriginalName: state.videoOriginalName,
        style: JSON.parse(JSON.stringify(state.style)),
        titleCards: JSON.parse(JSON.stringify(state.style.titleCards || {})),
        sentences: state.sentences
      };
      const blob = new Blob([JSON.stringify(projectData, null, 2)], { type: "application/json" });
      const url = URL.createObjectURL(blob);
      const a = document.createElement("a");
      a.href = url;
      a.download = `${finalName}.ktv`;
      document.body.appendChild(a);
      a.click();
      document.body.removeChild(a);
      URL.revokeObjectURL(url);
    });
  }

  // Open project file
  const projectFileInput = document.getElementById("project-file-input");
  const btnOpenProject = document.getElementById("btn-open-project");
  if (btnOpenProject && projectFileInput) {
    btnOpenProject.addEventListener("click", () => projectFileInput.click());
    projectFileInput.addEventListener("change", (e) => {
      const file = e.target.files[0];
      if (!file) return;
      const reader = new FileReader();
      reader.onload = (evt) => {
        try {
          const data = JSON.parse(evt.target.result);
          state.setProjectData(data);
          if (data.title) {
            updateProjectTitleUI(data.title);
          }
          if (data.videoUrl) {
            player.video.src = data.videoUrl;
            player.video.load();
            if (data.videoLocalPath) {
              loadWaveform(state, data.videoLocalPath, timeline);
            }
          }
          // Redraw canvas frame immediately with newly loaded styles & title cards
          if (player && typeof player.drawCurrentFrame === "function") {
            player.drawCurrentFrame();
          }
          alert("專案讀取成功！已同步還原所有樣式、排版位置與片頭片尾字卡。");
        } catch (err) {
          alert("專案檔案格式錯誤: " + err);
        }
      };
      reader.readAsText(file);
      projectFileInput.value = "";
    });
  }
}

async function loadDemoProject(state, player, timeline) {
  try {
    const res = await fetch("/api/demo");
    const demo = await res.json();

    // Preserve user's custom saved styles (colors, positions, fonts, etc.)
    const saved = state.getSavedStyle();
    if (saved && Object.keys(saved).length > 0) {
      demo.style = { ...demo.style, ...saved };
    }

    demo.title = demo.title || "窗外雨水 (伴唱範例)";
    state.setProjectData(demo);
    const badge = document.getElementById("project-title-badge");
    if (badge) {
      badge.innerText = demo.title;
      badge.title = `目前專案名稱: ${demo.title} (點擊可重新命名)`;
    }
    const exportFilenameInput = document.getElementById("export-filename");
    if (exportFilenameInput) {
      exportFilenameInput.value = `${demo.title}_伴唱.mp4`;
    }

    player.video.src = demo.videoUrl;
    player.video.load();

    if (demo.videoLocalPath) {
      loadWaveform(state, demo.videoLocalPath, timeline);
    }
  } catch (e) {
    console.log("Demo load error:", e);
  }
}

async function loadWaveform(state, videoPath, timeline) {
  try {
    const res = await fetch("/api/waveform", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ videoPath: videoPath })
    });
    const data = await res.json();
    state.waveformData = data.peaks || [];
    state.videoDuration = data.duration || state.videoDuration;
    timeline.resizeCanvas();
  } catch (e) {
    console.log("Waveform load error:", e);
  }
}

function initLyricsManager(state, player) {
  const modal = document.getElementById("lyrics-modal");
  const btnOpenModal = document.getElementById("btn-import-lyrics-modal");
  const btnCloseModal = document.getElementById("btn-close-lyrics-modal");
  const btnParse = document.getElementById("btn-parse-lyrics");
  const lyricsTextarea = document.getElementById("lyrics-input-textarea");
  const lyricsFileInput = document.getElementById("lyrics-file-input");

  if (btnOpenModal && modal) {
    btnOpenModal.addEventListener("click", () => modal.classList.add("open"));
    btnCloseModal.addEventListener("click", () => modal.classList.remove("open"));
  }

  if (lyricsFileInput && lyricsTextarea) {
    lyricsFileInput.addEventListener("change", (e) => {
      const file = e.target.files[0];
      if (!file) return;
      const reader = new FileReader();
      reader.onload = (evt) => {
        lyricsTextarea.value = evt.target.result;
      };
      reader.readAsText(file);
    });
  }

  if (btnParse) {
    btnParse.addEventListener("click", async () => {
      const text = lyricsTextarea.value.trim();
      if (!text) {
        alert("請輸入或貼上歌詞！");
        return;
      }
      const defaultSinger = document.getElementById("lyrics-default-singer")?.value || state.style.defaultSinger || "duet";
      
      // Update global style with selected default singer
      state.updateStyle({ defaultSinger });

      // 1. Instant client-side parsing (guarantees immediate correct singer assignment)
      const clientSentences = parseLyricsClientSide(text, defaultSinger);
      state.setSentences(clientSentences);
      state.activeSentenceIndex = 0;
      state.activeWordIndex = 0;

      modal.classList.remove("open");
      renderSentenceList(state, player);

      // 2. Also sync with backend
      try {
        const res = await fetch("/api/parse-lyrics", {
          method: "POST",
          headers: { "Content-Type": "application/json" },
          body: JSON.stringify({ text, defaultSinger })
        });
        if (res.ok) {
          const data = await res.json();
          if (data.sentences && data.sentences.length > 0) {
            state.setSentences(data.sentences);
            renderSentenceList(state, player);
          }
        }
      } catch (e) {
        console.log("Backend lyrics parse sync:", e);
      }
    });
  }

  state.on("sentencesChanged", () => renderSentenceList(state, player));
}

function parseLyricsClientSide(text, defaultSinger = "duet") {
  const lines = text.trim().split("\n");
  const sentences = [];
  let currentSinger = (defaultSinger || "duet").toLowerCase();
  if (!["male", "female", "duet"].includes(currentSinger)) {
    currentSinger = "duet";
  }

  const tagRegex = /^[\[【(（](男|女|合)(?:唱)?[\]】)）][:：]?\s*|^([男女合])(?:唱)?[:：/]\s*/;
  let pendingPrefix = "";

  for (const rawLine of lines) {
    let line = rawLine.trim();
    if (!line) continue;

    let prefix = pendingPrefix || "";
    pendingPrefix = "";

    // Check singer tag
    const match = line.match(tagRegex);
    if (match) {
      const singerChar = match[1] || match[2];
      if (singerChar === "男") {
        currentSinger = "male";
        prefix = "男：";
      } else if (singerChar === "女") {
        currentSinger = "female";
        prefix = "女：";
      } else if (singerChar === "合") {
        currentSinger = "duet";
        prefix = "合：";
      }

      line = line.replace(tagRegex, "").trim();
    }

    if (!line) {
      if (prefix) {
        pendingPrefix = prefix;
      }
      continue;
    }

    const singer = currentSinger;
    const tokens = line.match(/[\u4e00-\u9fff\u3400-\u4dbf\u3040-\u30ff\uac00-\ud7af]|[a-zA-Z0-9]+|[^\s\w]/g) || line.split("");
    const words = tokens.map(t => ({
      text: t,
      startTime: 0.0,
      endTime: 0.0
    }));

    sentences.push({
      id: "s_" + Math.random().toString(36).substring(2, 9),
      singer: singer,
      prefix: prefix,
      text: line,
      words: words,
      hasCountdown: false
    });
  }

  return sentences;
}

function renderSentenceList(state, player) {
  const container = document.getElementById("sentence-list-container");
  if (!container) return;

  const savedScroll = container.scrollTop;

  const sentences = state.sentences || [];
  if (sentences.length === 0) {
    container.innerHTML = `<div style="color: var(--text-dim); text-align: center; padding: 40px 0;">尚未匯入歌詞，請點擊上方「匯入歌詞」</div>`;
    return;
  }

  container.innerHTML = "";
  sentences.forEach((s, idx) => {
    const isLine1 = (idx % 2 === 0);
    const words = s.words || [];
    const start = words.length > 0 ? (words[0].startTime || 0) : 0;
    const end = words.length > 0 ? (words[words.length - 1].endTime || 0) : 0;
    const hasTiming = (end > start);

    const div = document.createElement("div");
    div.className = `sentence-item ${state.activeSentenceIndex === idx ? 'active' : ''}`;

    const singerBadgeClass = s.singer === 'male' ? 'male' : s.singer === 'female' ? 'female' : 'duet';
    const singerLabel = s.singer === 'male' ? '男唱' : s.singer === 'female' ? '女唱' : '合唱';

    div.innerHTML = `
      <div class="sentence-item-header">
        <div>
          <span style="font-weight: bold; color: var(--text-muted); margin-right: 6px;">#${idx + 1}</span>
          <span style="font-size: 0.75rem; color: ${isLine1 ? 'var(--accent-cyan)' : 'var(--accent-pink)'}">【${isLine1 ? '第 1 行' : '第 2 行'}】</span>
        </div>
        <div class="ctrl-group">
          <label class="countdown-check-label ${s.hasCountdown ? 'checked' : ''}" title="勾選此句進歌前顯示倒數提示點 (● ● ● ●)">
            <input type="checkbox" class="sentence-countdown-checkbox" ${s.hasCountdown ? 'checked' : ''}>
            <span>提示點</span>
          </label>
          <span class="singer-badge ${singerBadgeClass}" title="點擊切換演唱者 (男/女/合)">${singerLabel}</span>
          <button class="btn btn-sm btn-cascade-singer" title="將【${singerLabel}】向下套用至後續所有句子" style="padding: 2px 6px; font-size: 0.75rem;">⏬ 順延</button>
          <button class="btn btn-sm btn-edit-sentence" title="修改此句歌詞 (亦可直接在歌詞上連按兩下)" style="padding: 2px 7px; font-size: 0.75rem; background: rgba(0, 210, 255, 0.15); border: 1px solid var(--accent-cyan); color: var(--accent-cyan);">✏️ 修改</button>
          <button class="btn btn-sm btn-tap-sentence" title="切換至此句打點">🎯 打點</button>
        </div>
      </div>
      <div class="sentence-text" title="按兩下可修改此句歌詞">${s.text}</div>
      <div class="sentence-meta">
        <span>${words.length} 字</span>
        <span>${hasTiming ? `${start.toFixed(2)}s ~ ${end.toFixed(2)}s` : '<span style="color: #f59e0b">未打點</span>'}</span>
      </div>
    `;

    let isEditing = false;
    let lastClickTime = 0;

    const startInlineEdit = () => {
      if (isEditing) return;
      isEditing = true;
      const originalText = s.text;

      textContainer.innerHTML = `
        <div class="sentence-edit-wrap" style="display: flex; gap: 6px; align-items: center; width: 100%; margin: 3px 0;">
          <input type="text" class="sentence-edit-input" value="${originalText.replace(/"/g, '&quot;')}" placeholder="請輸入歌詞..." style="flex: 1; padding: 6px 10px; font-size: 0.95rem; font-weight: 600; background: #090e17; border: 1.5px solid var(--accent-cyan); border-radius: 6px; color: #ffffff; outline: none; box-shadow: 0 0 10px rgba(0, 210, 255, 0.2);">
          <button type="button" class="btn-save-edit" title="儲存 (Enter)" style="padding: 5px 12px; font-size: 0.8rem; font-weight: 700; background: var(--accent-cyan); color: #06090e; border: none; border-radius: 5px; cursor: pointer;">儲存</button>
          <button type="button" class="btn-cancel-edit" title="取消 (Esc)" style="padding: 5px 10px; font-size: 0.8rem; background: #253347; color: #94a3b8; border: none; border-radius: 5px; cursor: pointer;">取消</button>
        </div>
      `;

      const input = textContainer.querySelector(".sentence-edit-input");
      const btnSave = textContainer.querySelector(".btn-save-edit");
      const btnCancel = textContainer.querySelector(".btn-cancel-edit");

      setTimeout(() => {
        if (input) {
          input.focus();
          input.select();
        }
      }, 30);

      let committed = false;
      const commit = () => {
        if (committed) return;
        committed = true;
        const newText = input.value.trim();
        if (newText && newText !== originalText) {
          state.updateSentenceText(idx, newText);
        } else {
          renderSentenceList(state, player);
        }
      };

      const cancel = () => {
        if (committed) return;
        committed = true;
        renderSentenceList(state, player);
      };

      btnSave.addEventListener("click", (e) => {
        e.stopPropagation();
        commit();
      });

      btnCancel.addEventListener("click", (e) => {
        e.stopPropagation();
        cancel();
      });

      input.addEventListener("keydown", (e) => {
        e.stopPropagation();
        if (e.key === "Enter") {
          e.preventDefault();
          commit();
        } else if (e.key === "Escape") {
          e.preventDefault();
          cancel();
        }
      });

      input.addEventListener("click", (e) => e.stopPropagation());
      input.addEventListener("dblclick", (e) => e.stopPropagation());
    };

    // Render interactive character chips in sentence text
    const textContainer = div.querySelector(".sentence-text");
    textContainer.innerHTML = "";

    // Show prefix badge if present
    if (s.prefix) {
      const pfxSpan = document.createElement("span");
      const pfxClass = s.singer === 'male' ? 'male' : s.singer === 'female' ? 'female' : 'duet';
      pfxSpan.className = `singer-badge ${pfxClass}`;
      pfxSpan.style.marginRight = "8px";
      pfxSpan.style.cursor = "default";
      pfxSpan.innerText = s.prefix;
      pfxSpan.title = "句首演唱者標籤（常駐變色顯示，不計入打點）";
      textContainer.appendChild(pfxSpan);
    }

    words.forEach((w, wIdx) => {
      const charSpan = document.createElement("span");
      charSpan.className = "lyric-char-tag";
      charSpan.innerText = w.text;
      const wordHasTime = (w.startTime > 0 || w.endTime > 0);
      if (wordHasTime) {
        charSpan.classList.add("has-time");
        charSpan.title = `【${w.text}】${(w.startTime || 0).toFixed(2)}s ~ ${(w.endTime || 0).toFixed(2)}s (點擊跳轉播放，按兩下修改歌詞)`;
      } else {
        charSpan.title = `【${w.text}】(點擊跳轉，按兩下修改歌詞)`;
      }

      charSpan.addEventListener("click", (e) => {
        if (isEditing) return;
        e.stopPropagation();

        // Check rapid double click on character
        const now = Date.now();
        if (now - lastClickTime < 450) {
          lastClickTime = 0;
          startInlineEdit();
          return;
        }
        lastClickTime = now;

        state.activeSentenceIndex = idx;
        state.activeWordIndex = wIdx;
        container.querySelectorAll(".sentence-item").forEach((el, i) => {
          el.classList.toggle("active", i === idx);
        });
        if (w.startTime > 0) {
          player.seek(w.startTime);
        } else if (hasTiming) {
          player.seek(start);
        }
      });

      charSpan.addEventListener("dblclick", (e) => {
        e.preventDefault();
        e.stopPropagation();
        startInlineEdit();
      });

      textContainer.appendChild(charSpan);
    });

    textContainer.addEventListener("dblclick", (e) => {
      e.preventDefault();
      e.stopPropagation();
      startInlineEdit();
    });

    // ✏️ Edit button
    const btnEditSentence = div.querySelector(".btn-edit-sentence");
    if (btnEditSentence) {
      btnEditSentence.addEventListener("click", (e) => {
        e.stopPropagation();
        startInlineEdit();
      });
    }

    // Double click row to edit lyrics
    div.title = "按兩下可修改歌詞";
    div.addEventListener("dblclick", (e) => {
      if (e.target.closest("button, input, label, .singer-badge")) return;
      e.preventDefault();
      e.stopPropagation();
      startInlineEdit();
    });

    // Countdown dot toggle checkbox
    const chkCountdown = div.querySelector(".sentence-countdown-checkbox");
    const lblCountdown = div.querySelector(".countdown-check-label");
    if (chkCountdown) {
      chkCountdown.addEventListener("click", (e) => {
        e.stopPropagation();
      });
      chkCountdown.addEventListener("change", (e) => {
        e.stopPropagation();
        state.updateSentenceCountdown(idx, e.target.checked);
      });
    }
    if (lblCountdown) {
      lblCountdown.addEventListener("click", (e) => {
        e.stopPropagation();
      });
    }

    // Click singer badge to cycle male -> female -> duet
    const badge = div.querySelector(".singer-badge");
    badge.addEventListener("click", (e) => {
      e.stopPropagation();
      const nextSinger = s.singer === "male" ? "female" : s.singer === "female" ? "duet" : "male";
      state.updateSentenceSinger(idx, nextSinger);
    });

    // Cascade singer button to apply this role to all subsequent lines
    const btnCascade = div.querySelector(".btn-cascade-singer");
    if (btnCascade) {
      btnCascade.addEventListener("click", (e) => {
        e.stopPropagation();
        const role = s.singer;
        const roleName = role === "male" ? "男唱" : role === "female" ? "女唱" : "合唱";
        for (let j = idx + 1; j < sentences.length; j++) {
          sentences[j].singer = role;
        }
        state.emit("sentencesChanged", sentences);
      });
    }

    // "🎯 打點" button
    const btnTapSentence = div.querySelector(".btn-tap-sentence");
    btnTapSentence.addEventListener("click", (e) => {
      e.stopPropagation();
      state.activeSentenceIndex = idx;
      state.activeWordIndex = 0;
      // switch to tap mode
      document.getElementById("mode-btn-tap").click();
      // seek video to 2s before sentence if has timing or to start
      if (hasTiming) {
        player.seek(Math.max(0, start - 2));
      }
    });

    // Click row to seek video
    div.addEventListener("click", (e) => {
      if (isEditing) return;
      if (e.target.closest("button, input, label, .singer-badge")) return;

      const now = Date.now();
      if (now - lastClickTime < 450) {
        lastClickTime = 0;
        e.preventDefault();
        e.stopPropagation();
        startInlineEdit();
        return;
      }
      lastClickTime = now;

      state.activeSentenceIndex = idx;
      state.activeWordIndex = 0;
      container.querySelectorAll(".sentence-item").forEach((el, i) => {
        el.classList.toggle("active", i === idx);
      });
      if (hasTiming) {
        player.seek(start);
      }
    });

    container.appendChild(div);
  });

  if (savedScroll > 0) {
    container.scrollTop = savedScroll;
  }
}
