<script setup lang="ts">
import { onMounted, onUnmounted, ref, computed, nextTick, watch } from 'vue';
import { usePromptStore, splitTokens, normalizeSymbols, parseDetailedToken, constructToken, toNumericForm, toBracketForm, formatWeight } from '../stores/promptStore';
import type { LangCode, PresetFolder } from '../types';
import NotificationToast from './NotificationToast.vue';
import TranslationPopup from './TranslationPopup.vue';
import EditorToolbar from './editor/EditorToolbar.vue';
import EditorInput from './editor/EditorInput.vue';
import TokenMappingPanel from './editor/TokenMappingPanel.vue';

const store = usePromptStore();
const draggingIndex = ref<number | null>(null);
const overIndex = ref<number | null>(null);
const dragPreview = ref<HTMLElement | null>(null);
const isDragging = ref(false);
const externalDraggingTag = ref<string | null>(null);
// 指针拖拽新增状态
const insertSide = ref<'before' | 'after' | null>(null);
const pointerId = ref<number | null>(null);
const startX = ref(0);
const startY = ref(0);
const lastX = ref(0);
const lastY = ref(0);
const dragOffsetX = ref(0);
const dragOffsetY = ref(0);
const dragStarted = ref(false);
const cachedTokenRects = ref<{
  index: number;
  left: number;
  top: number;
  width: number;
  height: number;
  midX: number;
}[]>([]);
let rafId: number | null = null;

const DRAG_THRESHOLD = 3; // 像素阈值，避免误触
const editingIndex = ref<number | null>(null);
const presetName = ref('');
// 跨页面切换保留所选文件夹（刷新后重置为默认）
const selectedFolderId = computed({
  get: () => store.editorSelectedFolderId,
  set: (v: string) => { store.editorSelectedFolderId = v; },
});
// 保存预设时是否同时标记为收藏
const saveAsFavorite = ref(false);
const viewMode = ref<'compact' | 'detail'>('compact');
// 左侧光标所在的 token 序号，用于在右侧映射中定位高亮
const activeTokenIndex = ref<number | null>(null);
const showPresetDropdown = ref(false);
const showTranslationPopup = ref(false);
const translationTargetToken = ref<string | null>(null);

const translationTokens = computed(() => {
  if (translationTargetToken.value) {
    return [translationTargetToken.value];
  }
  return unmappedTokens.value;
});
const notification = ref<{ message: string; type: 'success' | 'error' | 'info'; show: boolean }>({
  message: '',
  type: 'info',
  show: false
});

function showNotification(message: string, type: 'success' | 'error' | 'info' = 'info') {
  notification.value = { message, type, show: true };
  setTimeout(() => {
    notification.value.show = false;
  }, 3000);
}

// 点击外部关闭下拉菜单
function handleClickOutside(event: Event) {
  const target = event.target as HTMLElement;
  // 检查点击是否在预设下拉区域内，包括重命名输入框和按钮
  if (!target.closest('.pe-presets') && !target.closest('.pd-dropdown')) {
    showPresetDropdown.value = false;
  }
}

onMounted(() => {
  document.addEventListener('click', handleClickOutside);
  // 仅首次进入时初始化为默认文件夹；后续页面切换保留用户上次的选择
  if (!store.editorFolderInitialized) {
    const defaultFolder = store.presetManagement?.settings?.defaultFolder;
    if (defaultFolder) {
      store.editorSelectedFolderId = defaultFolder;
    }
    store.editorFolderInitialized = true;
  }
  store.searchQuery = ''; // Reset search query to ensure all tags are shown
});

// 清理事件监听器
onUnmounted(() => {
  document.removeEventListener('click', handleClickOutside);
});

const selectedLang = computed({
  get: () => store.selectedLang,
  set: (v: LangCode) => store.setLanguage(v),
});

const tokens = computed(() => store.tokens);

const folderTree = computed(() => {
  const folders = store.presetFolders || [];
  const rootFolders = folders.filter(f => !f.parentId);

  function buildTree(parentFolders: PresetFolder[]): any[] {
    return parentFolders.map(folder => ({
      ...folder,
      children: buildTree(folders.filter(f => f.parentId === folder.id)),
      presetCount: (store.extendedPresets || []).filter(p => p.folderId === folder.id).length
    }));
  }

  return buildTree(rootFolders);
});

const flattenedFolders = computed(() => {
  type FlatItem = { id: string; name: string; label: string; level: number; presetCount: number; hasChildren: boolean };
  const res: FlatItem[] = [];
  function walk(nodes: any[], level: number, parentPath: string) {
    nodes.forEach((node: any) => {
      const label = parentPath ? `${parentPath} / ${node.name}` : node.name;
      res.push({
        id: node.id,
        name: node.name,
        label,
        level,
        presetCount: node.presetCount,
        hasChildren: !!(node.children && node.children.length)
      });
      if (node.children && node.children.length) {
        walk(node.children, level + 1, label);
      }
    });
  }
  walk(folderTree.value, 0, '');
  return res;
});

const suggestions = ref<string[]>([]);
const editSuggestions = ref<string[]>([]);
const editorInputRef = ref<InstanceType<typeof EditorInput> | null>(null);
const tokenMappingRef = ref<InstanceType<typeof TokenMappingPanel> | null>(null);
// 优先级模式开关：false = 括号嵌套，true = 数字权重后缀
const numericMode = ref(false);
// 括号嵌套时使用的括号样式（默认圆括号）
const bracketStyle = ref<'()' | '{}' | '[]' | '<>'>('()');
const priorityStep = ref(0.1);
function splitTokensLocal(txt: string): string[] {
  return splitTokens(txt);
}
function normalizeToken(t: string): string { return t.trim().replace(/\s+/g, ' '); }
function normalizePromptLocal(txt: string): string {
  return splitTokens(txt).map(t => t.replace(/\s+/g, ' ')).join(', ');
}
function applyFullPrompt(newText: string) {
  const el = editorInputRef.value?.inputEl;
  if (!el) { text.value = newText; return; }
  el.focus();
  applyTextReplacement(el, 0, text.value.length, newText);
}
function getTokenWeight(token: string): number {
  const { core } = store.parseTokenWrappers(token);
  const idx = core.lastIndexOf(':');
  if (idx > -1) {
    const w = parseFloat(core.slice(idx + 1).trim());
    return isNaN(w) ? 1 : w;
  }
  return 1;
}
function hasWeightSuffix(token: string): boolean {
  const { core } = store.parseTokenWrappers(token);
  return /:\s*\d+(?:\.\d+)?$/.test(core);
}
function roundToDecimals(v: number, decimals: number): number {
  const m = Math.pow(10, decimals);
  return Math.round(v * m) / m;
}
function stepDecimals(): number {
  const s = String(priorityStep.value);
  return s.includes('.') ? (s.split('.')[1]?.length ?? 0) : 0;
}

// 数字权重模式：确保 () 容器，按步进调整显式权重（默认 1，即无后缀）
function adjustNumericWeight(token: string, deltaSteps: number): string {
  const { core, weight, prefix, suffix } = parseDetailedToken(token);
  if (prefix || suffix) return token;
  const base = weight ?? 1;
  let next = roundToDecimals(base + deltaSteps * priorityStep.value, stepDecimals());
  if (next < 0) next = 0;
  if (next === 1) return `(${core})`;
  return `(${core}:${formatWeight(next)})`;
}

// 括号嵌套模式：在最外层再套一层选定括号
function addBracketLayer(token: string): string {
  const { core, weight, wrappers, prefix, suffix } = parseDetailedToken(token);
  const newWrappers = [bracketStyle.value, ...wrappers];
  return constructToken(core, weight, newWrappers, prefix, suffix);
}

// 括号嵌套模式：去掉最外层括号；若移除后无 () 承载裸权重则一并清除
function removeBracketLayer(token: string): string {
  const { core, weight, wrappers, prefix, suffix } = parseDetailedToken(token);
  if (prefix || suffix) return token;
  if (wrappers.length === 0) return core;
  const newWrappers = wrappers.slice(1);
  const hasParen = newWrappers.includes('()');
  const keepWeight = weight !== undefined && weight !== 1 && hasParen ? weight : undefined;
  return constructToken(core, keepWeight, newWrappers, prefix, suffix);
}
const text = ref('');

watch(text, (val) => {
  store.setPromptTextRaw(val);
  updateSuggestionsFromText();
});

// 当 store.promptText 发生变化（例如点击右侧预设加载）时，主动同步到左侧输入
watch(() => store.promptText, (v) => {
  if (text.value !== v) text.value = v;
}, { immediate: true });

function updateSuggestionsFromText() {
  const el = editorInputRef.value?.inputEl;
  const txt = store.promptText;
  let pos = txt.length;
  if (el && typeof el.selectionStart === 'number') {
    pos = el.selectionStart ?? txt.length;
  }
  const leftCommaEn = txt.lastIndexOf(',', pos - 1);
  const leftCommaCn = txt.lastIndexOf('，', pos - 1);
  const left = Math.max(leftCommaEn, leftCommaCn);
  const rightCommaEn = txt.indexOf(',', pos);
  const rightCommaCn = txt.indexOf('，', pos);
  const rightCandidates = [rightCommaEn, rightCommaCn].filter(i => i !== -1);
  const right = rightCandidates.length ? Math.min(...rightCandidates) : txt.length;
  const segment = txt.slice(left < 0 ? 0 : left + 1, right).trim();
  const { core } = parseDetailedToken(segment);
  const cleanCore = core.replace(/^[\(\[\{<]+/, '').replace(/[\)\]\}>]+$/, '');
  suggestions.value = store.getSuggestions(cleanCore, 8);
}

function updateEditSuggestionsFromValue(val: string) {
  const { core } = parseDetailedToken(val);
  const cleanCore = core.replace(/^[\(\[\{<]+/, '').replace(/[\)\]\}>]+$/, '');
  editSuggestions.value = store.getSuggestions(cleanCore, 8);
}

// 统一的文本替换方法：优先使用原生插入以保留撤回栈，失败时回退
function applyTextReplacement(
  el: HTMLTextAreaElement | HTMLInputElement,
  start: number,
  end: number,
  text: string,
) {
  try {
    if (typeof el.setSelectionRange === 'function') {
      el.setSelectionRange(start, end);
    }
    const ok = (document as any).execCommand && (document as any).execCommand('insertText', false, text);
    if (ok) return;
  } catch { }
  try {
    el.setRangeText(text, start, end, 'end');
    try {
      const ie = new (window as any).InputEvent('input', { bubbles: true, data: text, inputType: 'insertReplacementText' });
      el.dispatchEvent(ie);
    } catch {
      el.dispatchEvent(new Event('input', { bubbles: true }));
    }
  } catch {
    const value = (el as any).value as string;
    (el as any).value = value.slice(0, start) + text + value.slice(end);
    el.dispatchEvent(new Event('input', { bubbles: true }));
  }
}


function handleAddTag(tag: string) {
  const el = editorInputRef.value?.inputEl;
  if (!el) {
    store.setPromptTextRaw(store.promptText ? store.promptText + ', ' + tag : tag);
    return;
  }

  el.focus();

  let start = el.selectionStart ?? store.promptText.length;
  const textVal = el.value;
  const len = textVal.length;

  let tokenStart = start;
  while (tokenStart > 0 && !/[,，]/.test(textVal[tokenStart - 1] || '')) {
    tokenStart--;
  }
  let tokenEnd = start;
  while (tokenEnd < len && !/[,，]/.test(textVal[tokenEnd] || '')) {
    tokenEnd++;
  }

  const rawToken = textVal.slice(tokenStart, tokenEnd);
  const trimmedToken = rawToken.trim();

  if (trimmedToken.length > 0) {
    const tokenCenter = tokenStart + rawToken.length / 2;
    if (start < tokenCenter) {
      start = tokenStart;
    } else {
      start = tokenEnd;
    }
  }

  let prefix = '';
  let suffix = '';

  if (start > 0) {
    const prevText = textVal.slice(0, start);
    if (/[^,，\s]$/.test(prevText)) {
      prefix = ', ';
    } else if (/[,，]$/.test(prevText)) {
      prefix = ' ';
    } else if (/[,，]\s+$/.test(prevText)) {
      prefix = '';
    } else if (/\s+$/.test(prevText)) {
      const trimmedPrev = prevText.trimEnd();
      if (trimmedPrev.length > 0 && !/[,，]$/.test(trimmedPrev)) {
        if (!/[,，]\s*$/.test(prevText)) {
          prefix = ', ';
        }
      }
    }
  }

  if (start < len) {
    const nextText = textVal.slice(start);
    if (/^[^,，\s]/.test(nextText)) {
      suffix = ', ';
    } else if (/^\s+[^,，]/.test(nextText)) {
      suffix = ', ';
    }
  }

  const toInsert = prefix + tag + suffix;
  applyTextReplacement(el, start, start, toInsert);

  nextTick(() => {
    el.focus();
  });
}

async function copyLeft() {
  try {
    await navigator.clipboard.writeText(store.promptText);
    showNotification('提示词已复制到剪贴板', 'success');
  } catch (error) {
    showNotification('复制失败，请手动复制', 'error');
  }
}
function replaceCnComma() { applyFullPrompt(normalizeSymbols(text.value)); }
function formatPrompt() { applyFullPrompt(normalizePromptLocal(text.value)); }

function unifyPriorityStyle() {
  const list = splitTokens(text.value).map(token =>
    numericMode.value ? toNumericForm(token) : toBracketForm(token)
  );
  applyFullPrompt(list.join(', '));
  showNotification(numericMode.value ? '已统一为数字权重' : '已统一为括号样式', 'success');
}

// 新增功能方法
function toggleUnderscoreSpace() {
  const tokens = splitTokens(text.value);

  // 1. 统计全局倾向
  let spaceCount = 0;
  let underscoreCount = 0;

  // 预解析所有 Token
  const parsedList = tokens.map(t => parseDetailedToken(t));

  parsedList.forEach(({ core }) => {
    for (const char of core) {
      if (char === ' ') spaceCount++;
      if (char === '_') underscoreCount++;
    }
  });

  // 2. 确定目标格式
  // 逻辑：统一成“非优势”的一方。
  // 如果下划线更多（或相等），则统一变成空格（通常是为了可读性）。
  // 如果空格更多，则统一变成下划线（通常是为了作为 Tag 使用）。
  const targetIsUnderscore = spaceCount > underscoreCount;

  const newTokens = parsedList.map(({ core, weight, wrappers }) => {
    let newCore = core;

    if (targetIsUnderscore) {
      newCore = newCore.replace(/ /g, '_');
    } else {
      newCore = newCore.replace(/_/g, ' ');
    }

    // 重构 Token (保持权重和包裹层)
    let result = newCore;
    let currentWrappers = [...wrappers];

    if (weight !== undefined && weight !== 1) {
      const lastWrapper = currentWrappers[currentWrappers.length - 1];
      if (lastWrapper === '()') {
        currentWrappers.pop();
      }
      const wStr = Number.isInteger(weight) ? weight.toString() : weight.toFixed(2).replace(/\.?0+$/, '');
      result = `(${result}:${wStr})`;
    }

    return store.wrapToken(result, currentWrappers);
  });

  applyFullPrompt(newTokens.join(', '));
  showNotification(targetIsUnderscore ? '已统一为下划线格式' : '已统一为空格格式', 'success');
}

function addWrapperToToken(index: number) {
  const tokens = splitTokensLocal(text.value);
  if (index < 0 || index >= tokens.length) return;
  const token = tokens[index]!;
  tokens[index] = numericMode.value ? adjustNumericWeight(token, +1) : addBracketLayer(token);
  applyFullPrompt(tokens.join(', '));
  showNotification(numericMode.value ? '已提升权重' : '已添加优先级', 'success');
}

function removeWrapperFromToken(index: number) {
  const tokens = splitTokensLocal(text.value);
  if (index < 0 || index >= tokens.length) return;
  const token = tokens[index]!;
  tokens[index] = numericMode.value ? adjustNumericWeight(token, -1) : removeBracketLayer(token);
  applyFullPrompt(tokens.join(', '));
  showNotification(numericMode.value ? '已降低权重' : '已移除优先级', 'success');
}

function getTokenWrapperInfo(token: string) {
  return store.getTokenWrapperInfo(token);
}

function cacheTokenRects() {
  const dragContainer = tokenMappingRef.value?.dragContainer;
  if (!dragContainer) {
    cachedTokenRects.value = [];
    return;
  }
  const selector = viewMode.value === 'compact' ? '.pe-token-compact' : '.pe-token-detail';
  const elements = dragContainer.querySelectorAll(selector);
  cachedTokenRects.value = Array.from(elements).map(el => {
    const htmlEl = el as HTMLElement;
    const idx = parseInt(htmlEl.getAttribute('data-index') || '-1', 10);
    return {
      index: idx,
      left: htmlEl.offsetLeft,
      top: htmlEl.offsetTop,
      width: htmlEl.offsetWidth,
      height: htmlEl.offsetHeight,
      midX: htmlEl.offsetLeft + htmlEl.offsetWidth / 2
    };
  }).filter(item => item.index !== -1);
}

// 指针事件版拖拽：更高性能且可自定义插入指示
function onPointerDown(index: number, e: PointerEvent) {
  if (editingIndex.value === index) return;
  draggingIndex.value = index;
  pointerId.value = e.pointerId;
  startX.value = e.clientX;
  startY.value = e.clientY;
  lastX.value = e.clientX;
  lastY.value = e.clientY;

  const target = e.currentTarget as HTMLElement;
  const rect = target.getBoundingClientRect();
  dragOffsetX.value = e.clientX - rect.left;
  dragOffsetY.value = e.clientY - rect.top;

  dragStarted.value = false;
  isDragging.value = false;
  insertSide.value = null;

  // 缓存所有 Token 的位置信息 (相对于 dragContainer)
  cacheTokenRects();

  // 监听全局移动与抬起
  window.addEventListener('pointermove', handlePointerMove);
  window.addEventListener('pointerup', handlePointerUp, { once: true });
}

function handlePointerMove(e: PointerEvent) {
  lastX.value = e.clientX;
  lastY.value = e.clientY;
  const dx = e.clientX - startX.value;
  const dy = e.clientY - startY.value;

  if (!dragStarted.value) {
    if (Math.hypot(dx, dy) > DRAG_THRESHOLD) {
      dragStarted.value = true;
      isDragging.value = true;
      if (draggingIndex.value != null) createPointerPreview(draggingIndex.value);
    }
    return;
  }

  if (!isDragging.value) return;

  // 使用 requestAnimationFrame 节流渲染
  if (rafId) return;
  rafId = requestAnimationFrame(() => {
    positionPreview(lastX.value, lastY.value);
    updateOverIndexAndSideFast(lastX.value, lastY.value, draggingIndex.value);
    rafId = null;
  });
}

function handlePointerUp(e: PointerEvent) {
  window.removeEventListener('pointermove', handlePointerMove);
  if (!dragStarted.value || draggingIndex.value == null) {
    cleanupDrag();
    return;
  }
  const from = draggingIndex.value!;
  const j = overIndex.value;
  const side = insertSide.value;
  if (j != null && side) {
    let to = j;
    if (side === 'before') {
      to = j - (from < j ? 1 : 0);
    } else {
      to = j + (from > j ? 1 : 0);
    }
    const list = splitTokensLocal(text.value);
    if (to < 0) to = 0;
    if (to >= list.length) to = list.length - 1;
    const [item] = list.splice(from, 1);
    if (item != null) list.splice(to, 0, item);
    applyFullPrompt(list.join(', '));
    showNotification('已重新排序', 'success');
  }
  cleanupDrag();
}

function cleanupDrag() {
  if (rafId) { cancelAnimationFrame(rafId); rafId = null; }
  cachedTokenRects.value = [];
  draggingIndex.value = null;
  overIndex.value = null;
  isDragging.value = false;
  insertSide.value = null;
  pointerId.value = null;
  if (dragPreview.value) {
    document.body.removeChild(dragPreview.value);
    dragPreview.value = null;
  }
}

function createPointerPreview(index: number) {
  const token = tokens.value[index] || '';
  const translation = displayTrans(token);
  const preview = document.createElement('div');
  preview.className = 'drag-preview';
  preview.innerHTML = `
    <div class="drag-preview-content">
      <span class="drag-preview-key">${token}</span>
      <span class="drag-preview-arrow">→</span>
      <span class="drag-preview-trans">${translation}</span>
    </div>
  `;
  preview.style.position = 'fixed';
  preview.style.top = '0';
  preview.style.left = '0';
  preview.style.zIndex = '1000';
  preview.style.pointerEvents = 'none';
  ; (preview.style as any).contain = 'layout style paint';
  preview.style.willChange = 'transform, opacity';
  document.body.appendChild(preview);
  dragPreview.value = preview;
}

function positionPreview(x: number, y: number) {
  if (!dragPreview.value) return;
  dragPreview.value.style.transform = `translate(${x - dragOffsetX.value}px, ${y - dragOffsetY.value}px)`;
}

function updateOverIndexAndSideFast(clientX: number, clientY: number, activeDraggingIndex: number | null = null) {
  const dragContainer = tokenMappingRef.value?.dragContainer;
  if (!dragContainer) return;

  // 计算鼠标在容器内的相对坐标
  const containerRect = dragContainer.getBoundingClientRect();
  const relX = clientX - containerRect.left;
  const relY = clientY - containerRect.top;

  const candidates = cachedTokenRects.value.filter(item => item.index !== activeDraggingIndex);
  if (!candidates.length) {
    overIndex.value = null;
    insertSide.value = null;
    return;
  }

  // 优先使用命中检测，鼠标落在 token 上时更准确
  const target = candidates.find(item =>
    relX >= item.left && relX <= item.left + item.width &&
    relY >= item.top && relY <= item.top + item.height
  );

  if (target) {
    overIndex.value = target.index;
    insertSide.value = relX < target.midX ? 'before' : 'after';
    return;
  }

  // 命中空隙时，选择同一行或最近的一项，支持任意位置插入
  let nearest: typeof candidates[number] | null = null;
  let bestScore = Number.POSITIVE_INFINITY;
  for (const item of candidates) {
    const dx = relX - item.midX;
    const dy =
      relY < item.top ? item.top - relY :
      relY > item.top + item.height ? relY - (item.top + item.height) :
      0;
    const score = Math.abs(dx) + dy * 4;
    if (score < bestScore) {
      bestScore = score;
      nearest = item;
    }
  }

  if (!nearest) {
    overIndex.value = null;
    insertSide.value = null;
    return;
  }

  overIndex.value = nearest.index;
  insertSide.value = relX < nearest.midX ? 'before' : 'after';
}

function isQuickAddDragEvent(event: DragEvent): boolean {
  const types = Array.from(event.dataTransfer?.types ?? []);
  return types.includes('application/x-prompt-tag') || types.includes('text/plain');
}

function cleanupExternalDrag() {
  externalDraggingTag.value = null;
  cachedTokenRects.value = [];
  overIndex.value = null;
  insertSide.value = null;
  if (draggingIndex.value == null) {
    isDragging.value = false;
  }
}

function handleQuickAddDragStart(tag: string) {
  externalDraggingTag.value = tag;
  isDragging.value = true;
  overIndex.value = null;
  insertSide.value = null;
  cacheTokenRects();
}

function handleQuickAddDragEnd() {
  cleanupExternalDrag();
}

function handlePanelDragOver(event: DragEvent) {
  if (!isQuickAddDragEvent(event)) return;
  event.preventDefault();
  if (event.dataTransfer) {
    event.dataTransfer.dropEffect = 'copy';
  }
  if (!externalDraggingTag.value) {
    externalDraggingTag.value =
      event.dataTransfer?.getData('application/x-prompt-tag') ||
      event.dataTransfer?.getData('text/plain') ||
      null;
  }
  isDragging.value = true;
  if (!cachedTokenRects.value.length && tokens.value.length) {
    cacheTokenRects();
  }
  updateOverIndexAndSideFast(event.clientX, event.clientY, null);
}

function handlePanelDragLeave(event: DragEvent) {
  if (!externalDraggingTag.value) return;
  const dragContainer = tokenMappingRef.value?.dragContainer;
  if (!dragContainer) return;
  const related = event.relatedTarget as Node | null;
  if (related && dragContainer.contains(related)) return;
  const rect = dragContainer.getBoundingClientRect();
  const inside =
    event.clientX >= rect.left &&
    event.clientX <= rect.right &&
    event.clientY >= rect.top &&
    event.clientY <= rect.bottom;
  if (!inside) {
    overIndex.value = null;
    insertSide.value = null;
  }
}

function insertTagIntoTokens(tag: string, targetIndex: number | null, side: 'before' | 'after' | null) {
  const list = splitTokensLocal(text.value);
  let insertAt = list.length;
  if (targetIndex != null && side) {
    insertAt = targetIndex + (side === 'after' ? 1 : 0);
  }
  insertAt = Math.max(0, Math.min(insertAt, list.length));
  list.splice(insertAt, 0, normalizeToken(tag));
  applyFullPrompt(list.join(', '));
  showNotification('已插入提示词', 'success');
}

function handlePanelDropTag(event: DragEvent) {
  if (!isQuickAddDragEvent(event)) return;
  event.preventDefault();
  const tag =
    externalDraggingTag.value ||
    event.dataTransfer?.getData('application/x-prompt-tag') ||
    event.dataTransfer?.getData('text/plain') ||
    '';
  if (!tag) {
    cleanupExternalDrag();
    return;
  }
  insertTagIntoTokens(tag, overIndex.value, insertSide.value);
  cleanupExternalDrag();
}

function commitEdit(value: string) {
  const i = tokenMappingRef.value?.editingIndex;
  if (i == null) return;
  const tokens = splitTokensLocal(text.value);
  if (i >= 0 && i < tokens.length) {
    tokens[i] = normalizeToken(value);
    applyFullPrompt(tokens.join(', '));
  }
}

function showAddMap(i: number) {
  const token = tokens.value[i];
  if (!token) return;
  translationTargetToken.value = token;
  showTranslationPopup.value = true;
}

function removeToken(i: number) {
  const tokens = splitTokensLocal(text.value);
  if (i < 0 || i >= tokens.length) return;
  tokens.splice(i, 1);
  applyFullPrompt(tokens.join(', '));
}
function addTokenAfter(i: number) {
  const tokens = splitTokensLocal(text.value);
  tokens.splice(i + 1, 0, normalizeToken('new_token'));
  applyFullPrompt(tokens.join(', '));
}

function savePreset() {
  if (!presetName.value.trim()) {
    showNotification('请输入预设名称', 'error');
    return;
  }

  const name = presetName.value.trim();

  // 只保存到新的扩展预设系统
  const folderId = selectedFolderId.value || store.presetManagement?.settings?.defaultFolder;
  store.createExtendedPreset({
    name: name,
    type: 'positive',
    content: store.promptText,
    description: '从编辑器快速保存',
    folderId: folderId,
    isFavorite: saveAsFavorite.value
  });

  showNotification(
    saveAsFavorite.value
      ? `预设「${name}」已保存并收藏`
      : `预设「${name}」已保存到预设管理`,
    'success'
  );
  presetName.value = '';
}

// 预设下拉组件的事件处理
function handlePresetLoad(name: string) {
  // 优先从扩展预设中查找
  const extendedPreset = store.extendedPresets.find(p => p.name === name);
  if (extendedPreset) {
    store.setPromptTextRaw(extendedPreset.content);
    text.value = extendedPreset.content;
  } else {
    // 回退到旧预设系统
    store.loadPreset(name);
    text.value = store.promptText;
  }
  showNotification(`已加载预设「${name}」`, 'success');
}

function handlePresetSave(name: string) {
  store.savePreset(name);
  showNotification(`预设「${name}」已保存`, 'success');
}

function handlePresetDelete(name: string) {
  store.deletePreset(name);
  showNotification(`预设「${name}」已删除`, 'info');
}

function handlePresetRename(oldName: string, newName: string) {
  store.renamePreset(oldName, newName);
  showNotification(`预设已重命名为「${newName}」`, 'success');
}


const unmappedTokens = computed(() => {
  return tokens.value.filter(k => isUnmapped(k));
});

function isUnmapped(key: string): boolean {
  void store.mappingVersion; // 追踪映射变更以触发响应式刷新
  const { core } = parseDetailedToken(key);
  const tag = store.getTagByKey(core);
  return !tag || !tag.translation?.[selectedLang.value];
}

function handleApplyTranslation(results: { key: string; trans: string }[]) {
  results.forEach(({ key, trans }) => {
    store.addMapping(key, selectedLang.value, trans);
  });
  showNotification(`已添加 ${results.length} 条映射`, 'success');
}

function displayTrans(key: string): string {
  void store.mappingVersion; // 追踪映射变更以触发响应式刷新
  const { core, weight, wrappers, prefix, suffix } = parseDetailedToken(key);
  const tag = store.getTagByKey(core);
  const translatedCore = tag?.translation?.[selectedLang.value] ?? tag?.key ?? core;

  return constructToken(translatedCore, weight, wrappers, prefix, suffix);
}

function isRemoveDisabled(token: string): boolean {
  const info = getTokenWrapperInfo(token);
  return info.wrapperCount === 0 && !hasWeightSuffix(token);
}
</script>

<template>
  <div class="pe-root">
    <EditorToolbar :languages="store.languages as LangCode[]" v-model:selected-lang="selectedLang"
      v-model:preset-name="presetName" v-model:selected-folder-id="selectedFolderId"
      v-model:save-as-favorite="saveAsFavorite"
      v-model:show-preset-dropdown="showPresetDropdown" :folder-tree="folderTree" :flattened-folders="flattenedFolders"
      @copy="copyLeft" @save-preset="savePreset" @preset-load="handlePresetLoad" @preset-save="handlePresetSave"
      @preset-delete="handlePresetDelete" @preset-rename="handlePresetRename" />

    <div class="pe-main">
      <EditorInput ref="editorInputRef" v-model:text="text" v-model:numeric-mode="numericMode"
        v-model:bracket-style="bracketStyle" v-model:priority-step="priorityStep" :suggestions="suggestions"
        :get-suggestions="(prefix, limit) => store.getSuggestions(prefix, limit)"
        @update-suggestions="updateSuggestionsFromText" @copy="copyLeft" @replace-cn-comma="replaceCnComma"
        @format-prompt="formatPrompt" @unify-priority="unifyPriorityStyle" @toggle-underscore="toggleUnderscoreSpace"
        @add-tag="handleAddTag" @drag-tag-start="handleQuickAddDragStart" @drag-tag-end="handleQuickAddDragEnd"
        @locate-token="(i) => activeTokenIndex = i >= 0 ? i : null" />

      <TokenMappingPanel ref="tokenMappingRef" :tokens="tokens" :selected-lang="selectedLang"
        v-model:view-mode="viewMode" :dragging-index="draggingIndex" :over-index="overIndex" :insert-side="insertSide"
        :is-dragging="isDragging" :edit-suggestions="editSuggestions" :numeric-mode="numericMode"
        :active-index="activeTokenIndex"
        :display-trans="displayTrans" :is-unmapped="isUnmapped" :get-token-wrapper-info="getTokenWrapperInfo"
        :has-weight-suffix="hasWeightSuffix" :get-suggestions="(prefix, limit) => store.getSuggestions(prefix, limit)"
        @pointer-down="onPointerDown" @panel-dragover="handlePanelDragOver" @panel-dragleave="handlePanelDragLeave"
        @drop-tag="handlePanelDropTag" @begin-edit="(i) => editingIndex = i" @commit-edit="commitEdit"
        @cancel-edit="() => editingIndex = null" @show-add-map="showAddMap" @add-wrapper="addWrapperToToken"
        @remove-wrapper="removeWrapperFromToken" @remove-token="removeToken" @add-token-after="addTokenAfter"
        @show-translation-popup="() => { translationTargetToken = null; showTranslationPopup = true; }"
        @update-edit-value="updateEditSuggestionsFromValue" />
    </div>

    <!-- 翻译弹窗 -->
    <TranslationPopup :visible="showTranslationPopup" :tokens="translationTokens" :target-lang="selectedLang"
      @close="() => { showTranslationPopup = false; translationTargetToken = null; }" @apply="handleApplyTranslation" />

    <!-- 通知组件 -->
    <NotificationToast :message="notification.message" :type="notification.type" :show="notification.show" />
  </div>
</template>

<style scoped>
.pe-root {
  display: flex;
  flex-direction: column;
  height: 100%;
  background-color: var(--color-bg-primary);
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', sans-serif;
}

.pe-main {
  display: grid;
  grid-template-columns: 1fr 1fr;
  flex: 1;
  min-height: 0;
  gap: 1px;
  background-color: var(--color-border);
  width: 100%;
  max-width: 1400px;
  margin: 0 auto;
}

/* 响应式设计 */
@media (max-width: 1024px) {
  .pe-main {
    grid-template-columns: 1fr;
    grid-template-rows: auto 1fr;
  }

  .pe-left-pane {
    border-bottom: 1px solid var(--color-border);
  }
}
</style>
