import React, { useState, useEffect, useRef, useMemo } from 'react';
import Editor, { OnMount } from '@monaco-editor/react';
import { Button, message, Modal, Input, Form, Dropdown, MenuProps, Tooltip, Select, Tabs } from 'antd';
import { PlayCircleOutlined, SaveOutlined, FormatPainterOutlined, SettingOutlined, CloseOutlined, StopOutlined } from '@ant-design/icons';
import { format } from 'sql-formatter';
import { v4 as uuidv4 } from 'uuid';
import { TabData, ColumnDefinition } from '../types';
import { useStore } from '../store';
import { DBQuery, DBQueryWithCancel, DBGetTables, DBGetAllColumns, DBGetDatabases, DBGetColumns, CancelQuery, GenerateQueryID } from '../../wailsjs/go/app/App';
import DataGrid, { GONAVI_ROW_KEY } from './DataGrid';
import { getDataSourceCapabilities } from '../utils/dataSourceCapabilities';
import { convertMongoShellToJsonCommand } from '../utils/mongodb';
import { getShortcutDisplay, isEditableElement, isShortcutMatch } from '../utils/shortcuts';

const SQL_KEYWORDS = [
    'SELECT', 'FROM', 'WHERE', 'LIMIT', 'INSERT', 'UPDATE', 'DELETE', 'JOIN', 'LEFT', 'RIGHT',
    'INNER', 'OUTER', 'ON', 'GROUP BY', 'ORDER BY', 'AS', 'AND', 'OR', 'NOT', 'NULL', 'IS',
    'IN', 'VALUES', 'SET', 'CREATE', 'TABLE', 'DROP', 'ALTER', 'ADD', 'MODIFY', 'CHANGE',
    'COLUMN', 'KEY', 'PRIMARY', 'FOREIGN', 'REFERENCES', 'CONSTRAINT', 'DEFAULT', 'AUTO_INCREMENT',
    'COMMENT', 'SHOW', 'DESCRIBE', 'EXPLAIN',
];

const QueryEditor: React.FC<{ tab: TabData }> = ({ tab }) => {
  const [query, setQuery] = useState(tab.query || 'SELECT * FROM ');
  
  type ResultSet = {
      key: string;
      sql: string;
      exportSql?: string;
      rows: any[];
      columns: string[];
      tableName?: string;
      pkColumns: string[];
      readOnly: boolean;
      truncated?: boolean;
      pkLoading?: boolean;
  };

  // Result Sets
  const [resultSets, setResultSets] = useState<ResultSet[]>([]);
  const [activeResultKey, setActiveResultKey] = useState<string>('');
  
  const [loading, setLoading] = useState(false);
  const [currentQueryId, setCurrentQueryId] = useState<string>('');
  const runSeqRef = useRef(0);
  const currentQueryIdRef = useRef('');
  const [isSaveModalOpen, setIsSaveModalOpen] = useState(false);
  const [saveForm] = Form.useForm();
  
  // Database Selection
  const [currentConnectionId, setCurrentConnectionId] = useState<string>(tab.connectionId);
  const [currentDb, setCurrentDb] = useState<string>(tab.dbName || '');
  const [dbList, setDbList] = useState<string[]>([]);

  // Resizing state
  const [editorHeight, setEditorHeight] = useState(300);
  const editorRef = useRef<any>(null);
  const monacoRef = useRef<any>(null);
  const lastExternalQueryRef = useRef<string>(tab.query || '');
  const dragRef = useRef<{ startY: number, startHeight: number } | null>(null);
  const queryEditorRootRef = useRef<HTMLDivElement | null>(null);
  const editorPaneRef = useRef<HTMLDivElement | null>(null);
  const tablesRef = useRef<{dbName: string, tableName: string}[]>([]); // Store tables for autocomplete (cross-db)
  const allColumnsRef = useRef<{dbName: string, tableName: string, name: string, type: string}[]>([]); // Store all columns (cross-db)
  const visibleDbsRef = useRef<string[]>([]); // Store visible databases for cross-db intellisense

  const connections = useStore(state => state.connections);
  const queryCapableConnections = useMemo(
      () => connections.filter(c => getDataSourceCapabilities(c.config).supportsQueryEditor),
      [connections]
  );
  const addSqlLog = useStore(state => state.addSqlLog);
  const addTab = useStore(state => state.addTab);
  const savedQueries = useStore(state => state.savedQueries);
  const currentConnectionIdRef = useRef(currentConnectionId);
  const currentDbRef = useRef(currentDb);
  const connectionsRef = useRef(connections);
  const columnsCacheRef = useRef<Record<string, ColumnDefinition[]>>({});
  const saveQuery = useStore(state => state.saveQuery);
  const theme = useStore(state => state.theme);
  const darkMode = theme === 'dark';
  const sqlFormatOptions = useStore(state => state.sqlFormatOptions);
  const setSqlFormatOptions = useStore(state => state.setSqlFormatOptions);
  const queryOptions = useStore(state => state.queryOptions);
  const setQueryOptions = useStore(state => state.setQueryOptions);
  const shortcutOptions = useStore(state => state.shortcutOptions);
  const activeTabId = useStore(state => state.activeTabId);

  const currentSavedQuery = useMemo(() => {
      const savedId = String(tab.savedQueryId || '').trim();
      if (savedId) {
          return savedQueries.find((item) => item.id === savedId) || null;
      }
      const tabId = String(tab.id || '').trim();
      if (!tabId) {
          return null;
      }
      return savedQueries.find((item) => item.id === tabId) || null;
  }, [savedQueries, tab.id, tab.savedQueryId]);

  useEffect(() => {
      currentConnectionIdRef.current = currentConnectionId;
  }, [currentConnectionId]);

  useEffect(() => {
      if (!queryCapableConnections.some(c => c.id === currentConnectionId)) {
          const fallback = queryCapableConnections[0]?.id || '';
          if (fallback && fallback !== currentConnectionId) {
              setCurrentConnectionId(fallback);
              setCurrentDb('');
          }
      }
  }, [queryCapableConnections, currentConnectionId]);

  useEffect(() => {
      currentDbRef.current = currentDb;
  }, [currentDb]);

  useEffect(() => {
      connectionsRef.current = connections;
  }, [connections]);

  const getCurrentQuery = () => {
      const val = editorRef.current?.getValue?.();
      if (typeof val === 'string') return val;
      return query || '';
  };

  const syncQueryToEditor = (sql: string) => {
      const next = sql || '';
      setQuery(next);
      const editor = editorRef.current;
      if (editor && editor.getValue?.() !== next) {
          editor.setValue(next);
      }
  };

  // If opening a saved query, load its SQL
  useEffect(() => {
      const incoming = tab.query || '';
      if (incoming === lastExternalQueryRef.current) {
          return;
      }
      lastExternalQueryRef.current = incoming;
      syncQueryToEditor(incoming || 'SELECT * FROM ');
  }, [tab.id, tab.query]);

  // Fetch Database List
  useEffect(() => {
      const fetchDbs = async () => {
          const conn = connections.find(c => c.id === currentConnectionId);
          if (!conn) return;

          const config = {
            ...conn.config,
            port: Number(conn.config.port),
            password: conn.config.password || "",
            database: conn.config.database || "",
            useSSH: conn.config.useSSH || false,
            ssh: conn.config.ssh || { host: "", port: 22, user: "", password: "", keyPath: "" }
          };

          const res = await DBGetDatabases(config as any);
          if (res.success && Array.isArray(res.data)) {
              let dbs = res.data.map((row: any) => row.Database || row.database);

              // 过滤只显示 includeDatabases 中配置的数据库
              const includeDbs = conn.includeDatabases;
              if (includeDbs && includeDbs.length > 0) {
                  dbs = dbs.filter((db: string) => includeDbs.includes(db));
              }

              // 存储可见数据库列表用于跨库智能提示
              visibleDbsRef.current = dbs;

              setDbList(dbs);
              if (!currentDbRef.current) {
                  if (conn.config.database && dbs.includes(conn.config.database)) setCurrentDb(conn.config.database);
                  else if (dbs.length > 0 && dbs[0] !== 'information_schema') setCurrentDb(dbs[0]);
              }
          } else {
              visibleDbsRef.current = [];
              setDbList([]);
          }
      };
      fetchDbs();
  }, [currentConnectionId, connections]);

  // Fetch Metadata for Autocomplete (Cross-database)
  useEffect(() => {
      const fetchMetadata = async () => {
          const conn = connections.find(c => c.id === currentConnectionId);
          if (!conn) return;

          const visibleDbs = visibleDbsRef.current;
          if (!visibleDbs || visibleDbs.length === 0) return;

          const config = {
            ...conn.config,
            port: Number(conn.config.port),
            password: conn.config.password || "",
            database: conn.config.database || "",
            useSSH: conn.config.useSSH || false,
            ssh: conn.config.ssh || { host: "", port: 22, user: "", password: "", keyPath: "" }
          };

          // 加载所有可见数据库的表
          const allTables: {dbName: string, tableName: string}[] = [];
          const allColumns: {dbName: string, tableName: string, name: string, type: string}[] = [];

          for (const dbName of visibleDbs) {
              // 获取表
              const resTables = await DBGetTables(config as any, dbName);
              if (resTables.success && Array.isArray(resTables.data)) {
                  const tableNames = resTables.data.map((row: any) => Object.values(row)[0] as string);
                  tableNames.forEach((tableName: string) => {
                      allTables.push({ dbName, tableName });
                  });
              }

              // 获取列 (所有数据库类型都支持 DBGetAllColumns)
              const resCols = await DBGetAllColumns(config as any, dbName);
              if (resCols.success && Array.isArray(resCols.data)) {
                  resCols.data.forEach((col: any) => {
                      allColumns.push({
                          dbName,
                          tableName: col.tableName,
                          name: col.name,
                          type: col.type
                      });
                  });
              }
          }

          tablesRef.current = allTables;
          allColumnsRef.current = allColumns;
      };
      fetchMetadata();
  }, [currentConnectionId, connections, dbList]); // dbList 变化时触发重新加载

  // Query ID management helpers
  const setQueryId = (id: string) => {
      currentQueryIdRef.current = id;
      setCurrentQueryId(id);
  };

  const clearQueryId = () => {
      currentQueryIdRef.current = '';
      setCurrentQueryId('');
  };

  // Handle Resizing
  const handleMouseDown = (e: React.MouseEvent) => {
      e.preventDefault();
      dragRef.current = { startY: e.clientY, startHeight: editorHeight };
      document.addEventListener('mousemove', handleMouseMove);
      document.addEventListener('mouseup', handleMouseUp);
  };

  const handleMouseMove = (e: MouseEvent) => {
      if (!dragRef.current) return;
      const delta = e.clientY - dragRef.current.startY;
      const newHeight = Math.max(100, Math.min(window.innerHeight - 200, dragRef.current.startHeight + delta));
      setEditorHeight(newHeight);
  };

  const handleMouseUp = () => {
      dragRef.current = null;
      document.removeEventListener('mousemove', handleMouseMove);
      document.removeEventListener('mouseup', handleMouseUp);
  };

  // Setup Autocomplete and Editor
  const handleEditorDidMount: OnMount = (editor, monaco) => {
      editorRef.current = editor;
      monacoRef.current = monaco;

      // 应用透明主题（主题已在 main.tsx 全局注册）
      monaco.editor.setTheme(darkMode ? 'transparent-dark' : 'transparent-light');

      monaco.languages.registerCompletionItemProvider('sql', {
          triggerCharacters: ['.'],
          provideCompletionItems: async (model: any, position: any) => {
              const word = model.getWordUntilPosition(position);
              const range = {
                  startLineNumber: position.lineNumber,
                  endLineNumber: position.lineNumber,
                  startColumn: word.startColumn,
                  endColumn: word.endColumn,
              };

              const stripQuotes = (ident: string) => {
                  let raw = (ident || '').trim();
                  if (!raw) return raw;
                  const first = raw[0];
                  const last = raw[raw.length - 1];
                  if ((first === '`' && last === '`') || (first === '"' && last === '"')) {
                      raw = raw.slice(1, -1);
                  }
                  return raw.trim();
              };

              const normalizeQualifiedName = (ident: string) => {
                  const raw = (ident || '').trim();
                  if (!raw) return raw;
                  return raw
                      .split('.')
                      .map(p => stripQuotes(p.trim()))
                      .filter(Boolean)
                      .join('.');
              };

              const getLastPart = (qualified: string) => {
                  const raw = normalizeQualifiedName(qualified);
                  if (!raw) return raw;
                  const parts = raw.split('.').filter(Boolean);
                  return parts[parts.length - 1] || raw;
              };

              const splitSchemaAndTable = (qualified: string): { schema: string; table: string } => {
                  const raw = normalizeQualifiedName(qualified);
                  if (!raw) return { schema: '', table: '' };
                  const parts = raw.split('.').filter(Boolean);
                  if (parts.length >= 2) {
                      return {
                          schema: parts[parts.length - 2] || '',
                          table: parts[parts.length - 1] || '',
                      };
                  }
                  return { schema: '', table: parts[0] || '' };
              };

              const buildConnConfig = () => {
                  const connId = currentConnectionIdRef.current;
                  const conn = connectionsRef.current.find(c => c.id === connId);
                  if (!conn) return null;
                  return {
                      ...conn.config,
                      port: Number(conn.config.port),
                      password: conn.config.password || "",
                      database: conn.config.database || "",
                      useSSH: conn.config.useSSH || false,
                      ssh: conn.config.ssh || { host: "", port: 22, user: "", password: "", keyPath: "" }
                  };
              };

              const getColumnsByDB = async (tableIdent: string) => {
                  const connId = currentConnectionIdRef.current;
                  const dbName = currentDbRef.current;
                  if (!connId || !dbName) return [] as ColumnDefinition[];
                  const key = `${connId}|${dbName}|${tableIdent}`;
                  const cached = columnsCacheRef.current[key];
                  if (cached) return cached;

                  const config = buildConnConfig();
                  if (!config) return [] as ColumnDefinition[];

                  const res = await DBGetColumns(config as any, dbName, tableIdent);
                  if (res?.success && Array.isArray(res.data)) {
                      const cols = res.data as ColumnDefinition[];
                      columnsCacheRef.current[key] = cols;
                      return cols;
                  }
                  return [] as ColumnDefinition[];
              };

              const fullText = model.getValue();

              // 获取当前行光标前的内容
              const linePrefix = model.getLineContent(position.lineNumber).slice(0, position.column - 1);

              // 0) 三段式 db.table.column 格式：当输入 db.table. 时提示列
              const threePartMatch = linePrefix.match(/([`"]?[\w]+[`"]?)\.([`"]?[\w]+[`"]?)\.(\w*)$/);
              if (threePartMatch) {
                  const dbPart = stripQuotes(threePartMatch[1]);
                  const tablePart = stripQuotes(threePartMatch[2]);
                  const colPrefix = (threePartMatch[3] || '').toLowerCase();

                  // 在 allColumnsRef 中查找匹配的列
                  const cols = allColumnsRef.current.filter(c =>
                      (c.dbName || '').toLowerCase() === dbPart.toLowerCase() &&
                      (c.tableName || '').toLowerCase() === tablePart.toLowerCase()
                  );

                  const filtered = colPrefix
                      ? cols.filter(c => (c.name || '').toLowerCase().startsWith(colPrefix))
                      : cols;

                  const suggestions = filtered.map(c => ({
                      label: c.name,
                      kind: monaco.languages.CompletionItemKind.Field,
                      insertText: c.name,
                      detail: `${c.type} (${c.dbName}.${c.tableName})`,
                      range,
                      sortText: '0' + c.name
                  }));
                  return { suggestions };
              }

              // 1) 两段式 qualifier.xxx 格式
              const qualifierMatch = linePrefix.match(/([`"]?[A-Za-z_][\w]*[`"]?)\.(\w*)$/);
              if (qualifierMatch) {
                  const qualifier = stripQuotes(qualifierMatch[1]);
                  const prefix = (qualifierMatch[2] || '').toLowerCase();
                  const qualifierLower = qualifier.toLowerCase();

                  // 首先检查 qualifier 是否是数据库名（跨库表提示）
                  const visibleDbs = visibleDbsRef.current;
                  if (visibleDbs.some(db => db.toLowerCase() === qualifierLower)) {
                      // qualifier 是数据库名，提示该库的表
                      const tables = tablesRef.current.filter(t =>
                          (t.dbName || '').toLowerCase() === qualifierLower
                      );
                      const filtered = prefix
                          ? tables.filter(t => (t.tableName || '').toLowerCase().startsWith(prefix))
                          : tables;

                      const suggestions = filtered.map(t => ({
                          label: t.tableName,
                          kind: monaco.languages.CompletionItemKind.Class,
                          insertText: t.tableName,
                          detail: `Table (${t.dbName})`,
                          range,
                          sortText: '0' + t.tableName
                      }));
                      return { suggestions };
                  }

                  // qualifier 是 schema（如 dbo/public）时，仅补全表名，避免输入 dbo. 后再补成 dbo.dbo.table
                  const schemaTables = tablesRef.current
                      .map(t => {
                          const parsed = splitSchemaAndTable(t.tableName || '');
                          return {
                              dbName: t.dbName || '',
                              schema: parsed.schema,
                              table: parsed.table,
                          };
                      })
                      .filter(t => t.schema.toLowerCase() === qualifierLower && !!t.table);

                  if (schemaTables.length > 0) {
                      const filtered = prefix
                          ? schemaTables.filter(t => t.table.toLowerCase().startsWith(prefix))
                          : schemaTables;

                      const suggestions = filtered.map(t => ({
                          label: t.table,
                          kind: monaco.languages.CompletionItemKind.Class,
                          insertText: t.table,
                          detail: `Table (${t.dbName}${t.schema ? '.' + t.schema : ''})`,
                          range,
                          sortText: '0' + t.table
                      }));
                      return { suggestions };
                  }

                  // 否则检查是否是表别名或表名，提示列
                  const reserved = new Set([
                      'where', 'on', 'group', 'order', 'limit', 'having',
                      'left', 'right', 'inner', 'outer', 'full', 'cross', 'join',
                      'union', 'except', 'intersect', 'as', 'set', 'values', 'returning',
                  ]);

                  const aliasMap: Record<string, {dbName: string, tableName: string}> = {};
                  // Capture table and optional alias, support db.table format
                  const aliasRegex = /\b(?:FROM|JOIN|UPDATE|INTO|DELETE\s+FROM)\s+([`"]?[\w]+[`"]?(?:\s*\.\s*[`"]?[\w]+[`"]?)?)(?:\s+(?:AS\s+)?([`"]?[\w]+[`"]?))?/gi;
                  let m;
                  while ((m = aliasRegex.exec(fullText)) !== null) {
                      const tableIdent = normalizeQualifiedName(m[1] || '');
                      if (!tableIdent) continue;

                      // 解析 db.table 或 table 格式
                      const parts = tableIdent.split('.');
                      let dbName = currentDbRef.current || '';
                      let tableName = tableIdent;
                      if (parts.length === 2) {
                          dbName = parts[0];
                          tableName = parts[1];
                      }

                      const shortTable = getLastPart(tableIdent);
                      // 用表名作为 qualifier
                      if (shortTable) aliasMap[shortTable.toLowerCase()] = { dbName, tableName };

                      const a = stripQuotes(m[2] || '').trim();
                      if (!a) continue;
                      const al = a.toLowerCase();
                      if (reserved.has(al)) continue;
                      aliasMap[al] = { dbName, tableName };
                  }

                  const tableInfo = aliasMap[qualifier.toLowerCase()];
                  if (tableInfo) {
                      // Prefer preloaded MySQL all-columns cache
                      let cols: { name: string, type?: string, tableName?: string, dbName?: string }[] = [];
                      if (allColumnsRef.current.length > 0) {
                          cols = allColumnsRef.current
                              .filter(c =>
                                  (c.dbName || '').toLowerCase() === (tableInfo.dbName || '').toLowerCase() &&
                                  (c.tableName || '').toLowerCase() === (tableInfo.tableName || '').toLowerCase()
                              )
                              .map(c => ({ name: c.name, type: c.type, tableName: c.tableName, dbName: c.dbName }));
                      } else {
                          const dbCols = await getColumnsByDB(tableInfo.tableName);
                          cols = dbCols.map(c => ({ name: c.name, type: c.type, tableName: tableInfo.tableName }));
                      }

                      const filtered = prefix
                          ? cols.filter(c => (c.name || '').toLowerCase().startsWith(prefix))
                          : cols;

                      const suggestions = filtered.map(c => ({
                          label: c.name,
                          kind: monaco.languages.CompletionItemKind.Field,
                          insertText: c.name,
                          detail: c.type ? `${c.type} (${c.dbName ? c.dbName + '.' : ''}${c.tableName})` : (c.tableName ? `(${c.tableName})` : ''),
                          range,
                          sortText: '0' + c.name
                      }));
                      return { suggestions };
                  }
              }

              // 2) global/table/column completion
              const tableRegex = /\b(?:FROM|JOIN|UPDATE|INTO|DELETE\s+FROM)\s+([`"]?[\w]+[`"]?(?:\s*\.\s*[`"]?[\w]+[`"]?)?)/gi;
              const foundTables = new Set<string>();
              let match;
              while ((match = tableRegex.exec(fullText)) !== null) {
                  const t = normalizeQualifiedName(match[1] || '');
                  if (!t) continue;
                  // 存储完整标识 db.table 或 table
                  foundTables.add(t.toLowerCase());
              }

              const currentDatabase = currentDbRef.current || '';
              const wordPrefix = (word.word || '').toLowerCase();
              const startsWithPrefix = (candidate: string) => !wordPrefix || candidate.toLowerCase().startsWith(wordPrefix);
              const expectsTableName = /\b(?:FROM|JOIN|UPDATE|INTO|DELETE\s+FROM|TABLE|DESCRIBE|DESC|EXPLAIN)\s+[`"]?[\w.]*$/i.test(linePrefix.trim());
              const shouldBoostKeywords = !expectsTableName
                  && wordPrefix.length > 0
                  && SQL_KEYWORDS.some((keyword) => keyword.toLowerCase().startsWith(wordPrefix));
              const sortGroups = shouldBoostKeywords
                  ? { keyword: '00', columnCurrent: '10', columnOther: '11', tableCurrent: '20', tableOther: '21', db: '30' }
                  : expectsTableName
                      ? { keyword: '20', columnCurrent: '10', columnOther: '11', tableCurrent: '00', tableOther: '01', db: '30' }
                      : { keyword: '30', columnCurrent: '00', columnOther: '01', tableCurrent: '10', tableOther: '11', db: '20' };

              // 相关列提示：匹配 SQL 中引用的表（FROM/JOIN 等）
              // 权重最高，输入 WHERE 条件时优先显示
              const relevantColumns = allColumnsRef.current
                  .filter(c => {
                      const fullIdent = `${c.dbName}.${c.tableName}`.toLowerCase();
                      const shortIdent = (c.tableName || '').toLowerCase();
                      return (foundTables.has(fullIdent) || foundTables.has(shortIdent)) && startsWithPrefix(c.name || '');
                  })
                  .map(c => {
                      // 当前库的表字段优先级更高
                      const isCurrentDb = (c.dbName || '').toLowerCase() === currentDatabase.toLowerCase();
                      return {
                          label: c.name,
                          kind: monaco.languages.CompletionItemKind.Field,
                          insertText: c.name,
                          detail: `${c.type} (${c.dbName}.${c.tableName})`,
                          range,
                          sortText: isCurrentDb ? sortGroups.columnCurrent + c.name : sortGroups.columnOther + c.name,
                      };
                  });

              // 表提示：当前库显示表名，其他库显示 db.table 格式
              const tableSuggestions = tablesRef.current
                .filter(t => {
                    const isCurrentDb = (t.dbName || '').toLowerCase() === currentDatabase.toLowerCase();
                    const label = isCurrentDb ? t.tableName : `${t.dbName}.${t.tableName}`;
                    return startsWithPrefix(label || '');
                })
                .map(t => {
                  const isCurrentDb = (t.dbName || '').toLowerCase() === currentDatabase.toLowerCase();
                  const label = isCurrentDb ? t.tableName : `${t.dbName}.${t.tableName}`;
                  const insertText = isCurrentDb ? t.tableName : `${t.dbName}.${t.tableName}`;
                  return {
                      label,
                      kind: monaco.languages.CompletionItemKind.Class,
                      insertText,
                      detail: `Table (${t.dbName})`,
                      range,
                      sortText: isCurrentDb ? sortGroups.tableCurrent + t.tableName : sortGroups.tableOther + t.tableName,
                  };
              });

              // 数据库提示
              const dbSuggestions = visibleDbsRef.current
                  .filter((db) => startsWithPrefix(db))
                  .map(db => ({
                      label: db,
                      kind: monaco.languages.CompletionItemKind.Module,
                      insertText: db,
                      detail: 'Database',
                      range,
                      sortText: sortGroups.db + db,
                  }));

              // 关键字提示
              const keywordSuggestions = SQL_KEYWORDS
                  .filter((k) => startsWithPrefix(k))
                  .map(k => ({
                  label: k,
                  kind: monaco.languages.CompletionItemKind.Keyword,
                  insertText: k,
                  range,
                  sortText: sortGroups.keyword + k,
              }));

              const suggestions = [
                  ...relevantColumns,   // FROM 表的列最优先
                  ...tableSuggestions,  // 表次之
                  ...dbSuggestions,     // 数据库
                  ...keywordSuggestions // 关键字最后
              ];
              return { suggestions };
          }
      });
  };

  const handleFormat = () => {
      try {
          const formatted = format(getCurrentQuery(), { language: 'mysql', keywordCase: sqlFormatOptions.keywordCase });
          syncQueryToEditor(formatted);
      } catch (e) {
          message.error("格式化失败: SQL 语法可能有误");
      }
  };

  const formatSettingsMenu: MenuProps['items'] = [
      { 
          key: 'upper', 
          label: '关键字大写', 
          icon: sqlFormatOptions.keywordCase === 'upper' ? '✓' : undefined,
          onClick: () => setSqlFormatOptions({ keywordCase: 'upper' }) 
      },
      { 
          key: 'lower', 
          label: '关键字小写', 
          icon: sqlFormatOptions.keywordCase === 'lower' ? '✓' : undefined,
          onClick: () => setSqlFormatOptions({ keywordCase: 'lower' }) 
      },
      { type: 'divider' },
      {
          key: 'shortcut-settings',
          label: '快捷键管理...',
          onClick: () => window.dispatchEvent(new CustomEvent('gonavi:open-shortcut-settings')),
      },
  ];

  const splitSQLStatements = (sql: string): string[] => {
    const text = (sql || '').replace(/\r\n/g, '\n');
    const statements: string[] = [];

    let cur = '';
    let inSingle = false;
    let inDouble = false;
    let inBacktick = false;
    let escaped = false;
    let inLineComment = false;
    let inBlockComment = false;
    let dollarTag: string | null = null; // postgres/kingbase: $$...$$ or $tag$...$tag$

    const push = () => {
        const s = cur.trim();
        if (s) statements.push(s);
        cur = '';
    };

    const isWS = (ch: string) => ch === ' ' || ch === '\t' || ch === '\n' || ch === '\r';

    for (let i = 0; i < text.length; i++) {
        const ch = text[i];
        const next = i + 1 < text.length ? text[i + 1] : '';
        const prev = i > 0 ? text[i - 1] : '';
        const next2 = i + 2 < text.length ? text[i + 2] : '';

        if (!inSingle && !inDouble && !inBacktick) {
            if (inLineComment) {
                cur += ch;
                if (ch === '\n') inLineComment = false;
                continue;
            }

            if (inBlockComment) {
                cur += ch;
                if (ch === '*' && next === '/') {
                    cur += next;
                    i++;
                    inBlockComment = false;
                }
                continue;
            }

            // Start comments
            if (ch === '/' && next === '*') {
                cur += ch + next;
                i++;
                inBlockComment = true;
                continue;
            }
            if (ch === '#') {
                cur += ch;
                inLineComment = true;
                continue;
            }
            if (ch === '-' && next === '-' && (i === 0 || isWS(prev)) && (next2 === '' || isWS(next2))) {
                cur += ch + next;
                i++;
                inLineComment = true;
                continue;
            }

            // Dollar-quoted strings (PG/Kingbase)
            if (dollarTag) {
                if (text.startsWith(dollarTag, i)) {
                    cur += dollarTag;
                    i += dollarTag.length - 1;
                    dollarTag = null;
                } else {
                    cur += ch;
                }
                continue;
            }
            if (ch === '$') {
                const m = text.slice(i).match(/^\$[A-Za-z0-9_]*\$/);
                if (m && m[0]) {
                    dollarTag = m[0];
                    cur += dollarTag;
                    i += dollarTag.length - 1;
                    continue;
                }
            }
        }

        if (escaped) {
            cur += ch;
            escaped = false;
            continue;
        }

        if ((inSingle || inDouble) && ch === '\\') {
            cur += ch;
            escaped = true;
            continue;
        }

        if (!inDouble && !inBacktick && ch === '\'') {
            inSingle = !inSingle;
            cur += ch;
            continue;
        }
        if (!inSingle && !inBacktick && ch === '"') {
            inDouble = !inDouble;
            cur += ch;
            continue;
        }
        if (!inSingle && !inDouble && ch === '`') {
            inBacktick = !inBacktick;
            cur += ch;
            continue;
        }

        if (!inSingle && !inDouble && !inBacktick && !dollarTag && (ch === ';' || ch === '；')) {
            push();
            continue;
        }

        cur += ch;
    }

    push();
    return statements;
  };

  const getLeadingKeyword = (sql: string): string => {
      const text = (sql || '').replace(/\r\n/g, '\n');
      const isWS = (ch: string) => ch === ' ' || ch === '\t' || ch === '\n' || ch === '\r';
      const isWord = (ch: string) => /[A-Za-z0-9_]/.test(ch);

      let inSingle = false;
      let inDouble = false;
      let inBacktick = false;
      let escaped = false;
      let inLineComment = false;
      let inBlockComment = false;
      let dollarTag: string | null = null;

      for (let i = 0; i < text.length; i++) {
          const ch = text[i];
          const next = i + 1 < text.length ? text[i + 1] : '';
          const prev = i > 0 ? text[i - 1] : '';
          const next2 = i + 2 < text.length ? text[i + 2] : '';

          if (!inSingle && !inDouble && !inBacktick) {
              if (inLineComment) {
                  if (ch === '\n') inLineComment = false;
                  continue;
              }
              if (inBlockComment) {
                  if (ch === '*' && next === '/') {
                      i++;
                      inBlockComment = false;
                  }
                  continue;
              }

              if (ch === '/' && next === '*') {
                  i++;
                  inBlockComment = true;
                  continue;
              }
              if (ch === '#') {
                  inLineComment = true;
                  continue;
              }
              if (ch === '-' && next === '-' && (i === 0 || isWS(prev)) && (next2 === '' || isWS(next2))) {
                  i++;
                  inLineComment = true;
                  continue;
              }

              if (dollarTag) {
                  if (text.startsWith(dollarTag, i)) {
                      i += dollarTag.length - 1;
                      dollarTag = null;
                  }
                  continue;
              }
              if (ch === '$') {
                  const m = text.slice(i).match(/^\$[A-Za-z0-9_]*\$/);
                  if (m && m[0]) {
                      dollarTag = m[0];
                      i += dollarTag.length - 1;
                      continue;
                  }
              }
          }

          if (escaped) {
              escaped = false;
              continue;
          }
          if ((inSingle || inDouble) && ch === '\\') {
              escaped = true;
              continue;
          }

          if (!inDouble && !inBacktick && ch === '\'') {
              inSingle = !inSingle;
              continue;
          }
          if (!inSingle && !inBacktick && ch === '"') {
              inDouble = !inDouble;
              continue;
          }
          if (!inSingle && !inDouble && ch === '`') {
              inBacktick = !inBacktick;
              continue;
          }

          if (inSingle || inDouble || inBacktick || dollarTag) continue;
          if (isWS(ch)) continue;

          if (isWord(ch)) {
              let j = i;
              while (j < text.length && isWord(text[j])) j++;
              return text.slice(i, j).toLowerCase();
          }
          return '';
      }
      return '';
  };

  const splitSqlTail = (sql: string): { main: string; tail: string } => {
      const text = (sql || '').replace(/\r\n/g, '\n');
      const isWS = (ch: string) => ch === ' ' || ch === '\t' || ch === '\n' || ch === '\r';

      let inSingle = false;
      let inDouble = false;
      let inBacktick = false;
      let escaped = false;
      let inLineComment = false;
      let inBlockComment = false;
      let dollarTag: string | null = null;
      let lastMeaningful = -1;

      for (let i = 0; i < text.length; i++) {
          const ch = text[i];
          const next = i + 1 < text.length ? text[i + 1] : '';
          const prev = i > 0 ? text[i - 1] : '';
          const next2 = i + 2 < text.length ? text[i + 2] : '';

          if (!inSingle && !inDouble && !inBacktick) {
              if (dollarTag) {
                  if (text.startsWith(dollarTag, i)) {
                      lastMeaningful = i + dollarTag.length - 1;
                      i += dollarTag.length - 1;
                      dollarTag = null;
                  } else if (!isWS(ch)) {
                      lastMeaningful = i;
                  }
                  continue;
              }
              if (inLineComment) {
                  if (ch === '\n') inLineComment = false;
                  continue;
              }
              if (inBlockComment) {
                  if (ch === '*' && next === '/') {
                      i++;
                      inBlockComment = false;
                  }
                  continue;
              }

              // Start comments
              if (ch === '/' && next === '*') {
                  i++;
                  inBlockComment = true;
                  continue;
              }
              if (ch === '#') {
                  inLineComment = true;
                  continue;
              }
              if (ch === '-' && next === '-' && (i === 0 || isWS(prev)) && (next2 === '' || isWS(next2))) {
                  i++;
                  inLineComment = true;
                  continue;
              }

              if (ch === '$') {
                  const m = text.slice(i).match(/^\$[A-Za-z0-9_]*\$/);
                  if (m && m[0]) {
                      dollarTag = m[0];
                      lastMeaningful = i + dollarTag.length - 1;
                      i += dollarTag.length - 1;
                      continue;
                  }
              }
          }

          if (escaped) {
              escaped = false;
          } else if ((inSingle || inDouble) && ch === '\\') {
              escaped = true;
          } else {
              if (!inDouble && !inBacktick && ch === '\'') inSingle = !inSingle;
              else if (!inSingle && !inBacktick && ch === '"') inDouble = !inDouble;
              else if (!inSingle && !inDouble && ch === '`') inBacktick = !inBacktick;
          }

          if (!inLineComment && !inBlockComment && !isWS(ch)) {
              lastMeaningful = i;
          }
      }

      if (lastMeaningful < 0) return { main: '', tail: text };
      return { main: text.slice(0, lastMeaningful + 1), tail: text.slice(lastMeaningful + 1) };
  };

  const findTopLevelKeyword = (sql: string, keyword: string): number => {
      const text = sql;
      const kw = keyword.toLowerCase();
      const isWS = (ch: string) => ch === ' ' || ch === '\t' || ch === '\n' || ch === '\r';
      const isWord = (ch: string) => /[A-Za-z0-9_]/.test(ch);

      let inSingle = false;
      let inDouble = false;
      let inBacktick = false;
      let escaped = false;
      let inLineComment = false;
      let inBlockComment = false;
      let dollarTag: string | null = null;
      let parenDepth = 0;

      for (let i = 0; i < text.length; i++) {
          const ch = text[i];
          const next = i + 1 < text.length ? text[i + 1] : '';
          const prev = i > 0 ? text[i - 1] : '';
          const next2 = i + 2 < text.length ? text[i + 2] : '';

          if (!inSingle && !inDouble && !inBacktick) {
              if (inLineComment) {
                  if (ch === '\n') inLineComment = false;
                  continue;
              }
              if (inBlockComment) {
                  if (ch === '*' && next === '/') {
                      i++;
                      inBlockComment = false;
                  }
                  continue;
              }

              if (ch === '/' && next === '*') {
                  i++;
                  inBlockComment = true;
                  continue;
              }
              if (ch === '#') {
                  inLineComment = true;
                  continue;
              }
              if (ch === '-' && next === '-' && (i === 0 || isWS(prev)) && (next2 === '' || isWS(next2))) {
                  i++;
                  inLineComment = true;
                  continue;
              }

              if (dollarTag) {
                  if (text.startsWith(dollarTag, i)) {
                      i += dollarTag.length - 1;
                      dollarTag = null;
                  }
                  continue;
              }
              if (ch === '$') {
                  const m = text.slice(i).match(/^\$[A-Za-z0-9_]*\$/);
                  if (m && m[0]) {
                      dollarTag = m[0];
                      i += dollarTag.length - 1;
                      continue;
                  }
              }
          }

          if (escaped) {
              escaped = false;
              continue;
          }
          if ((inSingle || inDouble) && ch === '\\') {
              escaped = true;
              continue;
          }

          if (!inDouble && !inBacktick && ch === '\'') {
              inSingle = !inSingle;
              continue;
          }
          if (!inSingle && !inBacktick && ch === '"') {
              inDouble = !inDouble;
              continue;
          }
          if (!inSingle && !inDouble && ch === '`') {
              inBacktick = !inBacktick;
              continue;
          }

          if (inSingle || inDouble || inBacktick || dollarTag) continue;

          if (ch === '(') { parenDepth++; continue; }
          if (ch === ')') { if (parenDepth > 0) parenDepth--; continue; }
          if (parenDepth !== 0) continue;

          if (!isWord(ch)) continue;

          if (text.slice(i, i + kw.length).toLowerCase() !== kw) continue;
          const before = i - 1 >= 0 ? text[i - 1] : '';
          const after = i + kw.length < text.length ? text[i + kw.length] : '';
          if ((before && isWord(before)) || (after && isWord(after))) continue;
          return i;
      }
      return -1;
  };

  const applyAutoLimit = (sql: string, dbType: string, maxRows: number): { sql: string; applied: boolean; maxRows: number } => {
      const normalizedType = (dbType || 'mysql').toLowerCase();
      const supportsLimit = normalizedType === 'mysql' || normalizedType === 'mariadb' || normalizedType === 'diros' || normalizedType === 'sphinx' || normalizedType === 'postgres' || normalizedType === 'kingbase' || normalizedType === 'sqlite' || normalizedType === 'duckdb' || normalizedType === 'tdengine' || normalizedType === 'clickhouse' || normalizedType === '';
      if (!supportsLimit) return { sql, applied: false, maxRows };
      if (!Number.isFinite(maxRows) || maxRows <= 0) return { sql, applied: false, maxRows };

      const { main, tail } = splitSqlTail(sql);
      if (!main.trim()) return { sql, applied: false, maxRows };

      const fromPos = findTopLevelKeyword(main, 'from');
      const limitPos = findTopLevelKeyword(main, 'limit');
      if (limitPos >= 0 && (fromPos < 0 || limitPos > fromPos)) return { sql, applied: false, maxRows };
      const fetchPos = findTopLevelKeyword(main, 'fetch');
      if (fetchPos >= 0 && (fromPos < 0 || fetchPos > fromPos)) return { sql, applied: false, maxRows };

      const offsetPos = findTopLevelKeyword(main, 'offset');
      const forPos = findTopLevelKeyword(main, 'for');
      const lockPos = findTopLevelKeyword(main, 'lock');

      const candidates = [offsetPos, forPos, lockPos]
          .filter(pos => pos >= 0 && (fromPos < 0 || pos > fromPos));

      const insertAt = candidates.length > 0 ? Math.min(...candidates) : main.length;
      const before = main.slice(0, insertAt).trimEnd();
      const after = main.slice(insertAt).trimStart();
      const nextMain = [before, `LIMIT ${maxRows}`, after].filter(Boolean).join(' ').trim();
      return { sql: nextMain + tail, applied: true, maxRows };
  };

  const getSelectedSQL = (): string => {
      const editor = editorRef.current;
      if (!editor) return '';
      const model = editor.getModel?.();
      const selection = editor.getSelection?.();
      if (!model || !selection) return '';

      const selected = model.getValueInRange?.(selection) || '';
      if (typeof selected !== 'string') return '';
      if (!selected.trim()) return '';
      return selected;
  };

  const handleRun = async () => {
    const currentQuery = getCurrentQuery();
    if (!currentQuery.trim()) return;
    if (!currentDb) {
        message.error("请先选择数据库");
        return;
    }
    // 如果已有查询在运行，先取消它
    if (currentQueryIdRef.current) {
        try {
            await CancelQuery(currentQueryIdRef.current);
        } catch (error) {
            // 忽略取消错误，可能查询已完成
        }
        // 清除旧查询ID
        clearQueryId();
    }
    const runSeq = ++runSeqRef.current;
    setLoading(true);
    const runStartTime = Date.now();
    const conn = connections.find(c => c.id === currentConnectionId);
    if (!conn) {
        message.error("Connection not found");
        if (runSeqRef.current === runSeq) setLoading(false);
        return;
    }
    const connCaps = getDataSourceCapabilities(conn.config);
    if (!connCaps.supportsQueryEditor) {
        message.error("当前数据源不支持 SQL 查询编辑器，请使用对应专用页面。");
        if (runSeqRef.current === runSeq) setLoading(false);
        return;
    }

    const config = { 
        ...conn.config, 
        port: Number(conn.config.port),
        password: conn.config.password || "",
        database: conn.config.database || "",
        useSSH: conn.config.useSSH || false,
        ssh: conn.config.ssh || { host: "", port: 22, user: "", password: "", keyPath: "" }
    };

    try {
        const rawSQL = getSelectedSQL() || currentQuery;
        const dbType = String((config as any).type || 'mysql');
        const normalizedDbType = dbType.trim().toLowerCase();
        const normalizedRawSQL = String(rawSQL || '').replace(/；/g, ';');
        const splitInput = normalizedDbType === 'mongodb'
            ? normalizedRawSQL
                .replace(/^\s*\/\/.*$/gm, '')
                .replace(/^\s*#.*$/gm, '')
            : normalizedRawSQL;
        const statements = splitSQLStatements(splitInput);
        if (statements.length === 0) {
            message.info('没有可执行的 SQL。');
            setResultSets([]);
            setActiveResultKey('');
            return;
        }

        const nextResultSets: ResultSet[] = [];
        const maxRows = Number(queryOptions?.maxRows) || 0;
        const forceReadOnlyResult = connCaps.forceReadOnlyQueryResult;
        const wantsLimitProbe = Number.isFinite(maxRows) && maxRows > 0;
        const probeLimit = wantsLimitProbe ? (maxRows + 1) : 0;
        let anyTruncated = false;
        const pendingPk: Array<{ resultKey: string; tableName: string }> = [];

        for (let idx = 0; idx < statements.length; idx++) {
            const rawStatement = statements[idx];
            const leadingKeyword = getLeadingKeyword(rawStatement);
            const shouldAutoLimit = leadingKeyword === 'select' || leadingKeyword === 'with';

            const limitApplied = shouldAutoLimit && wantsLimitProbe;
            const limited = limitApplied ? applyAutoLimit(rawStatement, dbType, probeLimit) : { sql: rawStatement, applied: false, maxRows: probeLimit };
            let executedSql = limited.sql;
            if (String(dbType || '').trim().toLowerCase() === 'mongodb') {
                const shellConvert = convertMongoShellToJsonCommand(executedSql);
                if (shellConvert.recognized) {
                    if (shellConvert.error) {
                        const prefix = statements.length > 1 ? `第 ${idx + 1} 条语句执行失败：` : '';
                        message.error(prefix + shellConvert.error);
                        setResultSets([]);
                        setActiveResultKey('');
                        return;
                    }
                    if (shellConvert.command) {
                        executedSql = shellConvert.command;
                    }
                }
            }
            const startTime = Date.now();

            // Generate query ID for cancellation using backend UUID with fallback
            let queryId: string;
            try {
                queryId = await GenerateQueryID();
            } catch (error) {
                console.warn('GenerateQueryID failed, using local UUID fallback:', error);
                queryId = 'query-' + uuidv4();
            }
            setQueryId(queryId);

            const res = await DBQueryWithCancel(config as any, currentDb, executedSql, queryId);
            const duration = Date.now() - startTime;

            addSqlLog({
                id: `log-${Date.now()}-query-${idx + 1}`,
                timestamp: Date.now(),
                sql: executedSql,
                status: res.success ? 'success' : 'error',
                duration,
                message: res.success ? '' : res.message,
                affectedRows: (res.success && !Array.isArray(res.data)) ? (res.data as any).affectedRows : (Array.isArray(res.data) ? res.data.length : undefined),
                dbName: currentDb
            });

            if (!res.success) {
                // 检查是否为查询取消错误
                const errorMsg = res.message.toLowerCase();
                const isCancelledError = errorMsg.includes('context canceled') ||
                                         errorMsg.includes('查询已取消') ||
                                         errorMsg.includes('canceled') ||
                                         errorMsg.includes('cancelled') ||
                                         errorMsg.includes('statement canceled') ||
                                         errorMsg.includes('sql: statement canceled');

                // 确保不是超时错误
                const isTimeoutError = errorMsg.includes('context deadline exceeded') ||
                                       errorMsg.includes('timeout') ||
                                       errorMsg.includes('超时') ||
                                       errorMsg.includes('deadline exceeded');

                if (isCancelledError && !isTimeoutError) {
                    // 查询已被用户取消，不显示错误消息，清理状态
                    setResultSets([]);
                    setActiveResultKey('');
                    // 清除查询ID，与handleCancel保持一致
                    if (currentQueryIdRef.current) {
                        clearQueryId();
                    }
                    return;
                }

                const prefix = statements.length > 1 ? `第 ${idx + 1} 条语句执行失败：` : '';
                message.error(prefix + res.message);
                setResultSets([]);
                setActiveResultKey('');
                return;
            }

            if (Array.isArray(res.data)) {
                let rows = (res.data as any[]) || [];
                let truncated = false;
                if (limited.applied && Number.isFinite(maxRows) && maxRows > 0 && rows.length > maxRows) {
                    truncated = true;
                    anyTruncated = true;
                    rows = rows.slice(0, maxRows);
                }
                const cols = (res.fields && res.fields.length > 0)
                    ? (res.fields as string[])
                    : (rows.length > 0 ? Object.keys(rows[0]) : []);

                rows.forEach((row: any, i: number) => {
                    if (row && typeof row === 'object') row[GONAVI_ROW_KEY] = i;
                });

                let simpleTableName: string | undefined = undefined;
                const tableMatch = rawStatement.match(/^\s*SELECT\s+\*\s+FROM\s+[`"]?(\w+)[`"]?\s*(?:WHERE.*)?(?:ORDER BY.*)?(?:LIMIT.*)?$/i);
                if (tableMatch) {
                    simpleTableName = tableMatch[1];
                    if (!forceReadOnlyResult) {
                        pendingPk.push({ resultKey: `result-${idx + 1}`, tableName: simpleTableName });
                    }
                }

                nextResultSets.push({
                    key: `result-${idx + 1}`,
                    sql: rawStatement,
                    exportSql: limited.applied ? applyAutoLimit(rawStatement, dbType, Math.max(1, Number(maxRows) || 1)).sql : rawStatement,
                    rows,
                    columns: cols,
                    tableName: simpleTableName,
                    pkColumns: [],
                    readOnly: true,
                    pkLoading: !!simpleTableName,
                    truncated
                });
            } else {
                const affected = Number((res.data as any)?.affectedRows);
                if (Number.isFinite(affected)) {
                    const row = { affectedRows: affected };
                    (row as any)[GONAVI_ROW_KEY] = 0;
                    nextResultSets.push({
                        key: `result-${idx + 1}`,
                        sql: rawStatement,
                        exportSql: rawStatement,
                        rows: [row],
                        columns: ['affectedRows'],
                        pkColumns: [],
                        readOnly: true
                    });
                }
            }
        }

        setResultSets(nextResultSets);
        setActiveResultKey(nextResultSets[0]?.key || '');

        pendingPk.forEach(({ resultKey, tableName }) => {
            DBGetColumns(config as any, currentDb, tableName)
                .then((resCols: any) => {
                    if (runSeqRef.current !== runSeq) return;
                    if (!resCols?.success) {
                        setResultSets(prev => prev.map(rs => rs.key === resultKey ? { ...rs, pkLoading: false, readOnly: false } : rs));
                        return;
                    }
                    const primaryKeys = (resCols.data as ColumnDefinition[]).filter(c => c.key === 'PRI').map(c => c.name);
                    setResultSets(prev => prev.map(rs => rs.key === resultKey ? { ...rs, pkColumns: primaryKeys, pkLoading: false, readOnly: false } : rs));
                })
                .catch(() => {
                    if (runSeqRef.current !== runSeq) return;
                    setResultSets(prev => prev.map(rs => rs.key === resultKey ? { ...rs, pkLoading: false, readOnly: false } : rs));
                });
        });

        if (statements.length > 1) {
            message.success(`已执行 ${statements.length} 条语句，生成 ${nextResultSets.length} 个结果集。`);
        } else if (nextResultSets.length === 0) {
            message.success('执行成功。');
        }
        if (anyTruncated && maxRows > 0) {
            message.warning(`结果集已自动限制为最多 ${maxRows} 行（可在工具栏调整）。`);
        }
    } catch (e: any) {
        message.error("Error executing query: " + e.message);
        addSqlLog({
            id: `log-${Date.now()}-error`,
            timestamp: Date.now(),
            sql: getSelectedSQL() || query,
            status: 'error',
            duration: Date.now() - runStartTime,
            message: e.message,
            dbName: currentDb
        });
        setResultSets([]);
        setActiveResultKey('');
    } finally {
        if (runSeqRef.current === runSeq) setLoading(false);
        // Clear query ID after execution completes
        clearQueryId();
    }
  };

  const handleCancel = async () => {
    if (!currentQueryIdRef.current) {
      message.warning('没有正在运行的查询可取消');
      return;
    }
    const queryIdToCancel = currentQueryIdRef.current;
    try {
      const res = await CancelQuery(queryIdToCancel);
      if (res.success) {
        message.success('查询已取消');
        // Clear query ID after successful cancellation
        if (currentQueryIdRef.current === queryIdToCancel) {
          clearQueryId()
        }
      } else {
        message.warning(res.message);
      }
    } catch (error: any) {
      message.error('取消查询失败: ' + error.message);
    }
  };

  useEffect(() => {
      const handleSelectAllInEditor = (event: KeyboardEvent) => {
          if (activeTabId !== tab.id) {
              return;
          }
          if (!(event.ctrlKey || event.metaKey) || event.altKey || event.shiftKey || event.key.toLowerCase() !== 'a') {
              return;
          }

          const editor = editorRef.current;
          if (!editor) {
              return;
          }

          const targetNode = event.target instanceof Node ? event.target : null;
          const editorHasFocus = !!editor.hasTextFocus?.();
          const inEditorPane = !!(targetNode && editorPaneRef.current?.contains(targetNode));
          const inQueryEditor = !!(targetNode && queryEditorRootRef.current?.contains(targetNode));
          if (!editorHasFocus && !inEditorPane) {
              return;
          }
          if (!editorHasFocus && isEditableElement(event.target) && !inEditorPane) {
              return;
          }
          if (!editorHasFocus && !inQueryEditor) {
              return;
          }

          event.preventDefault();
          event.stopPropagation();
          editor.focus?.();
          editor.trigger('keyboard', 'editor.action.selectAll', null);
      };

      window.addEventListener('keydown', handleSelectAllInEditor, true);
      return () => {
          window.removeEventListener('keydown', handleSelectAllInEditor, true);
      };
  }, [activeTabId, tab.id]);

  useEffect(() => {
      const binding = shortcutOptions.runQuery;
      if (!binding?.enabled || !binding.combo) {
          return;
      }

      const handleRunShortcut = (event: KeyboardEvent) => {
          if (activeTabId !== tab.id) {
              return;
          }
          if (!isShortcutMatch(event, binding.combo)) {
              return;
          }
          const editorHasFocus = !!editorRef.current?.hasTextFocus?.();
          if (!editorHasFocus && !isEditableElement(event.target)) {
              return;
          }
          event.preventDefault();
          event.stopPropagation();
          void handleRun();
      };

      window.addEventListener('keydown', handleRunShortcut);
      return () => {
          window.removeEventListener('keydown', handleRunShortcut);
      };
  }, [activeTabId, tab.id, shortcutOptions.runQuery, handleRun]);

  useEffect(() => {
      const handleRunActiveQuery = () => {
          if (activeTabId !== tab.id) {
              return;
          }
          void handleRun();
      };

      window.addEventListener('gonavi:run-active-query', handleRunActiveQuery as EventListener);
      return () => {
          window.removeEventListener('gonavi:run-active-query', handleRunActiveQuery as EventListener);
      };
  }, [activeTabId, tab.id, handleRun]);

  const resolveDefaultQueryName = () => {
      const rawTitle = String(tab.title || '').trim();
      if (!rawTitle || rawTitle.startsWith('新建查询')) {
          return '未命名查询';
      }
      return rawTitle;
  };

  const persistQuery = (payload: { id: string; name: string; createdAt?: number }) => {
      const sql = getCurrentQuery();
      const saved = {
          id: payload.id,
          name: payload.name,
          sql,
          connectionId: currentConnectionId,
          dbName: currentDb || tab.dbName || '',
          createdAt: payload.createdAt ?? Date.now(),
      };
      saveQuery(saved);
      addTab({
          ...tab,
          title: payload.name,
          query: sql,
          connectionId: currentConnectionId,
          dbName: currentDb || tab.dbName || '',
          savedQueryId: payload.id,
      });
      return saved;
  };

  const handleQuickSave = () => {
      const existed = currentSavedQuery || null;
      const fallbackSavedId = String(tab.savedQueryId || '').trim();
      const saveId = existed?.id || fallbackSavedId || '';
      if (!saveId) {
          saveForm.setFieldsValue({ name: resolveDefaultQueryName() });
          setIsSaveModalOpen(true);
          return;
      }
      const saveName = existed?.name || resolveDefaultQueryName();
      persistQuery({ id: saveId, name: saveName, createdAt: existed?.createdAt });
      message.success('查询已保存！');
  };

  const handleSave = async () => {
      try {
          const values = await saveForm.validateFields();
          const existed = currentSavedQuery || null;
          const fallbackSavedId = String(tab.savedQueryId || '').trim();
          const nextSavedId = existed?.id || fallbackSavedId || `saved-${Date.now()}`;
          persistQuery({
              id: nextSavedId,
              name: String(values.name || '').trim() || '未命名查询',
              createdAt: existed?.createdAt,
          });
          message.success('查询已保存！');
          setIsSaveModalOpen(false);
      } catch (e) {
      }
  };

  const handleCloseResult = (key: string) => {
      setResultSets(prev => {
          const idx = prev.findIndex(r => r.key === key);
          if (idx < 0) return prev;
          const next = prev.filter(r => r.key !== key);

          setActiveResultKey(prevActive => {
              if (prevActive && prevActive !== key) return prevActive;
              const nextKey = next[idx]?.key || next[idx - 1]?.key || next[0]?.key || '';
              return nextKey;
          });

          return next;
      });
  };

  return (
    <div ref={queryEditorRootRef} style={{ flex: '1 1 auto', minHeight: 0, display: 'flex', flexDirection: 'column', height: '100%', overflow: 'hidden' }}>
      <style>{`
        .query-result-tabs {
          flex: 1 1 auto;
          min-height: 0;
          display: flex;
          flex-direction: column;
          overflow: hidden;
        }
        .query-result-tabs .ant-tabs-nav {
          flex: 0 0 auto;
        }
        .query-result-tabs .ant-tabs-content-holder {
          flex: 1 1 auto;
          overflow: hidden;
          min-height: 0;
          display: flex;
          flex-direction: column;
        }
        .query-result-tabs .ant-tabs-content {
          flex: 1 1 auto;
          min-height: 0;
          display: flex;
          flex-direction: column;
        }
        .query-result-tabs .ant-tabs-tabpane {
          flex: 1 1 auto;
          min-height: 0;
          display: flex;
          flex-direction: column;
          overflow: hidden;
        }
        .query-result-tabs .ant-tabs-tabpane > div {
          flex: 1 1 auto;
          min-height: 0;
        }
        .query-result-tabs .ant-tabs-tabpane-hidden {
          display: none !important;
        }
        .query-result-tabs .ant-tabs-ink-bar {
          transition: none !important;
        }
      `}</style>
      <div ref={editorPaneRef}>
      <div style={{ padding: '8px', display: 'flex', gap: '8px', flexShrink: 0, alignItems: 'center' }}>
        <Select 
            style={{ width: 150 }} 
            placeholder="选择连接"
            value={currentConnectionId}
            onChange={(val) => {
                setCurrentConnectionId(val);
                setCurrentDb('');
            }}
            options={queryCapableConnections.map(c => ({ label: c.name, value: c.id }))}
            showSearch
        />
        <Select 
            style={{ width: 200 }} 
            placeholder="选择数据库"
            value={currentDb}
            onChange={setCurrentDb}
            options={dbList.map(db => ({ label: db, value: db }))}
            showSearch
        />
        <Tooltip title="最大返回行数（会对 SELECT 自动加 LIMIT，防止大结果集卡死）">
            <Select
                style={{ width: 170 }}
                value={queryOptions?.maxRows ?? 5000}
                onChange={(val) => setQueryOptions({ maxRows: Number(val) })}
                options={[
                    { label: '最大行数：500', value: 500 },
                    { label: '最大行数：1000', value: 1000 },
                    { label: '最大行数：5000', value: 5000 },
                    { label: '最大行数：20000', value: 20000 },
                    { label: '最大行数：不限', value: 0 },
                ]}
            />
        </Tooltip>
        <Button.Group>
          <Tooltip
              title={
                  shortcutOptions.runQuery?.enabled && shortcutOptions.runQuery?.combo
                      ? `运行（${getShortcutDisplay(shortcutOptions.runQuery.combo)}）`
                      : '运行'
              }
          >
              <Button type="primary" icon={<PlayCircleOutlined />} onClick={handleRun} loading={loading}>
                运行
              </Button>
          </Tooltip>
          {loading && (
            <Button type="primary" danger icon={<StopOutlined />} onClick={handleCancel}>
              停止
            </Button>
          )}
        </Button.Group>
        <Button icon={<SaveOutlined />} onClick={handleQuickSave}>
          保存
        </Button>
        
        <Button.Group>
            <Tooltip title="美化 SQL">
                <Button icon={<FormatPainterOutlined />} onClick={handleFormat}>美化</Button>
            </Tooltip>
            <Dropdown menu={{ items: formatSettingsMenu }} placement="bottomRight">
                <Button icon={<SettingOutlined />} />
            </Dropdown>
        </Button.Group>
      </div>
      
      <div style={{ height: editorHeight, minHeight: '100px' }}>
        <Editor 
          height="100%" 
          defaultLanguage="sql" 
          theme={darkMode ? "transparent-dark" : "transparent-light"}
          defaultValue={query}
          onChange={(val) => setQuery(val || '')}
          onMount={handleEditorDidMount}
          options={{ 
            minimap: { enabled: false }, 
            automaticLayout: true,
            scrollBeyondLastLine: false,
            fontSize: 14
          }}
        />
      </div>

      <div 
        onMouseDown={handleMouseDown}
        style={{ 
            height: '5px', 
            cursor: 'row-resize', 
            background: darkMode ? 'rgba(255,255,255,0.08)' : 'rgba(0,0,0,0.04)',
            flexShrink: 0,
            zIndex: 10 
        }} 
        title="拖动调整高度"
      />
      </div>

      <div style={{ flex: 1, minHeight: 0, overflow: 'hidden', padding: 0, display: 'flex', flexDirection: 'column' }}>
        {resultSets.length > 0 ? (
          <Tabs
              className="query-result-tabs"
              activeKey={activeResultKey || resultSets[0]?.key}
              onChange={setActiveResultKey}
              animated={false}
              style={{ flex: 1, minHeight: 0 }}
              items={resultSets.map((rs, idx) => ({
                  key: rs.key,
                  label: (
                      <div style={{ display: 'inline-flex', alignItems: 'center', gap: 6 }}>
                          <Tooltip title={rs.sql}>
                              <span>{`结果 ${idx + 1}${Array.isArray(rs.rows) ? ` (${rs.rows.length}${rs.truncated ? '+' : ''})` : ''}`}</span>
                          </Tooltip>
                          <Tooltip title="关闭结果">
                              <span
                                  onClick={(e) => {
                                      e.preventDefault();
                                      e.stopPropagation();
                                      handleCloseResult(rs.key);
                                  }}
                                  style={{ display: 'inline-flex', alignItems: 'center', color: '#999', cursor: 'pointer' }}
                              >
                                  <CloseOutlined style={{ fontSize: 12 }} />
                              </span>
                          </Tooltip>
                      </div>
                  ),
                  children: (
                      <div style={{ flex: 1, minHeight: 0, overflow: 'hidden', display: 'flex', flexDirection: 'column' }}>
                          <DataGrid
                              data={rs.rows}
                              columnNames={rs.columns}
                              loading={loading}
                              tableName={rs.tableName}
                              exportScope="queryResult"
                              resultSql={rs.exportSql || rs.sql}
                              dbName={currentDb}
                              connectionId={currentConnectionId}
                              pkColumns={rs.pkColumns}
                              onReload={handleRun}
                              readOnly={rs.readOnly}
                          />
                      </div>
                  )
              }))}
          />
        ) : (
          <div style={{ flex: 1, minHeight: 0 }} />
        )}
      </div>

      <Modal 
        title="保存查询" 
        open={isSaveModalOpen} 
        onOk={handleSave} 
        onCancel={() => setIsSaveModalOpen(false)}
        okText="确认"
        cancelText="取消"
      >
          <Form form={saveForm} layout="vertical">
              <Form.Item name="name" label="查询名称" rules={[{ required: true, message: '请输入查询名称' }]}>
                  <Input placeholder="例如：查询所有用户" />
              </Form.Item>
          </Form>
      </Modal>
    </div>
  );
};

export default QueryEditor;
