import React, { useState, useEffect, useMemo, useRef } from 'react';
import { Modal, Form, Select, Input, Button, message, Steps, Transfer, Card, Alert, Divider, Typography, Progress, Checkbox, Table, Drawer, Tabs, theme as antdTheme } from 'antd';
import { DatabaseOutlined, RocketOutlined, SwapOutlined, TableOutlined } from '@ant-design/icons';
import { useStore } from '../store';
import { DBGetDatabases, DBGetTables, DataSync, DataSyncAnalyze, DataSyncPreview } from '../../wailsjs/go/app/App';
import { SavedConnection } from '../types';
import { EventsOn } from '../../wailsjs/runtime/runtime';
import { isMacLikePlatform, normalizeOpacityForPlatform, resolveAppearanceValues, resolveTextInputSafeBackdropFilter } from '../utils/appearance';
import { buildRpcConnectionConfig } from '../utils/connectionRpcConfig';
import { quoteIdentPart, quoteQualifiedIdent } from '../utils/sql';
import { formatLocalDateTimeLiteral, normalizeTemporalLiteralText } from './dataGridCopyInsert';
import { buildDataSyncRequest, type SourceDatasetMode, validateDataSyncSelection } from './dataSyncRequest';
import { t } from '../i18n';
import { useOptionalI18n } from '../i18n/provider';
import { resolveDataSyncEntryModePresentation, type DataSyncEntryMode } from './dataSyncEntryMode';
const { Title, Text } = Typography;
const { Step } = Steps;
const { Option } = Select;
const { TextArea } = Input;

type SyncLogEvent = { jobId: string; level?: string; message?: string; ts?: number };
type SyncProgressEvent = { jobId: string; percent?: number; current?: number; total?: number; table?: string; stage?: string };
type SyncLogItem = { level: string; message: string; ts?: number };
type TableDiffSummary = {
  table: string;
  pkColumn?: string;
  canSync?: boolean;
  inserts?: number;
  updates?: number;
  deletes?: number;
  same?: number;
  schemaDiffCount?: number;
  message?: string;
  targetTableExists?: boolean;
  plannedAction?: string;
  warnings?: string[];
  unsupportedObjects?: string[];
  indexesToCreate?: number;
  indexesSkipped?: number;
};
type TableOps = {
  insert: boolean;
  update: boolean;
  delete: boolean;
  selectedInsertPks?: string[];
  selectedUpdatePks?: string[];
  selectedDeletePks?: string[];
};

type WorkflowType = 'sync' | 'migration';

const quoteSqlIdent = (dbType: string, ident: string): string => {
  return quoteIdentPart(dbType, String(ident || '').trim());
};

const quoteSqlTable = (dbType: string, tableName: string): string => {
  return quoteQualifiedIdent(dbType, String(tableName || '').trim());
};

const toSqlLiteral = (value: any, dbType: string): string => {
  if (value === null || value === undefined) return 'NULL';
  if (typeof value === 'number') return Number.isFinite(value) ? String(value) : 'NULL';
  if (typeof value === 'bigint') return value.toString();
  if (typeof value === 'boolean') {
    const t = String(dbType || '').toLowerCase();
    if (t === 'sqlserver') return value ? '1' : '0';
    return value ? 'TRUE' : 'FALSE';
  }
  if (value instanceof Date) {
    return `'${formatLocalDateTimeLiteral(value).replace(/'/g, "''")}'`;
  }
  if (typeof value === 'string') {
    return `'${value.replace(/'/g, "''")}'`;
  }
  if (typeof value === 'object') {
    try {
      return `'${JSON.stringify(value).replace(/'/g, "''")}'`;
    } catch {
      return `'${String(value).replace(/'/g, "''")}'`;
    }
  }
  return `'${String(value).replace(/'/g, "''")}'`;
};

const toTypedSqlLiteral = (value: any, dbType: string, columnType?: string): string => {
  if (typeof value === 'string') {
    const normalized = normalizeTemporalLiteralText(value, columnType, false);
    return toSqlLiteral(normalized, dbType);
  }
  if (value instanceof Date) {
    const normalized = String(columnType || '').trim()
      ? formatLocalDateTimeLiteral(value)
      : value.toISOString();
    return toSqlLiteral(normalized, dbType);
  }
  return toSqlLiteral(value, dbType);
};

const resolveRedisDbIndex = (raw?: string): number => {
  const value = Number(String(raw || '').trim());
  return Number.isInteger(value) && value >= 0 && value <= 15 ? value : 0;
};

const isServiceNameBackedSyncConnection = (conn?: SavedConnection): boolean => {
  const type = String(conn?.config?.type || '').trim().toLowerCase();
  if (type === 'oracle') return true;
  if (type !== 'oceanbase') return false;
  const explicitProtocol = String((conn?.config as any)?.oceanBaseProtocol || '').trim().toLowerCase();
  if (explicitProtocol === 'oracle') return true;
  const params = new URLSearchParams(String(conn?.config?.connectionParams || ''));
  const protocol = String(params.get('protocol') || params.get('tenantMode') || '').trim().toLowerCase();
  return protocol === 'oracle';
};

const buildSqlPreview = (
  previewData: any,
  tableName: string,
  dbType: string,
  ops?: TableOps,
): { sqlText: string; statementCount: number } => {
  if (!previewData || !tableName) return { sqlText: '', statementCount: 0 };
  const tableExpr = quoteSqlTable(dbType, tableName);
  const pkCol = String(previewData.pkColumn || 'id');
  const columnTypesByLowerName = previewData?.columnTypes && typeof previewData.columnTypes === 'object'
    ? previewData.columnTypes as Record<string, string>
    : {};
  const statements: string[] = [];
  const schemaStatements = Array.isArray(previewData.schemaStatements)
    ? previewData.schemaStatements
        .map((item: any) => String(item || '').trim())
        .filter((item: string) => item.length > 0)
    : [];

  schemaStatements.forEach((statement: string) => {
    statements.push(statement.endsWith(';') ? statement : `${statement};`);
  });

  const insertRows = Array.isArray(previewData.inserts) ? previewData.inserts : [];
  const updateRows = Array.isArray(previewData.updates) ? previewData.updates : [];
  const deleteRows = Array.isArray(previewData.deletes) ? previewData.deletes : [];

  const selectedInsert = new Set((ops?.selectedInsertPks || []).map((v) => String(v)));
  const selectedUpdate = new Set((ops?.selectedUpdatePks || []).map((v) => String(v)));
  const selectedDelete = new Set((ops?.selectedDeletePks || []).map((v) => String(v)));

  if (ops?.insert !== false) {
    insertRows.forEach((rowWrap: any) => {
      const pk = String(rowWrap?.pk ?? '');
      if (selectedInsert.size > 0 && !selectedInsert.has(pk)) return;
      const row = rowWrap?.row || {};
      const columns = Object.keys(row);
      if (columns.length === 0) return;
      const colExpr = columns.map((c) => quoteSqlIdent(dbType, c)).join(', ');
      const valExpr = columns.map((c) => toTypedSqlLiteral(row[c], dbType, columnTypesByLowerName[String(c).toLowerCase()])).join(', ');
      statements.push(`INSERT INTO ${tableExpr} (${colExpr}) VALUES (${valExpr});`);
    });
  }

  if (ops?.update !== false) {
    updateRows.forEach((rowWrap: any) => {
      const pk = String(rowWrap?.pk ?? '');
      if (selectedUpdate.size > 0 && !selectedUpdate.has(pk)) return;
      const source = rowWrap?.source || {};
      const changedColumns = Array.isArray(rowWrap?.changedColumns)
        ? rowWrap.changedColumns
        : Object.keys(source).filter((k) => k !== pkCol);
      const setCols = changedColumns.filter((c: string) => String(c) !== pkCol);
      if (setCols.length === 0) return;
      const setExpr = setCols
        .map((c: string) => `${quoteSqlIdent(dbType, c)} = ${toTypedSqlLiteral(source[c], dbType, columnTypesByLowerName[String(c).toLowerCase()])}`)
        .join(', ');
      statements.push(
        `UPDATE ${tableExpr} SET ${setExpr} WHERE ${quoteSqlIdent(dbType, pkCol)} = ${toTypedSqlLiteral(pk, dbType, columnTypesByLowerName[String(pkCol).toLowerCase()])};`,
      );
    });
  }

  if (ops?.delete) {
    deleteRows.forEach((rowWrap: any) => {
      const pk = String(rowWrap?.pk ?? '');
      if (selectedDelete.size > 0 && !selectedDelete.has(pk)) return;
      statements.push(
        `DELETE FROM ${tableExpr} WHERE ${quoteSqlIdent(dbType, pkCol)} = ${toTypedSqlLiteral(pk, dbType, columnTypesByLowerName[String(pkCol).toLowerCase()])};`,
      );
    });
  }

  return {
    sqlText: statements.join('\n'),
    statementCount: statements.length,
  };
};

const DataSyncModal: React.FC<{ open: boolean; onClose: () => void; entryMode?: DataSyncEntryMode }> = ({ open, onClose, entryMode = 'sync' }) => {
  const i18n = useOptionalI18n();
  const i18nLanguage = i18n?.language;
  const tr = (key: string, params?: Parameters<typeof t>[1]) => t(key, params, i18nLanguage);
  const entryPresentation = resolveDataSyncEntryModePresentation(entryMode, tr);
  const isSchemaCompareEntry = entryMode === 'schemaCompare';
  const isDataCompareEntry = entryMode === 'dataCompare';
  const isCompareEntry = entryPresentation.readOnly;
  const connections = useStore((state) => state.connections);
  const themeMode = useStore((state) => state.theme);
  const appearance = useStore((state) => state.appearance);
  const [currentStep, setCurrentStep] = useState(0);
  const [loading, setLoading] = useState(false);
  const { token } = antdTheme.useToken();
  const darkMode = themeMode === 'dark';
  const resolvedAppearance = resolveAppearanceValues(appearance);
  const effectiveOpacity = normalizeOpacityForPlatform(resolvedAppearance.opacity);
  const disableLocalBackdropFilter = isMacLikePlatform();
  
  // Step 1: Config
  const [sourceConnId, setSourceConnId] = useState<string>('');
  const [targetConnId, setTargetConnId] = useState<string>('');
  const [sourceDb, setSourceDb] = useState<string>('');
  const [targetDb, setTargetDb] = useState<string>('');
  
  const [sourceDbs, setSourceDbs] = useState<string[]>([]);
  const [targetDbs, setTargetDbs] = useState<string[]>([]);

  // Step 2: Tables
  const [allTables, setAllTables] = useState<string[]>([]);
  const [selectedTables, setSelectedTables] = useState<string[]>([]);
  const [sourceDatasetMode, setSourceDatasetMode] = useState<SourceDatasetMode>('table');
  const [sourceQuery, setSourceQuery] = useState<string>('');

  // Options
  const [workflowType, setWorkflowType] = useState<WorkflowType>('sync');
  const [syncContent, setSyncContent] = useState<'data' | 'schema' | 'both'>(isSchemaCompareEntry ? 'schema' : 'data');
  const [syncMode, setSyncMode] = useState<string>('insert_update');
  const [autoAddColumns, setAutoAddColumns] = useState<boolean>(true);
  const [targetTableStrategy, setTargetTableStrategy] = useState<'existing_only' | 'auto_create_if_missing' | 'smart'>('existing_only');
  const [createIndexes, setCreateIndexes] = useState<boolean>(false);
  const [mongoCollectionName, setMongoCollectionName] = useState<string>('');
  const [showSameTables, setShowSameTables] = useState<boolean>(false);
  const [analyzing, setAnalyzing] = useState<boolean>(false);
  const [diffTables, setDiffTables] = useState<TableDiffSummary[]>([]);
  const [tableOptions, setTableOptions] = useState<Record<string, TableOps>>({});

  const [previewOpen, setPreviewOpen] = useState(false);
  const [previewTable, setPreviewTable] = useState<string>('');
  const [previewLoading, setPreviewLoading] = useState(false);
  const [previewData, setPreviewData] = useState<any>(null);

  // Step 3: Result
  const [syncResult, setSyncResult] = useState<any>(null);
  const [syncing, setSyncing] = useState(false);
  const [syncLogs, setSyncLogs] = useState<SyncLogItem[]>([]);
  const [syncProgress, setSyncProgress] = useState<{ percent: number; current: number; total: number; table: string; stage: string }>({
      percent: 0,
      current: 0,
      total: 0,
      table: '',
      stage: ''
  });
  const jobIdRef = useRef<string>('');
  const logBoxRef = useRef<HTMLDivElement>(null);
  const autoScrollRef = useRef(true);

  const normalizeConnConfig = (conn: SavedConnection, database?: string) => (
      buildRpcConnectionConfig(conn.config, {
          database: typeof database === 'string' && !isServiceNameBackedSyncConnection(conn) ? database : (conn.config.database || ''),
      })
  );

  useEffect(() => {
      if (!open) return;

      const offLog = EventsOn('sync:log', (event: SyncLogEvent) => {
          if (!event || event.jobId !== jobIdRef.current) return;
          const msg = String(event.message || '').trim();
          if (!msg) return;
          setSyncLogs(prev => [...prev, { level: String(event.level || 'info'), message: msg, ts: event.ts }]);
      });

      const offProgress = EventsOn('sync:progress', (event: SyncProgressEvent) => {
          if (!event || event.jobId !== jobIdRef.current) return;
          setSyncProgress(prev => ({
              percent: typeof event.percent === 'number' ? event.percent : prev.percent,
              current: typeof event.current === 'number' ? event.current : prev.current,
              total: typeof event.total === 'number' ? event.total : prev.total,
              table: typeof event.table === 'string' ? event.table : prev.table,
              stage: typeof event.stage === 'string' ? event.stage : prev.stage,
          }));
      });

      return () => {
          if (typeof offLog === 'function') offLog();
          if (typeof offProgress === 'function') offProgress();
      };
  }, [open]);

  useEffect(() => {
      if (!logBoxRef.current) return;
      if (!autoScrollRef.current) return;
      logBoxRef.current.scrollTop = logBoxRef.current.scrollHeight;
  }, [syncLogs]);

  useEffect(() => {
    if (open) {
        setCurrentStep(0);
        setSourceConnId('');
        setTargetConnId('');
        setSourceDb('');
        setTargetDb('');
        setAllTables([]);
        setSelectedTables([]);
        setSourceDatasetMode('table');
        setSourceQuery('');
        setWorkflowType('sync');
        setSyncContent(isSchemaCompareEntry ? 'schema' : 'data');
        setSyncMode('insert_update');
        setAutoAddColumns(true);
        setTargetTableStrategy('existing_only');
        setCreateIndexes(false);
        setShowSameTables(false);
        setAnalyzing(false);
        setDiffTables([]);
        setTableOptions({});
        setPreviewOpen(false);
        setPreviewTable('');
        setPreviewLoading(false);
        setPreviewData(null);
        setSyncResult(null);
        setSyncing(false);
        setSyncLogs([]);
        setSyncProgress({ percent: 0, current: 0, total: 0, table: '', stage: '' });
        jobIdRef.current = '';
        autoScrollRef.current = true;
    }
  }, [open, isSchemaCompareEntry]);

  useEffect(() => {
      if (isSchemaCompareEntry) {
          if (workflowType !== 'sync') {
              setWorkflowType('sync');
          }
          if (sourceDatasetMode !== 'table') {
              setSourceDatasetMode('table');
          }
          if (syncContent !== 'schema') {
              setSyncContent('schema');
          }
          if (syncMode !== 'insert_update') {
              setSyncMode('insert_update');
          }
          if (targetTableStrategy !== 'existing_only') {
              setTargetTableStrategy('existing_only');
          }
          if (createIndexes) {
              setCreateIndexes(false);
          }
          return;
      }
      if (isDataCompareEntry) {
          if (workflowType !== 'sync') {
              setWorkflowType('sync');
          }
          if (syncContent !== 'data') {
              setSyncContent('data');
          }
          if (syncMode !== 'insert_update') {
              setSyncMode('insert_update');
          }
          if (targetTableStrategy !== 'existing_only') {
              setTargetTableStrategy('existing_only');
          }
          if (createIndexes) {
              setCreateIndexes(false);
          }
          return;
      }
      if (workflowType === 'migration') {
          if (syncMode === 'insert_update') {
              setSyncMode('insert_only');
          }
          if (syncContent === 'schema') {
              setSyncContent('both');
          }
          if (targetTableStrategy === 'existing_only') {
              setTargetTableStrategy('smart');
          }
          if (!createIndexes) {
              setCreateIndexes(true);
          }
      } else {
          if (targetTableStrategy !== 'existing_only') {
              setTargetTableStrategy('existing_only');
          }
          if (createIndexes) {
              setCreateIndexes(false);
          }
      }
  }, [isSchemaCompareEntry, isDataCompareEntry, workflowType, sourceDatasetMode, syncContent, syncMode, targetTableStrategy, createIndexes]);

  useEffect(() => {
      if (sourceDatasetMode !== 'query') return;
      if (workflowType !== 'sync') {
          setWorkflowType('sync');
      }
      if (syncContent !== 'data') {
          setSyncContent('data');
      }
      if (targetTableStrategy !== 'existing_only') {
          setTargetTableStrategy('existing_only');
      }
      if (createIndexes) {
          setCreateIndexes(false);
      }
      if (autoAddColumns) {
          setAutoAddColumns(false);
      }
      if (selectedTables.length > 1) {
          setSelectedTables(selectedTables.slice(0, 1));
      }
  }, [sourceDatasetMode, workflowType, syncContent, targetTableStrategy, createIndexes, autoAddColumns, selectedTables]);

  const handleSourceConnChange = async (connId: string) => {
      setSourceConnId(connId);
      setSourceDb('');
      const conn = connections.find(c => c.id === connId);
      if (conn) {
          setLoading(true);
          try {
            const res = await DBGetDatabases(normalizeConnConfig(conn) as any);
            if (res.success) {
                const dbRows = Array.isArray(res.data) ? res.data : [];
                setSourceDbs(dbRows
                    .map((r: any) => r?.Database || r?.database || r?.username)
                    .filter((name: any) => typeof name === 'string' && name.trim() !== ''));
            }
          } catch(e: any) {
          message.error(tr('data_sync.message.fetch_source_databases_failed_detail', { detail: e?.message || String(e) }));
        }
          setLoading(false);
      }
  };

  const handleTargetConnChange = async (connId: string) => {
      setTargetConnId(connId);
      setTargetDb('');
      const conn = connections.find(c => c.id === connId);
      if (conn) {
          setLoading(true);
          try {
            const res = await DBGetDatabases(normalizeConnConfig(conn) as any);
            if (res.success) {
                const dbRows = Array.isArray(res.data) ? res.data : [];
                setTargetDbs(dbRows
                    .map((r: any) => r?.Database || r?.database || r?.username)
                    .filter((name: any) => typeof name === 'string' && name.trim() !== ''));
            }
          } catch(e: any) {
          message.error(tr('data_sync.message.fetch_target_databases_failed_detail', { detail: e?.message || String(e) }));
        }
          setLoading(false);
      }
  };

  const nextToTables = async () => {
      if (!sourceConnId || !targetConnId) return message.error("Select connections first");
      if (!sourceDb) return message.error("Select source database");
      if (!targetDb) return message.error("Select target database");

      setLoading(true);
      try {
          const connId = isSourceQueryMode ? targetConnId : sourceConnId;
          const dbName = isSourceQueryMode ? targetDb : sourceDb;
          const conn = connections.find(c => c.id === connId);
          if (conn) {
              const config = normalizeConnConfig(conn, dbName);
              const res = await DBGetTables(config as any, dbName);
              if (res.success) {
                  // DBGetTables returns [{Table: "name"}, ...]
                  const tableRows = Array.isArray(res.data) ? res.data : [];
                  const tables = tableRows
                      .map((row: any) => row?.Table || row?.table || row?.TABLE_NAME || Object.values(row || {})[0])
                      .filter((name: any) => typeof name === 'string' && name.trim() !== '');
                  setAllTables(tables as string[]);
                  setSelectedTables(prev => {
                      const existing = prev.filter((name) => tables.includes(name));
                      if (isSourceQueryMode) {
                          return existing.slice(0, 1);
                      }
                      return existing;
                  });
                  setCurrentStep(1);
              } else {
                  message.error(res.message ? tr('data_sync.message.fetch_tables_failed_detail', { detail: res.message }) : tr('data_sync.message.fetch_tables_failed'));
              }
          }
      } catch (e: any) {
          message.error(tr('data_sync.message.fetch_tables_failed_detail', { detail: e?.message || String(e) }));
      }
      setLoading(false);
  };

  const updateTableOption = (table: string, key: keyof TableOps, value: any) => {
      setTableOptions(prev => ({
          ...prev,
          [table]: { ...(prev[table] || { insert: true, update: true, delete: false }), [key]: value }
      }));
  };

  const analyzeDiff = async () => {
      const selectionError = validateDataSyncSelection({ sourceDatasetMode, selectedTables, sourceQuery, syncContent });
      if (selectionError) return message.error(tr(selectionError));
      if (!sourceConnId || !targetConnId) return message.error(tr('data_sync.message.select_connections_first'));
      if (!sourceDb || !targetDb) return message.error(tr('data_sync.message.select_databases_first'));

      setLoading(true);
      setAnalyzing(true);
      setDiffTables([]);
      setTableOptions({});
      setSyncLogs([]);

      const sConn = connections.find(c => c.id === sourceConnId)!;
      const tConn = connections.find(c => c.id === targetConnId)!;
      const jobId = `analyze-${Date.now()}-${Math.random().toString(16).slice(2, 8)}`;
      jobIdRef.current = jobId;
      autoScrollRef.current = true;
      setSyncProgress({ percent: 0, current: 0, total: selectedTables.length, table: '', stage: tr('data_sync.progress.stage.analyzing_diff') });

      const config = buildDataSyncRequest({
          sourceConfig: normalizeConnConfig(sConn, sourceDb),
          targetConfig: normalizeConnConfig(tConn, targetDb),
          sourceDatabase: sourceDb,
          targetDatabase: targetDb,
          selectedTables,
          sourceDatasetMode,
          sourceQuery,
          syncContent,
          syncMode: "insert_update",
          autoAddColumns,
          targetTableStrategy,
          createIndexes,
          mongoCollectionName,
          jobId,
      });

      try {
          const res = await DataSyncAnalyze(config as any);
          if (res.success) {
              const tables = ((res.data as any)?.tables || []) as TableDiffSummary[];
              setDiffTables(tables);
              const init: Record<string, TableOps> = {};
              tables.forEach(t => {
                  const can = !!t.canSync;
                  init[t.table] = {
                      insert: can,
                      update: can,
                      delete: false,
                      selectedInsertPks: [],
                      selectedUpdatePks: [],
                      selectedDeletePks: [],
                  };
              });
              setTableOptions(init);
              message.success(tr('data_sync.message.analysis_complete'));
          } else {
              message.error(res.message ? tr('data_sync.message.analysis_failed_detail', { detail: res.message }) : tr('data_sync.message.analysis_failed'));
          }
      } catch (e: any) {
          message.error(tr('data_sync.message.analysis_failed_detail', { detail: e?.message || String(e) }));
      }

      setLoading(false);
      setAnalyzing(false);
  };

  const openPreview = async (table: string) => {
      if (!table) return;
      const sConn = connections.find(c => c.id === sourceConnId)!;
      const tConn = connections.find(c => c.id === targetConnId)!;

      setPreviewOpen(true);
      setPreviewTable(table);
      setPreviewLoading(true);
      setPreviewData(null);

      const config = buildDataSyncRequest({
          sourceConfig: normalizeConnConfig(sConn, sourceDb),
          targetConfig: normalizeConnConfig(tConn, targetDb),
          sourceDatabase: sourceDb,
          targetDatabase: targetDb,
          selectedTables,
          sourceDatasetMode,
          sourceQuery,
          syncContent,
          syncMode: "insert_update",
          autoAddColumns,
          targetTableStrategy,
          createIndexes,
          mongoCollectionName,
      });

      try {
          const res = await DataSyncPreview(config as any, table, 200);
          if (res.success) {
              setPreviewData(res.data);
          } else {
              message.error(res.message ? tr('data_sync.message.preview_load_failed_detail', { detail: res.message }) : tr('data_sync.message.preview_load_failed'));
          }
      } catch (e: any) {
          message.error(tr('data_sync.message.preview_load_failed_detail', { detail: e?.message || String(e) }));
      }

      setPreviewLoading(false);
  };

  const runSync = async () => {
      const selectionError = validateDataSyncSelection({ sourceDatasetMode, selectedTables, sourceQuery, syncContent });
      if (selectionError) {
          message.error(tr(selectionError));
          return;
      }
      if (syncContent !== 'schema' && diffTables.length === 0) {
          message.error(tr('data_sync.message.analyze_before_sync'));
          return;
      }
      if (syncContent !== 'schema' && syncMode === 'full_overwrite') {
          const ok = await new Promise<boolean>((resolve) => {
              Modal.confirm({
                  title: tr('data_sync.modal.full_overwrite_title'),
                  content: tr('data_sync.modal.full_overwrite_content'),
                  okText: tr('data_sync.modal.full_overwrite_ok'),
                  cancelText: tr('common.cancel'),
                  onOk: () => resolve(true),
                  onCancel: () => resolve(false),
              });
          });
          if (!ok) return;
      }

      setLoading(true);
      setSyncing(true);
      setCurrentStep(2);
      setSyncResult(null);
      setSyncLogs([]);

      const sConn = connections.find(c => c.id === sourceConnId)!;
      const tConn = connections.find(c => c.id === targetConnId)!;

      const jobId = `sync-${Date.now()}-${Math.random().toString(16).slice(2, 8)}`;
      jobIdRef.current = jobId;
      autoScrollRef.current = true;
      setSyncProgress({
          percent: 0,
          current: 0,
          total: selectedTables.length,
          table: '',
          stage: tr('data_sync.progress.stage.preparing'),
      });
      
      const config = buildDataSyncRequest({
          sourceConfig: normalizeConnConfig(sConn, sourceDb),
          targetConfig: normalizeConnConfig(tConn, targetDb),
          sourceDatabase: sourceDb,
          targetDatabase: targetDb,
          selectedTables,
          sourceDatasetMode,
          sourceQuery,
          syncContent,
          syncMode,
          autoAddColumns,
          targetTableStrategy,
          createIndexes,
          mongoCollectionName,
          tableOptions,
          jobId,
      });

      try {
          const res = await DataSync(config as any);
          setSyncResult(res);
          if (Array.isArray(res?.logs) && res.logs.length > 0) {
              setSyncLogs(prev => {
                  if (prev.length > 0) return prev;
                  return (res.logs as string[]).map((log) => {
                      const msg = String(log || '').trim();
                      if (msg.includes('致命错误') || msg.includes('失败')) return { level: 'error', message: msg }; // i18n-scan: allow-raw backend log severity markers
                      if (msg.includes('跳过') || msg.includes('警告')) return { level: 'warn', message: msg }; // i18n-scan: allow-raw backend log severity markers
                      return { level: 'info', message: msg };
                  });
              });
          }
      } catch (e: any) {
          message.error(tr('data_sync.message.sync_execution_failed_detail', { detail: e?.message || String(e) }));
          setSyncResult({ success: false, message: tr('data_sync.message.sync_execution_failed'), logs: [] });
      }
      setLoading(false);
      setSyncing(false);
  };

  const renderSyncLogItem = (item: SyncLogItem) => {
      const level = String(item.level || 'info').toLowerCase();
      const color = level === 'error' ? '#ff4d4f' : (level === 'warn' ? '#faad14' : '#595959');
      const label = level === 'error' ? tr('data_sync.log.level.error') : (level === 'warn' ? tr('data_sync.log.level.warn') : tr('data_sync.log.level.info'));
      const timeText = typeof item.ts === 'number' ? new Date(item.ts).toLocaleTimeString(i18nLanguage || undefined, { hour12: false }) : '';
      return (
          <div style={{ display: 'flex', gap: 8, alignItems: 'flex-start' }}>
              <span style={{ color, flex: '0 0 auto' }}>● {label}</span>
              {timeText && <span style={{ color: '#8c8c8c', flex: '0 0 auto' }}>{timeText}</span>}
              <span style={{ whiteSpace: 'pre-wrap', wordBreak: 'break-word' }}>{item.message}</span>
          </div>
      );
  };

  const previewSql = useMemo(() => {
      if (!previewData || !previewTable) return { sqlText: '', statementCount: 0 };
      const targetType = String(connections.find(c => c.id === targetConnId)?.config?.type || '');
      const ops = tableOptions[previewTable] || { insert: true, update: true, delete: false };
      return buildSqlPreview(previewData, previewTable, targetType, ops);
  }, [previewData, previewTable, targetConnId, connections, tableOptions]);
  const previewHasSchemaStatements = useMemo(
      () => Array.isArray(previewData?.schemaStatements) && previewData.schemaStatements.length > 0,
      [previewData],
  );
  const previewSchemaWarnings = useMemo(
      () => Array.isArray(previewData?.schemaWarnings) ? previewData.schemaWarnings as string[] : [],
      [previewData],
  );
  const previewHasDataDiff = useMemo(
      () => Number(previewData?.totalInserts || 0) + Number(previewData?.totalUpdates || 0) + Number(previewData?.totalDeletes || 0) > 0,
      [previewData],
  );

  const analysisWarnings = useMemo(() => {
      const items: string[] = [];
      diffTables.forEach((table) => {
          (table.warnings || []).forEach((warning) => items.push(`${table.table}: ${warning}`));
          (table.unsupportedObjects || []).forEach((warning) => items.push(`${table.table}: ${warning}`));
      });
      return Array.from(new Set(items));
  }, [diffTables]);

  const isSourceQueryMode = sourceDatasetMode === 'query';
  const isMigrationWorkflow = !isCompareEntry && workflowType === 'migration';
  const sourceConn = useMemo(() => connections.find(c => c.id === sourceConnId), [connections, sourceConnId]);
  const targetConn = useMemo(() => connections.find(c => c.id === targetConnId), [connections, targetConnId]);
  const sourceType = String(sourceConn?.config?.type || '').toLowerCase();
  const targetType = String(targetConn?.config?.type || '').toLowerCase();
  const isRedisMongoKeyspaceMigration = isMigrationWorkflow && (
      (sourceType === 'redis' && targetType === 'mongodb') ||
      (sourceType === 'mongodb' && targetType === 'redis')
  );
  const defaultMongoCollectionName = useMemo(() => {
      if (sourceType === 'redis' && targetType === 'mongodb') {
          return `redis_db_${resolveRedisDbIndex(sourceDb || sourceConn?.config?.database)}_keys`;
      }
      if (sourceType === 'mongodb' && targetType === 'redis') {
          return selectedTables[0] || `redis_db_${resolveRedisDbIndex(targetDb || targetConn?.config?.database)}_keys`;
      }
      return '';
  }, [sourceType, targetType, sourceDb, targetDb, sourceConn, targetConn, selectedTables]);

  const modalPanelStyle = useMemo(() => ({
      background: darkMode
          ? 'linear-gradient(180deg, rgba(16,22,34,0.96) 0%, rgba(10,14,24,0.98) 100%)'
          : 'linear-gradient(180deg, rgba(255,255,255,0.98) 0%, rgba(246,248,252,0.98) 100%)',
      border: darkMode ? '1px solid rgba(255,255,255,0.08)' : '1px solid rgba(16,24,40,0.08)',
      boxShadow: darkMode ? '0 24px 56px rgba(0,0,0,0.36)' : '0 18px 44px rgba(15,23,42,0.14)',
      backdropFilter: resolveTextInputSafeBackdropFilter(darkMode ? 'blur(18px)' : 'none', disableLocalBackdropFilter),
  }), [darkMode, disableLocalBackdropFilter]);

  const shellCardStyle = useMemo<React.CSSProperties>(() => ({
      borderRadius: 18,
      border: darkMode ? '1px solid rgba(255,255,255,0.08)' : '1px solid rgba(15,23,42,0.08)',
      background: darkMode ? 'rgba(255,255,255,0.03)' : `rgba(255,255,255,${Math.max(effectiveOpacity, 0.88)})`,
      boxShadow: darkMode ? '0 12px 32px rgba(0,0,0,0.22)' : '0 10px 24px rgba(15,23,42,0.08)',
      overflow: 'hidden',
  }), [darkMode, effectiveOpacity]);

  const heroPanelStyle = useMemo<React.CSSProperties>(() => ({
      padding: 18,
      borderRadius: 18,
      border: darkMode ? '1px solid rgba(255,214,102,0.12)' : '1px solid rgba(24,144,255,0.12)',
      background: darkMode
          ? 'linear-gradient(135deg, rgba(255,214,102,0.10) 0%, rgba(255,255,255,0.03) 100%)'
          : 'linear-gradient(135deg, rgba(24,144,255,0.10) 0%, rgba(255,255,255,0.95) 100%)',
      marginBottom: 18,
  }), [darkMode]);

  const badgeStyle = useMemo<React.CSSProperties>(() => ({
      display: 'inline-flex',
      alignItems: 'center',
      gap: 6,
      padding: '6px 10px',
      borderRadius: 999,
      border: darkMode ? '1px solid rgba(255,255,255,0.10)' : '1px solid rgba(15,23,42,0.08)',
      background: darkMode ? 'rgba(255,255,255,0.04)' : 'rgba(255,255,255,0.86)',
      color: darkMode ? 'rgba(255,255,255,0.88)' : '#334155',
      fontSize: 12,
      fontWeight: 600,
  }), [darkMode]);

  const quietPanelStyle = useMemo<React.CSSProperties>(() => ({
      padding: 14,
      borderRadius: 16,
      border: darkMode ? '1px solid rgba(255,255,255,0.08)' : '1px solid rgba(15,23,42,0.08)',
      background: darkMode ? 'rgba(255,255,255,0.025)' : 'rgba(248,250,252,0.92)',
  }), [darkMode]);

  const modalWorkspaceStyle = useMemo<React.CSSProperties>(() => ({
      display: 'flex',
      flexDirection: 'column',
      height: '100%',
      minHeight: 0,
  }), []);

  const modalScrollableContentStyle = useMemo<React.CSSProperties>(() => ({
      flex: 1,
      minHeight: 0,
      overflowY: 'auto',
      overflowX: 'hidden',
      paddingRight: 4,
      overscrollBehavior: 'contain',
  }), []);

  const modalFooterBarStyle = useMemo<React.CSSProperties>(() => ({
      marginTop: 18,
      display: 'flex',
      justifyContent: 'flex-end',
      gap: 8,
      paddingTop: 12,
      borderTop: darkMode ? '1px solid rgba(255,255,255,0.06)' : '1px solid rgba(15,23,42,0.06)',
      flex: '0 0 auto',
  }), [darkMode]);

  const renderModalTitle = (title: string, description: string) => (
      <div style={{ display: 'flex', alignItems: 'flex-start', gap: 12 }}>
          <div style={{
              width: 38,
              height: 38,
              borderRadius: 14,
              display: 'grid',
              placeItems: 'center',
              background: darkMode ? 'rgba(255,214,102,0.12)' : 'rgba(24,144,255,0.10)',
              color: darkMode ? '#ffd666' : token.colorPrimary,
              flexShrink: 0,
          }}>
              {isMigrationWorkflow ? <RocketOutlined /> : <SwapOutlined />}
          </div>
          <div style={{ minWidth: 0 }}>
              <div style={{ fontSize: 16, fontWeight: 700, color: darkMode ? '#f8fafc' : '#0f172a' }}>{title}</div>
              <div style={{ marginTop: 4, fontSize: 12, lineHeight: 1.6, color: darkMode ? 'rgba(255,255,255,0.56)' : 'rgba(15,23,42,0.58)' }}>{description}</div>
          </div>
      </div>
  );

  return (
    <>
    <Modal
        title={renderModalTitle(
            isMigrationWorkflow
                ? tr('data_sync.title.migration_workbench')
                : (isCompareEntry ? entryPresentation.title : tr('data_sync.title.sync_workbench')),
            isMigrationWorkflow
                ? tr('data_sync.title.migration_description')
                : (isCompareEntry ? entryPresentation.description : tr('data_sync.title.sync_description')),
        )}
        open={open}
        onCancel={() => {
            if (syncing) {
                message.warning(tr('data_sync.message.close_blocked_running'));
                return;
            }
            onClose();
        }}
        width={920}
        footer={null}
        destroyOnHidden
        closable={!syncing}
        maskClosable={!syncing}
        styles={{
            content: modalPanelStyle,
            header: { background: 'transparent', borderBottom: 'none', paddingBottom: 10 },
            body: {
                paddingTop: 8,
                height: 760,
                maxHeight: 'calc(100vh - 120px)',
                overflow: 'hidden',
                display: 'flex',
                flexDirection: 'column',
            },
            footer: { background: 'transparent', borderTop: 'none', paddingTop: 12 },
        }}
    >
      <div style={modalWorkspaceStyle}>
      <div style={{ flex: '0 0 auto' }}>
      <div style={heroPanelStyle}>
          <div style={{ display: 'flex', justifyContent: 'space-between', gap: 12, alignItems: 'flex-start', flexWrap: 'wrap' }}>
              <div style={{ minWidth: 0 }}>
                  <div style={{ fontSize: 18, fontWeight: 700, color: darkMode ? '#f8fafc' : '#0f172a' }}>
                      {isMigrationWorkflow
                          ? tr('data_sync.title.migration')
                          : (isCompareEntry ? entryPresentation.heroTitle : tr('data_sync.title.sync'))}
                  </div>
                  <div style={{ marginTop: 6, fontSize: 13, lineHeight: 1.7, color: darkMode ? 'rgba(255,255,255,0.62)' : 'rgba(15,23,42,0.62)' }}>
                      {isMigrationWorkflow
                          ? tr('data_sync.title.migration_description')
                          : (isCompareEntry ? entryPresentation.heroDescription : tr('data_sync.title.sync_description'))}
                  </div>
              </div>
              <div style={{ display: 'flex', flexWrap: 'wrap', gap: 8 }}>
                  <span style={badgeStyle}>
                      {isMigrationWorkflow ? <RocketOutlined /> : <SwapOutlined />} {isMigrationWorkflow
                          ? tr('data_sync.badge.migration_mode')
                          : (isCompareEntry ? entryPresentation.badgeText : tr('data_sync.badge.sync_mode'))}
                  </span>
                  <span style={badgeStyle}><DatabaseOutlined /> {sourceConnId ? tr('data_sync.badge.source_selected') : tr('data_sync.badge.source_pending')}</span>
                  <span style={badgeStyle}><TableOutlined /> {tr('data_sync.badge.table_count', { count: selectedTables.length || 0 })}</span>
              </div>
          </div>
      </div>
      <Steps current={currentStep} style={{ marginBottom: 24 }}>
        <Step title={tr('data_sync.step.configure')} />
        <Step title={tr('data_sync.step.select_tables')} />
        <Step title={isCompareEntry ? entryPresentation.resultTitle : tr('data_sync.step.result')} />
      </Steps>
      </div>

      <div style={modalScrollableContentStyle}>
      {/* STEP 1: CONFIG */}
      {currentStep === 0 && (
          <div>
              <div style={{ display: 'grid', gridTemplateColumns: 'minmax(0, 1fr) 44px minmax(0, 1fr)', gap: 18, alignItems: 'stretch' }}>
                  <Card
                      title={tr('data_sync.title.source_database')}
                      style={shellCardStyle}
                      styles={{ header: { borderBottom: darkMode ? '1px solid rgba(255,255,255,0.08)' : '1px solid rgba(15,23,42,0.06)', fontWeight: 700 }, body: { padding: 18 } }}
                  >
                      <Form layout="vertical">
                          <Form.Item label={tr('data_sync.field.connection')}>
                              <Select value={sourceConnId} onChange={handleSourceConnChange}>
                                  {connections.map(c => <Option key={c.id} value={c.id}>{c.name} ({c.config.type})</Option>)}
                              </Select>
                          </Form.Item>
                          <Form.Item label={tr('data_sync.field.database')}>
                              <Select value={sourceDb} onChange={setSourceDb} showSearch>
                                  {sourceDbs.map(d => <Option key={d} value={d}>{d}</Option>)}
                              </Select>
                          </Form.Item>
                      </Form>
                  </Card>
                  <div style={{ display: 'grid', placeItems: 'center' }}>
                      <div style={{ ...badgeStyle, width: 44, height: 44, borderRadius: 14, justifyContent: 'center', padding: 0 }}>
                          <SwapOutlined />
                      </div>
                  </div>
                  <Card
                      title={tr('data_sync.title.target_database')}
                      style={shellCardStyle}
                      styles={{ header: { borderBottom: darkMode ? '1px solid rgba(255,255,255,0.08)' : '1px solid rgba(15,23,42,0.06)', fontWeight: 700 }, body: { padding: 18 } }}
                  >
                      <Form layout="vertical">
                          <Form.Item label={tr('data_sync.field.connection')}>
                              <Select value={targetConnId} onChange={handleTargetConnChange}>
                                  {connections.map(c => <Option key={c.id} value={c.id}>{c.name} ({c.config.type})</Option>)}
                              </Select>
                          </Form.Item>
                          <Form.Item label={tr('data_sync.field.database')}>
                              <Select value={targetDb} onChange={setTargetDb} showSearch>
                                  {targetDbs.map(d => <Option key={d} value={d}>{d}</Option>)}
                              </Select>
                          </Form.Item>
                      </Form>
                  </Card>
              </div>

              <Card
                  title={isMigrationWorkflow
                      ? tr('data_sync.title.migration_options')
                      : (isCompareEntry ? entryPresentation.optionTitle : tr('data_sync.title.sync_options'))}
                  style={{ ...shellCardStyle, marginTop: 18 }}
                  styles={{ header: { borderBottom: darkMode ? '1px solid rgba(255,255,255,0.08)' : '1px solid rgba(15,23,42,0.06)', fontWeight: 700 }, body: { padding: 18 } }}
              >
                  <div style={{ ...quietPanelStyle, marginBottom: 14 }}>
                      <Text style={{ color: darkMode ? 'rgba(255,255,255,0.72)' : 'rgba(15,23,42,0.68)', lineHeight: 1.7 }}>
                          {isCompareEntry
                              ? tr('data_sync.compare_entry.workflow_help')
                              : tr('data_sync.help.workflow_type')}
                      </Text>
                  </div>
                  <Form layout="vertical">
                      {!isCompareEntry && (
                          <Form.Item label={tr('data_sync.field.workflow_type')}>
                              <Select value={workflowType} onChange={setWorkflowType}>
                                  <Option value="sync">{tr('data_sync.option.workflow.sync')}</Option>
                                  <Option value="migration" disabled={isSourceQueryMode}>{tr('data_sync.option.workflow.migration')}</Option>
                              </Select>
                          </Form.Item>
                      )}
                      {!isSchemaCompareEntry && (
                          <Form.Item label={tr('data_sync.field.source_dataset_mode')}>
                              <Select value={sourceDatasetMode} onChange={setSourceDatasetMode}>
                                  <Option value="table">{isCompareEntry ? tr('data_sync.compare_entry.option.source_dataset.table') : tr('data_sync.option.source_dataset.table')}</Option>
                                  <Option value="query">{isCompareEntry ? tr('data_sync.compare_entry.option.source_dataset.query') : tr('data_sync.option.source_dataset.query')}</Option>
                              </Select>
                          </Form.Item>
                      )}
                        <Alert
                            type={isMigrationWorkflow || isCompareEntry ? 'info' : 'success'}
                            showIcon
                            style={{ marginBottom: 12 }}
                            message={isMigrationWorkflow
                                ? tr('data_sync.alert.migration_mode')
                                : isSchemaCompareEntry
                                    ? tr('data_sync.compare_entry.alert.schema')
                                    : isDataCompareEntry
                                        ? tr('data_sync.compare_entry.alert.data')
                                        : tr('data_sync.alert.sync_mode')}
                        />
                      {isSourceQueryMode && (
                          <Alert
                              type="info"
                              showIcon
                              style={{ marginBottom: 12 }}
                              message={tr('data_sync.alert.query_mode')}
                          />
                      )}
                      {!isCompareEntry && (
                          <Form.Item label={isMigrationWorkflow ? tr('data_sync.field.migration_content') : tr('data_sync.field.sync_content')}>
                              <Select value={syncContent} onChange={setSyncContent}>
                                  <Option value="data">{tr('data_sync.option.content.data')}</Option>
                                  <Option value="schema" disabled={isSourceQueryMode}>{tr('data_sync.option.content.schema')}</Option>
                                  <Option value="both" disabled={isSourceQueryMode}>{tr('data_sync.option.content.both')}</Option>
                              </Select>
                          </Form.Item>
                      )}
                      {!isCompareEntry && (
                          <Form.Item label={isMigrationWorkflow ? tr('data_sync.field.migration_mode') : tr('data_sync.field.sync_mode')}>
                              <Select value={syncMode} onChange={setSyncMode} disabled={syncContent === 'schema'}>
                                  <Option value="insert_update">{tr('data_sync.option.sync_mode.insert_update')}</Option>
                                  <Option value="insert_only">{tr('data_sync.option.sync_mode.insert_only')}</Option>
                                  <Option value="full_overwrite">{tr('data_sync.option.sync_mode.full_overwrite')}</Option>
                              </Select>
                          </Form.Item>
                      )}
                      {!isCompareEntry && (
                          <Form.Item label={isMigrationWorkflow ? tr('data_sync.field.target_table_strategy') : tr('data_sync.field.target_table_requirement')}>
                              <Select value={targetTableStrategy} onChange={setTargetTableStrategy} disabled={!isMigrationWorkflow || isSourceQueryMode}>
                                  <Option value="existing_only">{tr('data_sync.option.target_strategy.existing_only')}</Option>
                                  <Option value="auto_create_if_missing">{tr('data_sync.option.target_strategy.auto_create_if_missing')}</Option>
                                  <Option value="smart">{tr('data_sync.option.target_strategy.smart')}</Option>
                              </Select>
                          </Form.Item>
                      )}
                      {isRedisMongoKeyspaceMigration && (
                          <Form.Item
                              label={tr('data_sync.field.mongo_collection_name')}
                              extra={sourceType === 'redis'
                                  ? tr('data_sync.help.mongo_collection_redis_to_mongo')
                                  : tr('data_sync.help.mongo_collection_mongo_to_redis')}
                          >
                              <Input
                                  value={mongoCollectionName}
                                  onChange={(e) => setMongoCollectionName(e.target.value)}
                                  placeholder={defaultMongoCollectionName || tr('data_sync.placeholder.mongo_collection_name')}
                                  allowClear
                                  maxLength={128}
                              />
                          </Form.Item>
                      )}
                      {(!isCompareEntry || isSchemaCompareEntry) && (
                          <Form.Item>
                              <Checkbox checked={autoAddColumns} onChange={(e) => setAutoAddColumns(e.target.checked)} disabled={isSourceQueryMode}>
                                  {isSchemaCompareEntry
                                      ? tr('data_sync.compare_entry.option.auto_add_columns')
                                      : tr('data_sync.option.auto_add_columns')}
                              </Checkbox>
                          </Form.Item>
                      )}
                      {!isCompareEntry && (
                          <Form.Item>
                              <Checkbox checked={createIndexes} onChange={(e) => setCreateIndexes(e.target.checked)} disabled={!isMigrationWorkflow || targetTableStrategy === 'existing_only' || isSourceQueryMode}>
                                  {tr('data_sync.option.create_indexes')}
                              </Checkbox>
                          </Form.Item>
                      )}
                      {isMigrationWorkflow && targetTableStrategy !== 'existing_only' && (
                          <Alert
                              type="info"
                              showIcon
                              message={tr('data_sync.alert.auto_create_planner_scope')}
                              style={{ marginBottom: 12 }}
                          />
                      )}
                      {!isCompareEntry && !isMigrationWorkflow && (
                          <Alert
                              type="info"
                              showIcon
                              message={tr('data_sync.alert.existing_target_only')}
                              style={{ marginBottom: 12 }}
                          />
                      )}
                      {syncContent !== 'schema' && syncMode === 'full_overwrite' && (
                          <Alert
                              type="warning"
                              showIcon
                              message={tr('data_sync.alert.full_overwrite')}
                          />
                      )}
                  </Form>
              </Card>
          </div>
      )}

      {/* STEP 2: TABLES */}
      {currentStep === 1 && (
          <div style={{ display: 'flex', flexDirection: 'column', gap: 14 }}>
              <div style={quietPanelStyle}>
                  {!isSourceQueryMode && (
                      <>
                          <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 10 }}>
                              <Text type="secondary">{isCompareEntry ? entryPresentation.tableSelectLabel : tr('data_sync.help.select_tables')}</Text>
                              <Checkbox checked={showSameTables} onChange={(e) => setShowSameTables(e.target.checked)}>
                                  {tr('data_sync.option.show_same_tables')}
                              </Checkbox>
                          </div>
                          <Transfer
                              dataSource={allTables.map(t => ({ key: t, title: t }))}
                              titles={[tr('data_sync.transfer.source_tables'), tr('data_sync.transfer.selected_tables')]}
                              targetKeys={selectedTables}
                              onChange={(keys) => setSelectedTables(keys as string[])}
                              render={item => item.title}
                              listStyle={{ width: 390, height: 320, marginTop: 0, borderRadius: 14, overflow: 'hidden' }}
                              locale={{ itemUnit: tr('data_sync.transfer.item_unit'), itemsUnit: tr('data_sync.transfer.items_unit'), searchPlaceholder: tr('data_sync.transfer.search_placeholder'), notFoundContent: tr('data_sync.transfer.empty') }}
                          />
                      </>
                  )}
                  {isSourceQueryMode && (
                      <Form layout="vertical">
                          <Alert
                              type="info"
                              showIcon
                              style={{ marginBottom: 12 }}
                              message={tr('data_sync.help.source_query_mode')}
                          />
                          <Form.Item label={tr('data_sync.field.source_query_sql')}>
                              <TextArea
                                  value={sourceQuery}
                                  onChange={(e) => setSourceQuery(e.target.value)}
                                  rows={8}
                                  placeholder={tr('data_sync.placeholder.source_query_sql')}
                                  spellCheck={false}
                              />
                          </Form.Item>
                          <Form.Item label={tr('data_sync.field.target_table')}>
                              <Select
                                  value={selectedTables[0]}
                                  onChange={(value) => setSelectedTables(value ? [value] : [])}
                                  showSearch
                                  allowClear
                                  placeholder={tr('data_sync.placeholder.target_table')}
                                  optionFilterProp="children"
                              >
                                  {allTables.map((table) => <Option key={table} value={table}>{table}</Option>)}
                              </Select>
                          </Form.Item>
                      </Form>
                  )}
              </div>

              {diffTables.length > 0 && (
                  <div style={quietPanelStyle}>
                      <Divider orientation="left" style={{ marginTop: 0 }}>{tr('data_sync.title.compare_result')}</Divider>
                      {analysisWarnings.length > 0 && (
                          <Alert
                              type="warning"
                              showIcon
                              message={tr('data_sync.message.precheck_warnings')}
                              description={
                                  <ul style={{ margin: 0, paddingLeft: 18 }}>
                                      {analysisWarnings.slice(0, 8).map((item) => <li key={item}>{item}</li>)}
                                      {analysisWarnings.length > 8 && <li>{tr('data_sync.message.more_items_collapsed', { count: analysisWarnings.length - 8 })}</li>}
                                  </ul>
                              }
                              style={{ marginBottom: 12 }}
                          />
                      )}
                      <Table
                          size="small"
                          pagination={false}
                          rowKey={(r: any) => r.table}
                          dataSource={diffTables.filter(t => {
                              const ins = Number(t.inserts || 0);
                              const upd = Number(t.updates || 0);
                              const del = Number(t.deletes || 0);
                              const same = Number(t.same || 0);
                              const msg = String(t.message || '').trim();
                              const can = !!t.canSync;
                              const warns = Array.isArray(t.warnings) ? t.warnings.length : 0;
                              const unsupported = Array.isArray(t.unsupportedObjects) ? t.unsupportedObjects.length : 0;
                              if (showSameTables) return true;
                              if (!can) return true;
                              if (msg || warns > 0 || unsupported > 0) return true;
                              return ins > 0 || upd > 0 || del > 0 || same === 0;
                          })}
                          columns={[
                              { title: tr('data_sync.table.table_name'), dataIndex: 'table', key: 'table', ellipsis: true },
                              {
                                  title: tr('data_sync.table.target_table'),
                                  key: 'targetTableExists',
                                  width: 90,
                                  render: (_: any, r: any) => r.targetTableExists ? tr('data_sync.table.target_exists') : tr('data_sync.table.target_missing')
                              },
                              {
                                  title: tr('data_sync.table.plan'),
                                  dataIndex: 'plannedAction',
                                  key: 'plannedAction',
                                  width: 220,
                                  ellipsis: true,
                                  render: (v: any) => String(v || '')
                              },
                              {
                                  title: tr('data_sync.table.insert'),
                                  key: 'inserts',
                                  width: 90,
                                  render: (_: any, r: any) => {
                                      const ops = tableOptions[r.table] || { insert: true, update: true, delete: false };
                                      const disabled = !r.canSync || analyzing || Number(r.inserts || 0) === 0;
                                      return (
                                          <Checkbox checked={!!ops.insert} disabled={disabled} onChange={(e) => updateTableOption(r.table, 'insert', e.target.checked)}>
                                              {Number(r.inserts || 0)}
                                          </Checkbox>
                                      );
                                  }
                              },
                              {
                                  title: tr('data_sync.table.update'),
                                  key: 'updates',
                                  width: 90,
                                  render: (_: any, r: any) => {
                                      const ops = tableOptions[r.table] || { insert: true, update: true, delete: false };
                                      const disabled = !r.canSync || analyzing || Number(r.updates || 0) === 0;
                                      return (
                                          <Checkbox checked={!!ops.update} disabled={disabled} onChange={(e) => updateTableOption(r.table, 'update', e.target.checked)}>
                                              {Number(r.updates || 0)}
                                          </Checkbox>
                                      );
                                  }
                              },
                              {
                                  title: tr('data_sync.table.delete'),
                                  key: 'deletes',
                                  width: 90,
                                  render: (_: any, r: any) => {
                                      const ops = tableOptions[r.table] || { insert: true, update: true, delete: false };
                                      const disabled = !r.canSync || analyzing || Number(r.deletes || 0) === 0;
                                      return (
                                          <Checkbox checked={!!ops.delete} disabled={disabled} onChange={(e) => updateTableOption(r.table, 'delete', e.target.checked)}>
                                              {Number(r.deletes || 0)}
                                          </Checkbox>
                                      );
                                  }
                              },
                              { title: tr('data_sync.table.same'), dataIndex: 'same', key: 'same', width: 70, render: (v: any) => Number(v || 0) },
                              {
                                  title: tr('data_sync.table.risk'),
                                  key: 'warnings',
                                  width: 220,
                                  render: (_: any, r: any) => {
                                      const warns = [...(Array.isArray(r.warnings) ? r.warnings : []), ...(Array.isArray(r.unsupportedObjects) ? r.unsupportedObjects : [])];
                                      if (warns.length === 0) return '-';
                                      return (
                                          <div style={{ color: '#d48806', fontSize: 12, lineHeight: 1.5 }}>
                                              {warns.slice(0, 2).map((item: string) => <div key={item}>{item}</div>)}
                                              {warns.length > 2 && <div>{tr('data_sync.message.more_items_collapsed', { count: warns.length - 2 })}</div>}
                                          </div>
                                      );
                                  }
                              },
                              {
                                  title: tr('data_sync.table.preview'),
                                  key: 'preview',
                                  width: 80,
                                  render: (_: any, r: any) => {
                                      const can = !!r.canSync;
                                      const hasDiff = Number(r.inserts || 0) + Number(r.updates || 0) + Number(r.deletes || 0) > 0;
                                      const hasSchemaDiff = Number(r.schemaDiffCount || 0) > 0;
                                      return (
                                          <Button size="small" disabled={!can || !(hasDiff || hasSchemaDiff) || analyzing} onClick={() => openPreview(r.table)}>
                                              {tr('data_sync.action.view')}
                                          </Button>
                                      );
                                  }
                              }
                          ]}
                      />
                  </div>
              )}
          </div>
      )}

      {/* STEP 3: RESULT */}
      {currentStep === 2 && (
          <div style={{ display: 'flex', flexDirection: 'column', gap: 14 }}>
              <div style={quietPanelStyle}>
                  <Alert
                      message={syncing
                          ? (isCompareEntry ? tr('data_sync.compare_entry.result.running') : tr('data_sync.result.running'))
                          : (syncResult?.success
                              ? (isCompareEntry ? tr('data_sync.compare_entry.result.completed') : tr('data_sync.result.completed'))
                              : (isCompareEntry ? tr('data_sync.compare_entry.result.failed') : tr('data_sync.result.failed')))}
                      description={
                          syncing
                              ? (isCompareEntry
                                  ? tr('data_sync.compare_entry.result.running_description', {
                                      stage: syncProgress.stage || tr('data_sync.compare_entry.result.stage_fallback'),
                                      table: syncProgress.table ? tr('data_sync.compare_entry.result.table_suffix', { table: syncProgress.table }) : '',
                                    })
                                  : tr('data_sync.result.running_description', {
                                      stage: syncProgress.stage || tr('data_sync.progress.stage.executing'),
                                      table: syncProgress.table ? tr('data_sync.result.table_suffix', { table: syncProgress.table }) : '',
                                    }))
                              : (syncResult?.message || (isCompareEntry
                                  ? tr('data_sync.compare_entry.result.success_summary', {
                                      tables: diffTables.length || syncResult?.tablesSynced || 0,
                                    })
                                  : tr('data_sync.result.success_summary', {
                                      tables: syncResult?.tablesSynced || 0,
                                      inserted: syncResult?.rowsInserted || 0,
                                      updated: syncResult?.rowsUpdated || 0,
                                    })))
                      }
                      type={syncing ? "info" : (syncResult?.success ? "success" : "error")}
                      showIcon
                  />

                  <div style={{ marginTop: 14 }}>
                      <Progress
                          percent={syncProgress.percent}
                          status={syncing ? "active" : (syncResult?.success ? "success" : "exception")}
                          format={() => `${syncProgress.current}/${syncProgress.total}`}
                      />
                  </div>
              </div>
              <div style={quietPanelStyle}>
                  <Divider orientation="left" style={{ marginTop: 0 }}>{isCompareEntry ? tr('data_sync.compare_entry.title.analysis_log') : tr('data_sync.title.execution_log')}</Divider>
                  <div
                      ref={logBoxRef}
                      onScroll={() => {
                          const el = logBoxRef.current;
                          if (!el) return;
                          const nearBottom = el.scrollHeight - el.scrollTop - el.clientHeight < 40;
                          autoScrollRef.current = nearBottom;
                      }}
                      style={{
                          background: darkMode ? 'rgba(255,255,255,0.03)' : 'rgba(248,250,252,0.92)',
                          border: darkMode ? '1px solid rgba(255,255,255,0.08)' : '1px solid rgba(15,23,42,0.06)',
                          borderRadius: 14,
                          padding: 12,
                          height: 300,
                          overflowY: 'auto',
                          fontFamily: 'var(--gn-font-mono)'
                      }}
                  >
                      {syncLogs.map((item, i: number) => <div key={i}>{renderSyncLogItem(item)}</div>)}
                  </div>
              </div>
          </div>
      )}

      </div>

      <div style={modalFooterBarStyle}>
          {currentStep === 0 && (
              <Button type="primary" onClick={nextToTables} loading={loading}>{tr('data_sync.action.next')}</Button>
          )}
          {currentStep === 1 && (
              <>
                  <Button onClick={() => setCurrentStep(0)} style={{ marginRight: 8 }}>{tr('data_sync.action.previous')}</Button>
                  <Button
                      onClick={analyzeDiff}
                      loading={loading}
                      disabled={(isCompareEntry ? false : syncContent === 'schema') || selectedTables.length === 0 || analyzing || (isSourceQueryMode && !sourceQuery.trim())}
                      style={{ marginRight: 8 }}
                  >
                      {isCompareEntry ? entryPresentation.analyzeButtonText : tr('data_sync.action.analyze_diff')}
                  </Button>
                  {isCompareEntry && (
                      <Button onClick={onClose}>
                          {entryPresentation.closeButtonText}
                      </Button>
                  )}
                  {!isCompareEntry && (
                      <Button
                          type="primary"
                          onClick={runSync}
                          loading={loading}
                          disabled={selectedTables.length === 0 || (isSourceQueryMode && !sourceQuery.trim()) || (syncContent !== 'schema' && diffTables.length === 0)}
                      >
                          {tr('data_sync.action.start_sync')}
                      </Button>
                  )}
              </>
          )}
          {currentStep === 2 && (
              <>
                  <Button disabled={syncing} onClick={() => setCurrentStep(1)} style={{ marginRight: 8 }}>{isCompareEntry ? tr('data_sync.compare_entry.action.return_to_compare') : tr('data_sync.action.continue_sync')}</Button>
                  <Button type="primary" disabled={syncing} onClick={onClose}>{isCompareEntry ? entryPresentation.closeButtonText : tr('data_sync.action.close')}</Button>
              </>
          )}
      </div>
      </div>
    </Modal>
    <Drawer
        title={tr('data_sync.preview.title', { table: previewTable })}
        styles={{ body: { background: darkMode ? 'rgba(9,13,20,0.98)' : '#f8fafc' } }}
        open={previewOpen}
        onClose={() => { setPreviewOpen(false); setPreviewTable(''); setPreviewData(null); }}
        width={900}
    >
        {previewLoading && <Alert type="info" showIcon message={tr('data_sync.preview.loading')} />}
        {!previewLoading && previewData && (
            <div>
                <Alert
                    type="info"
                    showIcon
                    message={
                        previewHasDataDiff
                            ? tr('data_sync.preview.data_summary', { inserts: previewData.totalInserts || 0, updates: previewData.totalUpdates || 0, deletes: previewData.totalDeletes || 0 })
                            : (previewData.schemaSummary || tr('data_sync.preview.schema_statement_count', { count: previewSql.statementCount }))
                    }
                />
                {previewSchemaWarnings.length > 0 && (
                    <Alert
                        style={{ marginTop: 12 }}
                        type="warning"
                        showIcon
                        message={tr('data_sync.preview.schema_warning_title')}
                        description={
                            <ul style={{ margin: 0, paddingLeft: 18 }}>
                                {previewSchemaWarnings.slice(0, 8).map((item) => <li key={item}>{item}</li>)}
                                {previewSchemaWarnings.length > 8 && <li>{tr('data_sync.message.more_items_collapsed', { count: previewSchemaWarnings.length - 8 })}</li>}
                            </ul>
                        }
                    />
                )}
                <Divider />
                <Tabs
                    items={[
                        ...(previewHasSchemaStatements ? [{
                            key: 'schema',
                            label: tr('data_sync.preview.tab.schema', { count: Array.isArray(previewData.schemaStatements) ? previewData.schemaStatements.length : 0 }),
                            children: (
                                <div>
                                    <Text type="secondary">
                                        {previewData.schemaSummary || tr('data_sync.preview.schema_plan_help')}
                                    </Text>
                                    <pre
                                        style={{
                                            marginTop: 8,
                                            marginBottom: 0,
                                            padding: 10,
                                            border: '1px solid #f0f0f0',
                                            borderRadius: 6,
                                            background: '#fafafa',
                                            maxHeight: 420,
                                            overflow: 'auto',
                                            whiteSpace: 'pre-wrap',
                                            wordBreak: 'break-word'
                                        }}
                                    >
                                        {Array.isArray(previewData.schemaStatements) && previewData.schemaStatements.length > 0
                                            ? previewData.schemaStatements.join('\n')
                                            : tr('data_sync.preview.sql.no_schema_changes')}
                                    </pre>
                                </div>
                            )
                        }] : []),
                        ...(previewHasDataDiff ? [{
                            key: 'insert',
                            label: tr('data_sync.preview.tab.insert', { count: previewData.totalInserts || 0 }),
                            children: (
                                <div>
                                    <Text type="secondary">{isCompareEntry ? tr('data_sync.compare_entry.preview.selection_hint') : tr('data_sync.preview.selection_hint.insert')}</Text>
                                    <Table
                                        size="small"
                                        style={{ marginTop: 8 }}
                                        rowKey={(r: any) => r.pk}
                                        dataSource={(previewData.inserts || []).map((r: any) => ({ ...r, key: r.pk }))}
                                        pagination={false}
                                        rowSelection={{
                                            selectedRowKeys: (tableOptions[previewTable]?.selectedInsertPks || []) as any,
                                            onChange: (keys) => updateTableOption(previewTable, 'selectedInsertPks', keys as string[]),
                                            getCheckboxProps: () => ({ disabled: !tableOptions[previewTable]?.insert }),
                                        }}
                                        columns={[
                                            { title: previewData.pkColumn || tr('data_sync.preview.column.primary_key'), dataIndex: 'pk', key: 'pk', width: 200, ellipsis: true },
                                            { title: tr('data_sync.preview.column.data'), dataIndex: 'row', key: 'row', render: (v: any) => <pre style={{ margin: 0, maxHeight: 140, overflow: 'auto' }}>{JSON.stringify(v, null, 2)}</pre> }
                                        ]}
                                    />
                                </div>
                            )
                        },
                        {
                            key: 'update',
                            label: tr('data_sync.preview.tab.update', { count: previewData.totalUpdates || 0 }),
                            children: (
                                <div>
                                    <Text type="secondary">{isCompareEntry ? tr('data_sync.compare_entry.preview.selection_hint') : tr('data_sync.preview.selection_hint.update')}</Text>
                                    <Table
                                        size="small"
                                        style={{ marginTop: 8 }}
                                        rowKey={(r: any) => r.pk}
                                        dataSource={(previewData.updates || []).map((r: any) => ({ ...r, key: r.pk }))}
                                        pagination={false}
                                        rowSelection={{
                                            selectedRowKeys: (tableOptions[previewTable]?.selectedUpdatePks || []) as any,
                                            onChange: (keys) => updateTableOption(previewTable, 'selectedUpdatePks', keys as string[]),
                                            getCheckboxProps: () => ({ disabled: !tableOptions[previewTable]?.update }),
                                        }}
                                        columns={[
                                            { title: previewData.pkColumn || tr('data_sync.preview.column.primary_key'), dataIndex: 'pk', key: 'pk', width: 200, ellipsis: true },
                                            { title: tr('data_sync.preview.column.changed_columns'), dataIndex: 'changedColumns', key: 'changedColumns', render: (v: any) => Array.isArray(v) ? v.join(', ') : '' },
                                            {
                                                title: tr('data_sync.preview.column.detail'),
                                                key: 'detail',
                                                width: 80,
                                                render: (_: any, r: any) => (
                                                    <Button size="small" onClick={() => {
                                                        Modal.info({
                                                            title: tr('data_sync.preview.update_detail_title', { table: previewTable, pk: r.pk }),
                                                            width: 900,
                                                            content: (
                                                                <div style={{ display: 'flex', gap: 12 }}>
                                                                    <div style={{ flex: 1 }}>
                                                                        <Title level={5}>{tr('data_sync.preview.side.source')}</Title>
                                                                        <pre style={{ maxHeight: 360, overflow: 'auto', background: '#f5f5f5', padding: 8 }}>{JSON.stringify(r.source, null, 2)}</pre>
                                                                    </div>
                                                                    <div style={{ flex: 1 }}>
                                                                        <Title level={5}>{tr('data_sync.preview.side.target')}</Title>
                                                                        <pre style={{ maxHeight: 360, overflow: 'auto', background: '#f5f5f5', padding: 8 }}>{JSON.stringify(r.target, null, 2)}</pre>
                                                                    </div>
                                                                </div>
                                                            )
                                                        });
                                                    }}>{tr('data_sync.action.view')}</Button>
                                                )
                                            }
                                        ]}
                                    />
                                </div>
                            )
                        },
                        {
                            key: 'delete',
                            label: tr('data_sync.preview.tab.delete', { count: previewData.totalDeletes || 0 }),
                            children: (
                                <div>
                                    <Alert type="warning" showIcon message={tr('data_sync.preview.delete_warning')} />
                                    <Text type="secondary">{isCompareEntry ? tr('data_sync.compare_entry.preview.selection_hint') : tr('data_sync.preview.selection_hint.delete')}</Text>
                                    <Table
                                        size="small"
                                        style={{ marginTop: 8 }}
                                        rowKey={(r: any) => r.pk}
                                        dataSource={(previewData.deletes || []).map((r: any) => ({ ...r, key: r.pk }))}
                                        pagination={false}
                                        rowSelection={{
                                            selectedRowKeys: (tableOptions[previewTable]?.selectedDeletePks || []) as any,
                                            onChange: (keys) => updateTableOption(previewTable, 'selectedDeletePks', keys as string[]),
                                            getCheckboxProps: () => ({ disabled: !tableOptions[previewTable]?.delete }),
                                        }}
                                        columns={[
                                            { title: previewData.pkColumn || tr('data_sync.preview.column.primary_key'), dataIndex: 'pk', key: 'pk', width: 200, ellipsis: true },
                                            { title: tr('data_sync.preview.column.data'), dataIndex: 'row', key: 'row', render: (v: any) => <pre style={{ margin: 0, maxHeight: 140, overflow: 'auto' }}>{JSON.stringify(v, null, 2)}</pre> }
                                        ]}
                                    />
                                </div>
                            )
                        }] : []),
                        {
                            key: 'sql',
                            label: tr('data_sync.preview.tab.sql', { count: previewSql.statementCount }),
                            children: (
                                <div>
                                    <Alert
                                        type="info"
                                        showIcon
                                        message={
                                            previewHasDataDiff
                                                ? (isCompareEntry ? tr('data_sync.compare_entry.preview.sql.data_help') : tr('data_sync.preview.sql.data_help'))
                                                : (isCompareEntry ? tr('data_sync.compare_entry.preview.sql.schema_help') : tr('data_sync.preview.sql.schema_help'))
                                        }
                                    />
                                    <div style={{ marginTop: 8, marginBottom: 8, display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
                                        <Text type="secondary">
                                            {previewHasDataDiff
                                                ? tr('data_sync.preview.sql.statement_count', { count: previewSql.statementCount })
                                                : tr('data_sync.preview.sql.schema_statement_count', { count: previewSql.statementCount })}
                                        </Text>
                                        <Button
                                            size="small"
                                            disabled={!previewSql.sqlText}
                                            onClick={async () => {
                                                try {
                                                    await navigator.clipboard.writeText(previewSql.sqlText || '');
                                                    message.success(tr('data_sync.preview.message.sql_copied'));
                                                } catch {
                                                    message.error(tr('data_sync.preview.message.copy_failed'));
                                                }
                                            }}
                                        >
                                            {tr('data_sync.preview.action.copy_sql')}
                                        </Button>
                                    </div>
                                    <pre
                                        style={{
                                            margin: 0,
                                            padding: 10,
                                            border: '1px solid #f0f0f0',
                                            borderRadius: 6,
                                            background: '#fafafa',
                                            maxHeight: 420,
                                            overflow: 'auto',
                                            whiteSpace: 'pre-wrap',
                                            wordBreak: 'break-word'
                                        }}
                                    >
                                        {previewSql.sqlText || (previewHasDataDiff ? tr('data_sync.preview.sql.no_data_sql') : tr('data_sync.preview.sql.no_schema_changes'))}
                                    </pre>
                                </div>
                            )
                        }
                    ]}
                />
            </div>
        )}
    </Drawer>
    </>
  );
};

export default DataSyncModal;
