/**
 * 录音详情高级操作组合式函数
 *
 * 包含所有录音相关的复杂操作：
 * - 重处理转写/总结
 * - 转写编辑（文本和 ASR）
 * - 说话人管理
 * - 状态管理
 * - 标记功能（Inbox/Highlight）
 * - 轮询监控
 *
 * 与原代码（main-old.js）相比的【行为差异】，需在迁移/回归时特别注意：
 *
 * 1. 说话人/重处理相关状态改为“依赖注入”（方案 B，2024 重构修复）
 *    - 原代码：speakerMap / highlightedSpeaker / showSpeakerModal /
 *      speakerSuggestions / regenerateSummaryAfterSpeakerUpdate /
 *      asrReprocessOptions 等只在 main.js setup 内部定义一份，方法直接闭包读写。
 *    - 拆分初版：本 composable 内部又创建了一份同名 ref，与 main.js 那份分裂
 *      → “名字不显示 / 高亮不渲染 / 点击外部不收起建议 / 清空对象错误”4 个回归。
 *    - 现行做法：这些 ref 全部由 main.js 顶部定义并作为参数注入，本 composable
 *      内部【不再】创建同名 ref，全部读写注入的那份。openSpeakerModal 写的
 *      speakerMap 与 main.js 的 processedTranscription 读的是同一份 ref。
 *    - 隐式契约：调用方必须传齐 23 个共享 ref（见函数签名），否则首次访问
 *      该 ref 时抛 undefined。当前唯一调用方 main.js 已传齐。
 *
 * 2. highlightedTranscript 实现从“空壳”恢复为完整渲染
 *    - 拆分初版：本 composable 的 highlightedTranscript 只是返回原始 transcription
 *      字符串，不做任何处理 → 模板 v-html="highlightedTranscript" 既不转义
 *      （XSS 风险）、不换行、不高亮/着色。
 *    - 现行做法：替换为 main-old.js:424-504 的完整实现：HTML 转义防注入、
 *      \n→<br>、[Speaker]: 包成带 speaker-color-N + speaker-highlight 的 span、
 *      用 speakerMap 的 name 替换显示名。依赖注入的 speakerMap / highlightedSpeaker，
 *      与 openSpeakerModal / highlightSpeakerInTranscript 同源。
 *
 * @param {Object} dependencies - 依赖项
 * @returns {Object} 所有状态和方法
 */

import { ref, computed, nextTick } from "vue";

export function useRecordingOperations({
  // 外部响应式状态
  recordings,
  selectedRecording,
  selectedTab,
  uploadQueue,
  currentlyProcessingFile,
  processingProgress,
  processingMessage,
  progressPopupMinimized,
  progressPopupClosed,
  identifiedSpeakers,
  processedTranscription,
  // 外部方法
  showToast,
  setGlobalError,
  resetCurrentFileProcessingState,
  // 说话人/重处理/编辑相关共享状态（由 main.js 注入，单一数据源）
  speakerMap,
  highlightedSpeaker,
  showSpeakerModal,
  showResetModal,
  showReprocessModal,
  showTextEditorModal,
  showAsrEditorModal,
  editingTranscriptionContent,
  editingSegments,
  availableSpeakers,
  speakerDisplayMap,
  modalSpeakers,
  regenerateSummaryAfterSpeakerUpdate,
  speakerSuggestions,
  loadingSuggestions,
  activeSpeakerInput,
  isAutoIdentifying,
  currentSpeakerGroupIndex,
  speakerGroups,
  recordingToReset,
  reprocessType,
  reprocessRecording,
  asrReprocessOptions,
}) {
  // ==================== 内部响应式状态 ====================
  // 说话人/重处理/编辑相关状态全部由外部注入（见参数列表），不再在此创建，
  // 以保证 main.js 的 computed/handler 与本 composable 的方法读写同一份 ref。

  // --- 轮询监控相关 ---
  const reprocessingPolls = ref(new Map());

  // ==================== 计算属性 ====================

  const identifiedSpeakersInOrder = computed(() => {
    if (!processedTranscription.value?.speakers) return [];
    return processedTranscription.value.speakers;
  });

  const hasSpeakerNames = computed(() => {
    return Object.values(speakerMap.value).some(
      (data) => data.name && data.name.trim() !== ""
    );
  });

  // 纯文本格式转录的高亮渲染：HTML 转义防注入、换行、说话人着色与高亮 span。
  // 【行为差异 2】拆分初版这里只 return 原始 transcription 字符串（无转义、无
  // 高亮、无着色），导致模板 v-html="highlightedTranscript" 出现 XSS 风险 +
  // 高亮失效。现行实现从 main-old.js:424-504 恢复完整逻辑。依赖注入的
  // speakerMap / highlightedSpeaker，保证与 openSpeakerModal、
  // highlightSpeakerInTranscript 操作的是同一份状态。
  const highlightedTranscript = computed(() => {
    if (!selectedRecording.value?.transcription) return "";
    let html = selectedRecording.value.transcription;
    // Escape HTML to prevent injection, but keep it minimal
    html = html.replace(/</g, "<").replace(/>/g, ">");

    // 1. Replace newlines with <br> tags for proper line breaks in HTML
    html = html.replace(/\n/g, "<br>");

    // 2. Get speaker colors from the speakerMap if available
    const speakerColors = {};
    if (speakerMap.value) {
      Object.keys(speakerMap.value).forEach((speaker, index) => {
        speakerColors[speaker] =
          speakerMap.value[speaker].color ||
          `speaker-color-${(index % 8) + 1}`;
      });
    }

    // 3. Wrap each speaker tag in a span for styling and interaction with colors
    html = html.replace(/\[([^\]]+)\]/g, (match, speakerId) => {
      const isHighlighted = speakerId === highlightedSpeaker.value;
      const colorClass = speakerColors[speakerId] || "";
      // Replace speaker ID with name if available
      const displayName = speakerMap.value[speakerId]?.name || speakerId;
      const displayText = `[${displayName}]`;
      // Use a more specific and stylish class structure with color
      return `<span class="speaker-tag ${colorClass} ${
        isHighlighted ? "speaker-highlight" : ""
      }" data-speaker-id="${speakerId}">${displayText}</span>`;
    });

    return html;
  });

  // ==================== 重处理功能 ====================

  const reprocessTranscription = (recordingId) => {
    const recording =
      recordings.value.find((r) => r.id === recordingId) ||
      selectedRecording.value;
    confirmReprocess("transcription", recording);
  };

  const reprocessSummary = (recordingId) => {
    const recording =
      recordings.value.find((r) => r.id === recordingId) ||
      selectedRecording.value;
    confirmReprocess("summary", recording);
  };

  const confirmReprocess = (type, recording) => {
    reprocessType.value = type;
    reprocessRecording.value = recording;
    showReprocessModal.value = true;
  };

  const cancelReprocess = () => {
    showReprocessModal.value = false;
    reprocessType.value = null;
    reprocessRecording.value = null;
  };

  const executeReprocess = async () => {
    if (!reprocessRecording.value || !reprocessType.value) return;

    const recordingId = reprocessRecording.value.id;
    const type = reprocessType.value;

    // Close the modal first
    cancelReprocess();

    if (type === "transcription") {
      await performReprocessTranscription(
        recordingId,
        asrReprocessOptions.value.language,
        asrReprocessOptions.value.min_speakers,
        asrReprocessOptions.value.max_speakers
      );
    } else if (type === "summary") {
      await performReprocessSummary(recordingId);
    }
  };

  const performReprocessTranscription = async (
    recordingId,
    language,
    minSpeakers,
    maxSpeakers
  ) => {
    if (!recordingId) {
      setGlobalError("No recording ID provided for reprocessing.");
      return;
    }

    try {
      const payload = {
        language: language,
        min_speakers: minSpeakers,
        max_speakers: maxSpeakers,
      };

      const response = await fetch(
        `/tool/speakr/recording/${recordingId}/reprocess_transcription`,
        {
          method: "POST",
          headers: { "Content-Type": "application/json" },
          body: JSON.stringify(payload),
        }
      );

      const data = await response.json();
      if (!response.ok)
        throw new Error(
          data.error || "Failed to start transcription reprocessing"
        );

      // Ensure the recording status is properly set to PROCESSING
      if (data.recording && data.recording.status !== "PROCESSING") {
        console.warn(
          `Warning: Reprocess transcription returned unexpected status: ${data.recording.status}`
        );
        data.recording.status = "PROCESSING";
      }

      // Update the recording in the UI
      const index = recordings.value.findIndex((r) => r.id === recordingId);
      if (index !== -1) {
        recordings.value[index] = data.recording;
      }

      // Update selected recording if it's the one being reprocessed
      if (selectedRecording.value?.id === recordingId) {
        selectedRecording.value = data.recording;
      }

      showToast("Transcription reprocessing started", "fa-sync-alt");

      // Switch to Summary tab to show progress
      selectedTab.value = "summary";

      // Show progress modal for reprocessing
      showProgressModalForReprocessing(recordingId, "transcription");

      // Start polling for status updates
      startReprocessingPoll(recordingId);
    } catch (error) {
      console.error("Reprocess Transcription Error:", error);
      setGlobalError(
        `Failed to start transcription reprocessing: ${error.message}`
      );
    }
  };

  const performReprocessSummary = async (recordingId) => {
    if (!recordingId) {
      setGlobalError("No recording ID provided for reprocessing.");
      return;
    }

    try {
      const response = await fetch(
        `/tool/speakr/recording/${recordingId}/reprocess_summary`,
        {
          method: "POST",
          headers: { "Content-Type": "application/json" },
        }
      );

      const data = await response.json();
      if (!response.ok)
        throw new Error(
          data.error || "Failed to start summary reprocessing"
        );

      // Ensure the recording status is properly set to SUMMARIZING
      if (data.recording && data.recording.status !== "SUMMARIZING") {
        console.warn(
          `Warning: Reprocess summary returned unexpected status: ${data.recording.status}`
        );
        data.recording.status = "SUMMARIZING";
      }

      // Update the recording in the UI
      const index = recordings.value.findIndex((r) => r.id === recordingId);
      if (index !== -1) {
        recordings.value[index] = data.recording;
      }

      // Update selected recording if it's the one being reprocessed
      if (selectedRecording.value?.id === recordingId) {
        selectedRecording.value = data.recording;
      }

      showToast("Summary reprocessing started", "fa-sync-alt");

      // Switch to Summary tab to show progress
      selectedTab.value = "summary";

      // Show progress modal for reprocessing
      showProgressModalForReprocessing(recordingId, "summary");

      // Start polling for status updates
      startReprocessingPoll(recordingId);
    } catch (error) {
      console.error("Reprocess Summary Error:", error);
      setGlobalError(
        `Failed to start summary reprocessing: ${error.message}`
      );
    }
  };

  // Show progress modal for reprocessing operations
  const showProgressModalForReprocessing = (recordingId, type) => {
    const recording = recordings.value.find((r) => r.id === recordingId);
    if (!recording) return;

    // Create a mock file item for the progress modal
    const reprocessItem = {
      file: {
        name: recording.title || `Recording ${recordingId}`,
        size: recording.file_size || 0,
      },
      status: "pending",
      recordingId: recordingId,
      clientId: `reprocess-${type}-${recordingId}-${Date.now()}`,
      error: null,
      isReprocessing: true,
      reprocessType: type,
    };

    // Add to upload queue for progress tracking
    uploadQueue.value.unshift(reprocessItem);

    // Show progress modal
    progressPopupMinimized.value = false;
    progressPopupClosed.value = false;

    // Set as currently processing file
    currentlyProcessingFile.value = reprocessItem;
    processingProgress.value = 10;
    processingMessage.value =
      type === "transcription"
        ? "Starting transcription reprocessing..."
        : "Starting summary reprocessing...";
  };

  // ==================== 轮询监控功能 ====================

  const startReprocessingPoll = (recordingId) => {
    // Clear any existing poll for this recording
    if (reprocessingPolls.value.has(recordingId)) {
      clearInterval(reprocessingPolls.value.get(recordingId));
    }

    const pollInterval = setInterval(async () => {
      try {
        const response = await fetch(`/tool/speakr/status/${recordingId}`);
        if (!response.ok) {
          console.error(`Status check failed for recording ${recordingId}`);
          stopReprocessingPoll(recordingId);
          return;
        }

        const data = await response.json();

        // Update the recording in the UI
        const index = recordings.value.findIndex(
          (r) => r.id === recordingId
        );
        if (index !== -1) {
          recordings.value[index] = data;
        }

        // Update selected recording if it's the one being reprocessed
        if (selectedRecording.value?.id === recordingId) {
          selectedRecording.value = data;
        }

        const queueItem = uploadQueue.value.find(
          (item) => item.recordingId === recordingId
        );

        // Update the item's status and name for intermediate states
        if (queueItem) {
          queueItem.status = data.status;
          queueItem.displayName = data.title || data.original_filename || queueItem.file.name;
        }

        // Update progress modal if this is the currently processing item
        if (
          queueItem &&
          currentlyProcessingFile.value?.clientId === queueItem.clientId
        ) {
          updateReprocessingProgress(data.status, queueItem);
        }

        // Stop polling if processing is complete
        if (data.status === "COMPLETED" || data.status === "FAILED") {
          console.log(
            `Reprocessing ${data.status.toLowerCase()} for recording ${recordingId}`
          );
          stopReprocessingPoll(recordingId);

          // Update queue item status to final lowercase state
          if (queueItem) {
            queueItem.status =
              data.status === "COMPLETED" ? "completed" : "failed";
            if (data.status === "FAILED") {
              queueItem.error = data.error_message || "Reprocessing failed";
            }
          }

          // Clear current processing if this was the active item
          if (currentlyProcessingFile.value?.recordingId === recordingId) {
            resetCurrentFileProcessingState();
          }

          if (data.status === "COMPLETED") {
            showToast(
              "Reprocessing completed successfully",
              "fa-check-circle"
            );
          } else {
            showToast("Reprocessing failed", "fa-exclamation-circle");
          }
        }
      } catch (error) {
        console.error(
          `Error polling status for recording ${recordingId}:`,
          error
        );
        stopReprocessingPoll(recordingId);
      }
    }, 3000);

    reprocessingPolls.value.set(recordingId, pollInterval);
  };

  const updateReprocessingProgress = (status, queueItem) => {
    switch (status) {
      case "PENDING":
        processingProgress.value = 20;
        processingMessage.value = `Waiting to start ${queueItem.reprocessType} reprocessing...`;
        break;
      case "PROCESSING":
        processingProgress.value = Math.round(
          Math.min(70, processingProgress.value + Math.random() * 10)
        );
        processingMessage.value =
          queueItem.reprocessType === "transcription"
            ? "Reprocessing transcription..."
            : "Processing audio...";
        break;
      case "SUMMARIZING":
        processingProgress.value = Math.round(
          Math.min(90, processingProgress.value + Math.random() * 10)
        );
        processingMessage.value =
          queueItem.reprocessType === "summary"
            ? "Regenerating summary..."
            : "Generating title and summary...";
        break;
      case "COMPLETED":
        processingProgress.value = 100;
        processingMessage.value = "Reprocessing completed!";
        break;
      case "FAILED":
        processingProgress.value = 100;
        processingMessage.value = "Reprocessing failed.";
        break;
      default:
        processingProgress.value = 15;
        processingMessage.value = "Starting reprocessing...";
    }
  };

  const stopReprocessingPoll = (recordingId) => {
    if (reprocessingPolls.value.has(recordingId)) {
      clearInterval(reprocessingPolls.value.get(recordingId));
      reprocessingPolls.value.delete(recordingId);
      console.log(`Stopped reprocessing poll for recording ${recordingId}`);
    }
  };

  // ==================== 状态管理功能 ====================

  const generateSummary = async () => {
    if (!selectedRecording.value) return;

    try {
      const csrfToken = document
        .querySelector('meta[name="csrf-token"]')
        ?.getAttribute("content");

      const response = await fetch(
        `/tool/speakr/recording/${selectedRecording.value.id}/generate_summary`,
        {
          method: "POST",
          headers: {
            "Content-Type": "application/json",
            "X-CSRFToken": csrfToken,
          },
        }
      );

      const data = await response.json();

      if (!response.ok) {
        throw new Error(data.error || "Failed to generate summary");
      }

      // Update the recording status to show it's being processed
      selectedRecording.value.status = "SUMMARIZING";

      // Also update in recordings list if it exists
      const recordingInList = recordings.value.find(
        (r) => r.id === selectedRecording.value.id
      );
      if (recordingInList) {
        recordingInList.status = "SUMMARIZING";
      }

      showToast("Summary generation started", "success");
    } catch (error) {
      console.error("Error generating summary:", error);
      setGlobalError(`Failed to generate summary: ${error.message}`);
    }
  };

  const resetRecordingStatus = async (recordingId) => {
    const recording = recordings.value.find((r) => r.id === recordingId);
    if (!recording) return;

    try {
      const response = await fetch(
        `/tool/speakr/recording/${recording.id}/reset_status`,
        {
          method: "POST",
          headers: { "Content-Type": "application/json" },
        }
      );
      const data = await response.json();
      if (!response.ok) {
        throw new Error(data.error || "Failed to reset status");
      }
      const index = recordings.value.findIndex(
        (r) => r.id === recording.id
      );
      if (index !== -1) {
        recordings.value[index] = data.recording;
      }
      if (selectedRecording.value?.id === recording.id) {
        selectedRecording.value = data.recording;
      }
      showToast("Recording status has been reset.", "fa-check-circle");
    } catch (error) {
      setGlobalError(`Failed to reset status: ${error.message}`);
    }
  };

  const confirmReset = (recording) => {
    recordingToReset.value = recording;
    showResetModal.value = true;
  };

  const cancelReset = () => {
    showResetModal.value = false;
    recordingToReset.value = null;
  };

  const executeReset = async () => {
    if (!recordingToReset.value) return;
    const recordingId = recordingToReset.value.id;

    // Close the modal first
    cancelReset();

    try {
      const response = await fetch(
        `/tool/speakr/recording/${recordingId}/reset_status`,
        {
          method: "POST",
          headers: { "Content-Type": "application/json" },
        }
      );

      const data = await response.json();
      if (!response.ok)
        throw new Error(data.error || "Failed to reset status");

      // Update the recording in the UI
      const index = recordings.value.findIndex((r) => r.id === recordingId);
      if (index !== -1) {
        recordings.value[index] = data.recording;
      }

      // Update selected recording if it's the one being reset
      if (selectedRecording.value?.id === recordingId) {
        selectedRecording.value = data.recording;
      }

      showToast("Recording status has been reset.", "fa-check-circle");
    } catch (error) {
      console.error("Reset Status Error:", error);
      setGlobalError(`Failed to reset status: ${error.message}`);
    }
  };

  // ==================== 转写编辑功能 ====================

  const openTranscriptionEditor = () => {
    if (processedTranscription.value.isJson) {
      openAsrEditorModal();
    } else {
      openTextEditorModal();
    }
  };

  const openTextEditorModal = () => {
    if (!selectedRecording.value) return;
    editingTranscriptionContent.value =
      selectedRecording.value.transcription;
    showTextEditorModal.value = true;
  };

  const closeTextEditorModal = () => {
    showTextEditorModal.value = false;
    editingTranscriptionContent.value = "";
  };

  const saveTranscription = async () => {
    if (!selectedRecording.value) return;
    await saveTranscriptionContent(editingTranscriptionContent.value);
    closeTextEditorModal();
  };

  const openAsrEditorModal = async () => {
    if (!selectedRecording.value) return;
    try {
      const segments = JSON.parse(selectedRecording.value.transcription);
      editingSegments.value = segments.map((s, i) => ({
        ...s,
        id: i,
        showSuggestions: false,
        filteredSpeakers: [],
      }));

      // Populate available speakers
      const speakersInTranscript = [
        ...new Set(segments.map((s) => s.speaker)),
      ];
      const response = await fetch("/tool/speakr/speakers");
      const speakersFromDb = await response.json();
      const speakerNamesFromDb = speakersFromDb.map((s) => s.name);
      availableSpeakers.value = [
        ...new Set([...speakersInTranscript, ...speakerNamesFromDb]),
      ].sort();

      showAsrEditorModal.value = true;
    } catch (e) {
      console.error(
        "Could not parse transcription as JSON for ASR editor:",
        e
      );
      setGlobalError(
        "This transcription is not in the correct format for the ASR editor."
      );
    }
  };

  const closeAsrEditorModal = () => {
    showAsrEditorModal.value = false;
    editingSegments.value = [];
    availableSpeakers.value = [];
  };

  const saveAsrTranscription = async () => {
    const contentToSave = JSON.stringify(
      editingSegments.value.map(
        ({ id, showSuggestions, filteredSpeakers, ...rest }) => rest
      )
    );
    await saveTranscriptionContent(contentToSave);
    closeAsrEditorModal();
  };

  const adjustTime = (index, field, amount) => {
    if (editingSegments.value[index]) {
      editingSegments.value[index][field] = parseFloat(
        (editingSegments.value[index][field] + amount).toFixed(3)
      );
    }
  };

  const filterSpeakers = (index) => {
    const segment = editingSegments.value[index];
    if (segment) {
      const query = segment.speaker.toLowerCase();
      segment.filteredSpeakers = availableSpeakers.value.filter((s) =>
        s.toLowerCase().includes(query)
      );
    }
  };

  const openSpeakerSuggestions = (index) => {
    if (editingSegments.value[index]) {
      editingSegments.value[index].showSuggestions = true;
      filterSpeakers(index);
    }
  };

  const closeSpeakerSuggestions = (index) => {
    if (editingSegments.value[index]) {
      window.setTimeout(() => {
        if (editingSegments.value[index]) {
          editingSegments.value[index].showSuggestions = false;
        }
      }, 200);
    }
  };

  const selectSpeaker = (index, speaker) => {
    if (editingSegments.value[index]) {
      editingSegments.value[index].speaker = speaker;
      editingSegments.value[index].showSuggestions = false;
    }
  };

  const addSegment = () => {
    const lastSegment =
      editingSegments.value[editingSegments.value.length - 1];
    editingSegments.value.push({
      id: Date.now(),
      speaker: lastSegment ? lastSegment.speaker : "SPEAKER_00",
      start_time: lastSegment ? lastSegment.end_time : 0,
      end_time: lastSegment ? lastSegment.end_time + 1 : 1,
      sentence: "",
    });
  };

  const removeSegment = (index) => {
    editingSegments.value.splice(index, 1);
  };

  const saveTranscriptionContent = async (content) => {
    if (!selectedRecording.value) return;
    try {
      const response = await fetch(
        `/tool/speakr/recording/${selectedRecording.value.id}/update_transcription`,
        {
          method: "POST",
          headers: { "Content-Type": "application/json" },
          body: JSON.stringify({ transcription: content }),
        }
      );
      const data = await response.json();
      if (!response.ok)
        throw new Error(data.error || "Failed to update transcription");
      const index = recordings.value.findIndex(
        (r) => r.id === selectedRecording.value.id
      );
      if (index !== -1) {
        recordings.value[index] = data.recording;
      }
      selectedRecording.value = data.recording;
      showToast("Transcription updated successfully!", "fa-check-circle");
    } catch (error) {
      console.error("Save Transcription Error:", error);
      setGlobalError(`Failed to save transcription: ${error.message}`);
    }
  };

  // ==================== 说话人管理功能 ====================

  const searchSpeakers = async (query, speakerId) => {
    if (!query || query.length < 2) {
      speakerSuggestions.value[speakerId] = [];
      return;
    }

    loadingSuggestions.value[speakerId] = true;

    try {
      const response = await fetch(
        `/tool/speakr/speakers/search?q=${encodeURIComponent(query)}`
      );
      if (!response.ok) throw new Error("Failed to search speakers");

      const speakers = await response.json();
      speakerSuggestions.value[speakerId] = speakers;
    } catch (error) {
      console.error("Error searching speakers:", error);
      speakerSuggestions.value[speakerId] = [];
    } finally {
      loadingSuggestions.value[speakerId] = false;
    }
  };

  const selectSpeakerSuggestion = (speakerId, suggestion) => {
    if (speakerMap.value[speakerId]) {
      speakerMap.value[speakerId].name = suggestion.name;
      speakerSuggestions.value[speakerId] = [];
    }
  };

  const closeSpeakerSuggestionsOnClick = (event) => {
    // Check if the click was on an input field or dropdown
    const clickedInput = event.target.closest('input[type="text"]');
    const clickedDropdown = event.target.closest(".absolute.z-10");

    // If not clicking on input or dropdown, close all suggestions
    if (!clickedInput && !clickedDropdown) {
      Object.keys(speakerSuggestions.value).forEach((speakerId) => {
        speakerSuggestions.value[speakerId] = [];
      });
    }
  };

  const openSpeakerModal = () => {
    // Clear any existing speaker map data first
    speakerMap.value = {};
    speakerDisplayMap.value = {};

    // Get the same speaker order used in processedTranscription
    const transcription = selectedRecording.value?.transcription;
    let speakers = [];

    if (transcription) {
      try {
        const transcriptionData = JSON.parse(transcription);
        if (transcriptionData && Array.isArray(transcriptionData)) {
          // Use the exact same logic as processedTranscription to get speakers
          speakers = [
            ...new Set(
              transcriptionData
                .map((segment) => segment.speaker)
                .filter(Boolean)
            ),
          ];
        }
      } catch (e) {
        // Fall back to identifiedSpeakers if JSON parsing fails
        speakers = identifiedSpeakers.value;
      }
    }

    // Set modalSpeakers for the template to use
    modalSpeakers.value = speakers;

    // Initialize speaker map with the same order and colors as the transcript
    speakerMap.value = speakers.reduce((acc, speaker, index) => {
      acc[speaker] = {
        name: "",
        isMe: false,
        color: `speaker-color-${(index % 8) + 1}`,
      };
      // Keep the original speaker ID for display
      speakerDisplayMap.value[speaker] = speaker;
      return acc;
    }, {});

    highlightedSpeaker.value = null;
    speakerSuggestions.value = {};
    loadingSuggestions.value = {};
    isAutoIdentifying.value = false;
    showSpeakerModal.value = true;
  };

  const closeSpeakerModal = () => {
    showSpeakerModal.value = false;
    highlightedSpeaker.value = null;
    // Clear the speaker map to prevent stale data from persisting
    // 【保留原 main-old.js 行为】关闭 modal 时清空 speakerMap。这会导致
    // processedTranscription / highlightedTranscript 回退到显示原始 speaker ID
    // （如 SPEAKER_00），不再显示用户刚输入的名字。这是 main-old.js 的原始行为，
    // 拆分后仍保留。如果产品希望“保存后正文继续显示名字”，需要在
    // saveSpeakerNames 成功后从后端返回的 recording 里把 speaker_map 灌回
    // speakerMap ref，再 close（当前未做，属待确认增强）。
    speakerMap.value = {};
    speakerSuggestions.value = {};
    loadingSuggestions.value = {};

    // Clean up click handler if it exists
    if (window.speakerModalClickHandler) {
      const modalContent = document.querySelector(".modal-content");
      if (modalContent) {
        modalContent.removeEventListener(
          "click",
          window.speakerModalClickHandler
        );
      }
      delete window.speakerModalClickHandler;
    }
  };

  const saveSpeakerNames = async () => {
    if (!selectedRecording.value) return;

    // Create a filtered speaker map that excludes entries with blank names
    const filteredSpeakerMap = Object.entries(speakerMap.value).reduce(
      (acc, [speakerId, speakerData]) => {
        if (speakerData.name && speakerData.name.trim() !== "") {
          acc[speakerId] = speakerData;
        }
        return acc;
      },
      {}
    );

    try {
      const response = await fetch(
        `/tool/speakr/recording/${selectedRecording.value.id}/update_speakers`,
        {
          method: "POST",
          headers: { "Content-Type": "application/json" },
          body: JSON.stringify({
            speaker_map: filteredSpeakerMap,
            regenerate_summary: regenerateSummaryAfterSpeakerUpdate.value,
          }),
        }
      );

      const data = await response.json();
      if (!response.ok)
        throw new Error(data.error || "Failed to update speakers");

      // On success, close the modal and clear the speakerMap state *before*
      // updating the recording data.
      closeSpeakerModal();

      // The backend returns the fully updated recording object.
      const index = recordings.value.findIndex(
        (r) => r.id === selectedRecording.value.id
      );
      if (index !== -1) {
        recordings.value[index] = data.recording;
      }
      selectedRecording.value = data.recording;

      showToast("Speaker names updated successfully!", "fa-check-circle");

      // If a summary regeneration was requested, start polling for its status.
      if (regenerateSummaryAfterSpeakerUpdate.value) {
        startReprocessingPoll(selectedRecording.value.id);
      }
    } catch (error) {
      console.error("Save Speaker Names Error:", error);
      setGlobalError(`Failed to save speaker names: ${error.message}`);
    }
  };

  const findSpeakerGroups = (speakerId) => {
    if (!speakerId) return [];

    const groups = [];
    const modalTranscript = document.querySelector(
      "div.speaker-modal-transcript"
    );
    const mainTranscript = document.querySelector(
      ".transcription-simple-view, .transcription-with-speakers, .transcription-content"
    );
    const transcriptContainer = modalTranscript || mainTranscript;

    if (!transcriptContainer) return [];

    // For JSON-based transcripts with segments
    const allSegments =
      transcriptContainer.querySelectorAll(".speaker-segment");
    if (allSegments.length > 0) {
      let currentGroup = null;
      let lastSpeakerId = null;

      allSegments.forEach((segment) => {
        const speakerTag = segment.querySelector("[data-speaker-id]");
        const segmentSpeakerId = speakerTag?.dataset.speakerId;

        if (segmentSpeakerId === speakerId) {
          // If this is a new group (not consecutive with previous)
          if (lastSpeakerId !== speakerId) {
            currentGroup = {
              startElement: segment,
              elements: [segment],
            };
            groups.push(currentGroup);
          } else if (currentGroup) {
            // Add to existing group
            currentGroup.elements.push(segment);
          }
        }
        lastSpeakerId = segmentSpeakerId;
      });
    } else {
      // For plain text transcripts with speaker tags
      const allTags =
        transcriptContainer.querySelectorAll("[data-speaker-id]");
      let currentGroup = null;

      allTags.forEach((tag) => {
        if (tag.dataset.speakerId === speakerId) {
          // Find the parent element that contains this speaker's content
          const parentSegment =
            tag.closest(".speaker-segment") || tag.parentElement;

          if (
            !currentGroup ||
            !currentGroup.lastElement ||
            !parentSegment.previousElementSibling ||
            parentSegment.previousElementSibling !==
              currentGroup.lastElement
          ) {
            // Start a new group
            currentGroup = {
              startElement: parentSegment,
              elements: [parentSegment],
              lastElement: parentSegment,
            };
            groups.push(currentGroup);
          } else {
            // Continue the group
            currentGroup.elements.push(parentSegment);
            currentGroup.lastElement = parentSegment;
          }
        }
      });
    }

    return groups;
  };

  const highlightSpeakerInTranscript = async (speakerId) => {
    highlightedSpeaker.value = speakerId;

    if (speakerId) {
      await nextTick();
      speakerGroups.value = findSpeakerGroups(speakerId);
      currentSpeakerGroupIndex.value = 0;

      if (speakerGroups.value.length > 0) {
        const firstGroup = speakerGroups.value[0];
        if (firstGroup && firstGroup.startElement) {
          firstGroup.startElement.scrollIntoView({
            behavior: "smooth",
            block: "center",
          });
        }
      }
    } else {
      speakerGroups.value = [];
      currentSpeakerGroupIndex.value = -1;
    }
  };

  const navigateToNextSpeakerGroup = () => {
    if (speakerGroups.value.length === 0) return;

    // Don't reset the speaker groups, just update the index
    currentSpeakerGroupIndex.value =
      (currentSpeakerGroupIndex.value + 1) % speakerGroups.value.length;
    const group = speakerGroups.value[currentSpeakerGroupIndex.value];
    if (group && group.startElement) {
      group.startElement.scrollIntoView({
        behavior: "smooth",
        block: "center",
      });
    }
  };

  const navigateToPrevSpeakerGroup = () => {
    if (speakerGroups.value.length === 0) return;

    // Don't reset the speaker groups, just update the index
    currentSpeakerGroupIndex.value =
      currentSpeakerGroupIndex.value <= 0
        ? speakerGroups.value.length - 1
        : currentSpeakerGroupIndex.value - 1;
    const group = speakerGroups.value[currentSpeakerGroupIndex.value];
    if (group && group.startElement) {
      group.startElement.scrollIntoView({
        behavior: "smooth",
        block: "center",
      });
    }
  };

  const focusSpeaker = (speakerId) => {
    // Set this as the active speaker input
    activeSpeakerInput.value = speakerId;
    // Only highlight if not already highlighted (to preserve navigation state)
    if (highlightedSpeaker.value !== speakerId) {
      highlightSpeakerInTranscript(speakerId);
    }
  };

  const blurSpeaker = () => {
    // Clear the active speaker input after a delay to allow clicking on suggestions
    setTimeout(() => {
      activeSpeakerInput.value = null;
      speakerSuggestions.value = {};
    }, 200);
    clearSpeakerHighlight();
  };

  const clearSpeakerHighlight = () => {
    highlightedSpeaker.value = null;
  };

  const autoIdentifySpeakers = async () => {
    if (!selectedRecording.value) {
      showToast("No recording selected.", "fa-exclamation-circle");
      return;
    }

    isAutoIdentifying.value = true;
    showToast("Starting automatic speaker identification...", "fa-magic");

    try {
      const response = await fetch(
        `/tool/speakr/recording/${selectedRecording.value.id}/auto_identify_speakers`,
        {
          method: "POST",
          headers: { "Content-Type": "application/json" },
          body: JSON.stringify({
            current_speaker_map: speakerMap.value,
          }),
        }
      );

      const data = await response.json();
      if (!response.ok) {
        throw new Error(
          data.error || "Unknown error occurred during auto-identification."
        );
      }

      // Check if there's a message (e.g., all speakers already identified)
      if (data.message) {
        showToast(data.message, "fa-info-circle");
        return;
      }

      // Update speakerMap with the identified names (only for unidentified speakers)
      let identifiedCount = 0;
      for (const speakerId in data.speaker_map) {
        const identifiedName = data.speaker_map[speakerId];
        if (
          speakerMap.value[speakerId] &&
          identifiedName &&
          identifiedName.trim() !== ""
        ) {
          speakerMap.value[speakerId].name = identifiedName;
          identifiedCount++;
        }
      }

      if (identifiedCount > 0) {
        showToast(
          `${identifiedCount} speaker(s) identified successfully!`,
          "fa-check-circle"
        );
      } else {
        showToast(
          "No speakers could be identified from the context.",
          "fa-info-circle"
        );
      }
    } catch (error) {
      console.error("Auto Identify Speakers Error:", error);
      showToast(`Error: ${error.message}`, "fa-exclamation-circle", 5000);
    } finally {
      isAutoIdentifying.value = false;
    }
  };

  // ==================== 标记功能 ====================

  const toggleInbox = async (recording) => {
    if (!recording || !recording.id) return;

    try {
      const response = await fetch(
        `/tool/speakr/recording/${recording.id}/toggle_inbox`,
        {
          method: "POST",
          headers: { "Content-Type": "application/json" },
        }
      );

      const data = await response.json();
      if (!response.ok)
        throw new Error(data.error || "Failed to toggle inbox status");

      // Update the recording in the UI
      recording.is_inbox = data.is_inbox;

      // Update in the recordings list
      const index = recordings.value.findIndex(
        (r) => r.id === recording.id
      );
      if (index !== -1) {
        recordings.value[index].is_inbox = data.is_inbox;
      }

      showToast(
        `Recording ${data.is_inbox ? "moved to inbox" : "marked as read"}`
      );
    } catch (error) {
      console.error("Toggle Inbox Error:", error);
      setGlobalError(`Failed to toggle inbox status: ${error.message}`);
    }
  };

  const toggleHighlight = async (recording) => {
    if (!recording || !recording.id) return;

    try {
      const response = await fetch(
        `/tool/speakr/recording/${recording.id}/toggle_highlight`,
        {
          method: "POST",
          headers: { "Content-Type": "application/json" },
        }
      );

      const data = await response.json();
      if (!response.ok)
        throw new Error(
          data.error || "Failed to toggle highlighted status"
        );

      // Update the recording in the UI
      recording.is_highlighted = data.is_highlighted;

      // Update in the recordings list
      const index = recordings.value.findIndex(
        (r) => r.id === recording.id
      );
      if (index !== -1) {
        recordings.value[index].is_highlighted = data.is_highlighted;
      }

      showToast(
        `Recording ${data.is_highlighted ? "highlighted" : "unhighlighted"}`
      );
    } catch (error) {
      console.error("Toggle Highlight Error:", error);
      setGlobalError(
        `Failed to toggle highlighted status: ${error.message}`
      );
    }
  };

  // ==================== 返回所有状态和方法 ====================
  return {
    // 状态
    showReprocessModal,
    reprocessType,
    reprocessRecording,
    asrReprocessOptions,
    showTextEditorModal,
    editingTranscriptionContent,
    showAsrEditorModal,
    editingSegments,
    availableSpeakers,
    showSpeakerModal,
    speakerMap,
    speakerDisplayMap,
    modalSpeakers,
    regenerateSummaryAfterSpeakerUpdate,
    speakerSuggestions,
    loadingSuggestions,
    activeSpeakerInput,
    isAutoIdentifying,
    highlightedSpeaker,
    currentSpeakerGroupIndex,
    speakerGroups,
    recordingToReset,
    showResetModal,

    // 计算属性
    identifiedSpeakersInOrder,
    hasSpeakerNames,
    highlightedTranscript,

    // 重处理方法
    reprocessTranscription,
    reprocessSummary,
    confirmReprocess,
    cancelReprocess,
    executeReprocess,

    // 转写编辑方法
    openTranscriptionEditor,
    openTextEditorModal,
    closeTextEditorModal,
    saveTranscription,
    openAsrEditorModal,
    closeAsrEditorModal,
    saveAsrTranscription,
    adjustTime,
    filterSpeakers,
    openSpeakerSuggestions,
    closeSpeakerSuggestions,
    selectSpeaker,
    addSegment,
    removeSegment,

    // 说话人管理方法
    searchSpeakers,
    selectSpeakerSuggestion,
    closeSpeakerSuggestionsOnClick,
    openSpeakerModal,
    closeSpeakerModal,
    saveSpeakerNames,
    highlightSpeakerInTranscript,
    focusSpeaker,
    blurSpeaker,
    clearSpeakerHighlight,
    navigateToNextSpeakerGroup,
    navigateToPrevSpeakerGroup,
    autoIdentifySpeakers,

    // 状态管理方法
    generateSummary,
    resetRecordingStatus,
    confirmReset,
    cancelReset,
    executeReset,

    // 标记功能方法
    toggleInbox,
    toggleHighlight,

    // 轮询监控方法（如需要外部访问）
    startReprocessingPoll,
    stopReprocessingPoll,
  };
}
