import React, { useState, useEffect, useRef } from 'react';
import { Modal, Form, Input, InputNumber, Button, message, Checkbox, Divider, Select, Alert, Card, Row, Col, Typography, Collapse, Space, Table, Tag } from 'antd';
import { DatabaseOutlined, ConsoleSqlOutlined, FileTextOutlined, CloudServerOutlined, AppstoreAddOutlined, CloudOutlined } from '@ant-design/icons';
import { useStore } from '../store';
import { DBGetDatabases, MongoDiscoverMembers, TestConnection, RedisConnect } from '../../wailsjs/go/app/App';
import { MongoMemberInfo, SavedConnection } from '../types';

const { Meta } = Card;
const { Text } = Typography;

const getDefaultPortByType = (type: string) => {
  switch (type) {
    case 'mysql': return 3306;
    case 'postgres': return 5432;
    case 'redis': return 6379;
    case 'tdengine': return 6041;
    case 'oracle': return 1521;
    case 'dameng': return 5236;
    case 'kingbase': return 54321;
    case 'sqlserver': return 1433;
    case 'mongodb': return 27017;
    case 'highgo': return 5866;
    case 'mariadb': return 3306;
    case 'vastbase': return 5432;
    default: return 3306;
  }
};

const ConnectionModal: React.FC<{ open: boolean; onClose: () => void; initialValues?: SavedConnection | null }> = ({ open, onClose, initialValues }) => {
  const [form] = Form.useForm();
  const [loading, setLoading] = useState(false);
  const [useSSH, setUseSSH] = useState(false);
  const [dbType, setDbType] = useState('mysql');
  const [step, setStep] = useState(1); // 1: Select Type, 2: Configure
  const [activeGroup, setActiveGroup] = useState(0); // Active category index in step 1
  const [testResult, setTestResult] = useState<{ type: 'success' | 'error', message: string } | null>(null);
  const [dbList, setDbList] = useState<string[]>([]);
  const [redisDbList, setRedisDbList] = useState<number[]>([]); // Redis databases 0-15
  const [mongoMembers, setMongoMembers] = useState<MongoMemberInfo[]>([]);
  const [discoveringMembers, setDiscoveringMembers] = useState(false);
  const testInFlightRef = useRef(false);
  const testTimerRef = useRef<number | null>(null);
  const addConnection = useStore((state) => state.addConnection);
  const updateConnection = useStore((state) => state.updateConnection);
  const mysqlTopology = Form.useWatch('mysqlTopology', form) || 'single';
  const mongoTopology = Form.useWatch('mongoTopology', form) || 'single';
  const mongoSrv = Form.useWatch('mongoSrv', form) || false;

  const parseHostPort = (raw: string, defaultPort: number): { host: string; port: number } | null => {
      const text = String(raw || '').trim();
      if (!text) {
          return null;
      }
      if (text.startsWith('[')) {
          const closingBracket = text.indexOf(']');
          if (closingBracket > 0) {
              const host = text.slice(1, closingBracket).trim();
              const portText = text.slice(closingBracket + 1).trim().replace(/^:/, '');
              const parsedPort = Number(portText);
              return {
                  host: host || 'localhost',
                  port: Number.isFinite(parsedPort) && parsedPort > 0 ? parsedPort : defaultPort,
              };
          }
      }

      const colonCount = (text.match(/:/g) || []).length;
      if (colonCount === 1) {
          const splitIndex = text.lastIndexOf(':');
          const host = text.slice(0, splitIndex).trim();
          const portText = text.slice(splitIndex + 1).trim();
          const parsedPort = Number(portText);
          return {
              host: host || 'localhost',
              port: Number.isFinite(parsedPort) && parsedPort > 0 ? parsedPort : defaultPort,
          };
      }

      return { host: text, port: defaultPort };
  };

  const toAddress = (host: string, port: number, defaultPort: number) => {
      const safeHost = String(host || '').trim() || 'localhost';
      const safePort = Number.isFinite(Number(port)) && Number(port) > 0 ? Number(port) : defaultPort;
      return `${safeHost}:${safePort}`;
  };

  const normalizeAddressList = (rawList: unknown, defaultPort: number): string[] => {
      const list = Array.isArray(rawList) ? rawList : [];
      const seen = new Set<string>();
      const result: string[] = [];
      list.forEach((entry) => {
          const parsed = parseHostPort(String(entry || ''), defaultPort);
          if (!parsed) {
              return;
          }
          const normalized = toAddress(parsed.host, parsed.port, defaultPort);
          if (seen.has(normalized)) {
              return;
          }
          seen.add(normalized);
          result.push(normalized);
      });
      return result;
  };

  const normalizeMongoSrvHostList = (rawList: unknown, defaultPort: number): string[] => {
      const list = Array.isArray(rawList) ? rawList : [];
      const seen = new Set<string>();
      const result: string[] = [];
      list.forEach((entry) => {
          const parsed = parseHostPort(String(entry || ''), defaultPort);
          if (!parsed?.host) {
              return;
          }
          const host = String(parsed.host).trim();
          if (!host || seen.has(host)) {
              return;
          }
          seen.add(host);
          result.push(host);
      });
      return result;
  };

  const safeDecode = (text: string) => {
      try {
          return decodeURIComponent(text);
      } catch {
          return text;
      }
  };

  const parseMultiHostUri = (uriText: string, expectedScheme: string) => {
      const prefix = `${expectedScheme}://`;
      if (!uriText.toLowerCase().startsWith(prefix)) {
          return null;
      }
      let rest = uriText.slice(prefix.length);
      let queryText = '';
      const queryIndex = rest.indexOf('?');
      if (queryIndex >= 0) {
          queryText = rest.slice(queryIndex + 1);
          rest = rest.slice(0, queryIndex);
      }

      let pathText = '';
      const slashIndex = rest.indexOf('/');
      if (slashIndex >= 0) {
          pathText = rest.slice(slashIndex + 1);
          rest = rest.slice(0, slashIndex);
      }

      let hostText = rest;
      let username = '';
      let password = '';
      const atIndex = rest.lastIndexOf('@');
      if (atIndex >= 0) {
          const userInfo = rest.slice(0, atIndex);
          hostText = rest.slice(atIndex + 1);
          const colonIndex = userInfo.indexOf(':');
          if (colonIndex >= 0) {
              username = safeDecode(userInfo.slice(0, colonIndex));
              password = safeDecode(userInfo.slice(colonIndex + 1));
          } else {
              username = safeDecode(userInfo);
          }
      }

      const hosts = hostText
          .split(',')
          .map((item) => item.trim())
          .filter(Boolean);

      return {
          username,
          password,
          hosts,
          database: safeDecode(pathText),
          params: new URLSearchParams(queryText),
      };
  };

  const parseUriToValues = (uriText: string, type: string): Record<string, any> | null => {
      const trimmedUri = String(uriText || '').trim();
      if (!trimmedUri) {
          return null;
      }

      if (type === 'mysql' || type === 'mariadb') {
          const parsed = parseMultiHostUri(trimmedUri, 'mysql');
          if (!parsed) {
              return null;
          }
          const hostList = normalizeAddressList(parsed.hosts, 3306);
          const primary = parseHostPort(hostList[0] || 'localhost:3306', 3306);
          const timeoutValue = Number(parsed.params.get('timeout'));
          return {
              host: primary?.host || 'localhost',
              port: primary?.port || 3306,
              user: parsed.username,
              password: parsed.password,
              database: parsed.database || '',
              mysqlTopology: hostList.length > 1 || parsed.params.get('topology') === 'replica' ? 'replica' : 'single',
              mysqlReplicaHosts: hostList.slice(1),
              timeout: Number.isFinite(timeoutValue) && timeoutValue > 0 ? timeoutValue : undefined,
          };
      }

      if (type === 'mongodb') {
          const parsed = parseMultiHostUri(trimmedUri, 'mongodb') || parseMultiHostUri(trimmedUri, 'mongodb+srv');
          if (!parsed) {
              return null;
          }
          const isSrv = trimmedUri.toLowerCase().startsWith('mongodb+srv://');
          const hostList = isSrv
              ? normalizeMongoSrvHostList(parsed.hosts, 27017)
              : normalizeAddressList(parsed.hosts, 27017);
          const primary = isSrv
              ? { host: hostList[0] || 'localhost', port: 27017 }
              : parseHostPort(hostList[0] || 'localhost:27017', 27017);
          const timeoutMs = Number(parsed.params.get('connectTimeoutMS') || parsed.params.get('serverSelectionTimeoutMS'));
          return {
              host: primary?.host || 'localhost',
              port: primary?.port || 27017,
              user: parsed.username,
              password: parsed.password,
              database: parsed.database || '',
              mongoTopology: hostList.length > 1 || !!parsed.params.get('replicaSet') ? 'replica' : 'single',
              mongoHosts: hostList.slice(1),
              mongoSrv: isSrv,
              mongoReplicaSet: parsed.params.get('replicaSet') || '',
              mongoAuthSource: parsed.params.get('authSource') || '',
              mongoReadPreference: parsed.params.get('readPreference') || 'primary',
              mongoAuthMechanism: parsed.params.get('authMechanism') || '',
              timeout: Number.isFinite(timeoutMs) && timeoutMs > 0 ? Math.ceil(timeoutMs / 1000) : undefined,
              savePassword: true,
          };
      }

      return null;
  };

  const createUriAwareRequiredRule = (
      messageText: string,
      validateValue?: (value: unknown) => boolean
  ) => ({ getFieldValue }: { getFieldValue: (name: string) => unknown }) => ({
      validator(_: unknown, value: unknown) {
          const uriText = String(getFieldValue('uri') || '').trim();
          const type = String(getFieldValue('type') || dbType).trim().toLowerCase();
          if (uriText && parseUriToValues(uriText, type)) {
              return Promise.resolve();
          }
          const valid = validateValue
              ? validateValue(value)
              : String(value ?? '').trim() !== '';
          return valid ? Promise.resolve() : Promise.reject(new Error(messageText));
      }
  });

  const getUriPlaceholder = () => {
      if (dbType === 'mysql' || dbType === 'mariadb') {
          return 'mysql://user:pass@127.0.0.1:3306,127.0.0.2:3306/db_name?topology=replica';
      }
      if (dbType === 'mongodb') {
          return 'mongodb+srv://user:pass@cluster0.example.com/db_name?authSource=admin&authMechanism=SCRAM-SHA-256';
      }
      return '例如: postgres://user:pass@127.0.0.1:5432/db_name';
  };

  const buildUriFromValues = (values: any) => {
      const type = String(values.type || '').trim().toLowerCase();
      const defaultPort = getDefaultPortByType(type);
      const host = String(values.host || 'localhost').trim();
      const port = Number(values.port || defaultPort);
      const user = String(values.user || '').trim();
      const password = String(values.password || '');
      const database = String(values.database || '').trim();
      const timeout = Number(values.timeout || 30);
      const encodedAuth = user
          ? `${encodeURIComponent(user)}${password ? `:${encodeURIComponent(password)}` : ''}@`
          : '';

      if (type === 'mysql' || type === 'mariadb') {
          const primary = toAddress(host, port, 3306);
          const replicas = values.mysqlTopology === 'replica'
              ? normalizeAddressList(values.mysqlReplicaHosts, 3306)
              : [];
          const hosts = normalizeAddressList([primary, ...replicas], 3306);
          const params = new URLSearchParams();
          if (hosts.length > 1 || values.mysqlTopology === 'replica') {
              params.set('topology', 'replica');
          }
          if (Number.isFinite(timeout) && timeout > 0) {
              params.set('timeout', String(timeout));
          }
          const dbPath = database ? `/${encodeURIComponent(database)}` : '/';
          const query = params.toString();
          return `mysql://${encodedAuth}${hosts.join(',')}${dbPath}${query ? `?${query}` : ''}`;
      }

      if (type === 'mongodb') {
          const useSrv = !!values.mongoSrv;
          const primaryAddress = useSrv
              ? (parseHostPort(host, 27017)?.host || host || 'localhost')
              : toAddress(host, port, 27017);
          const extraNodes = values.mongoTopology === 'replica'
              ? (useSrv ? normalizeMongoSrvHostList(values.mongoHosts, 27017) : normalizeAddressList(values.mongoHosts, 27017))
              : [];
          const hosts = useSrv
              ? normalizeMongoSrvHostList([primaryAddress, ...extraNodes], 27017)
              : normalizeAddressList([primaryAddress, ...extraNodes], 27017);
          const scheme = useSrv ? 'mongodb+srv' : 'mongodb';
          const params = new URLSearchParams();
          const authSource = String(values.mongoAuthSource || database || 'admin').trim();
          if (authSource) {
              params.set('authSource', authSource);
          }
          const replicaSet = String(values.mongoReplicaSet || '').trim();
          if (replicaSet) {
              params.set('replicaSet', replicaSet);
          }
          const readPreference = String(values.mongoReadPreference || '').trim();
          if (readPreference) {
              params.set('readPreference', readPreference);
          }
          const authMechanism = String(values.mongoAuthMechanism || '').trim();
          if (authMechanism) {
              params.set('authMechanism', authMechanism);
          }
          if (Number.isFinite(timeout) && timeout > 0) {
              params.set('connectTimeoutMS', String(timeout * 1000));
              params.set('serverSelectionTimeoutMS', String(timeout * 1000));
          }
          const dbPath = database ? `/${encodeURIComponent(database)}` : '/';
          const query = params.toString();
          return `${scheme}://${encodedAuth}${hosts.join(',')}${dbPath}${query ? `?${query}` : ''}`;
      }

      const scheme = type === 'postgres' ? 'postgresql' : type;
      const dbPath = database ? `/${encodeURIComponent(database)}` : '';
      return `${scheme}://${encodedAuth}${toAddress(host, port, defaultPort)}${dbPath}`;
  };

  const handleGenerateURI = () => {
      try {
          const values = form.getFieldsValue(true);
          const uri = buildUriFromValues(values);
          form.setFieldValue('uri', uri);
          message.success('URI 已生成');
      } catch {
          message.error('生成 URI 失败');
      }
  };

  const handleParseURI = () => {
      const uriText = String(form.getFieldValue('uri') || '').trim();
      const type = String(form.getFieldValue('type') || dbType).trim().toLowerCase();
      if (!uriText) {
          message.warning('请先输入 URI');
          return;
      }
      const parsedValues = parseUriToValues(uriText, type);
      if (!parsedValues) {
          message.error('当前 URI 与数据源类型不匹配，或 URI 格式不支持');
          return;
      }
      form.setFieldsValue({ ...parsedValues, uri: uriText });
      if (testResult) {
          setTestResult(null);
      }
      message.success('已根据 URI 回填连接参数');
  };

  const handleCopyURI = async () => {
      let uriText = String(form.getFieldValue('uri') || '').trim();
      if (!uriText) {
          const values = form.getFieldsValue(true);
          uriText = buildUriFromValues(values);
          form.setFieldValue('uri', uriText);
      }
      if (!uriText) {
          message.warning('没有可复制的 URI');
          return;
      }
      try {
          await navigator.clipboard.writeText(uriText);
          message.success('URI 已复制');
      } catch {
          message.error('复制失败');
      }
  };

  useEffect(() => {
      if (open) {
          setTestResult(null); // Reset test result
          setDbList([]);
          setRedisDbList([]);
          setMongoMembers([]);
          if (initialValues) {
              // Edit mode: Go directly to step 2
              setStep(2);
              const config: any = initialValues.config || {};
              const configType = String(config.type || 'mysql');
              const defaultPort = getDefaultPortByType(configType);
              const normalizedHosts = normalizeAddressList(config.hosts, defaultPort);
              const primaryAddress = parseHostPort(
                  normalizedHosts[0] || toAddress(config.host || 'localhost', Number(config.port || defaultPort), defaultPort),
                  defaultPort
              );
              const primaryHost = primaryAddress?.host || String(config.host || 'localhost');
              const primaryPort = primaryAddress?.port || Number(config.port || defaultPort);
              const mysqlReplicaHosts = (configType === 'mysql' || configType === 'mariadb') ? normalizedHosts.slice(1) : [];
              const mongoHosts = configType === 'mongodb' ? normalizedHosts.slice(1) : [];
              const mysqlIsReplica = String(config.topology || '').toLowerCase() === 'replica' || mysqlReplicaHosts.length > 0;
              const mongoIsReplica = String(config.topology || '').toLowerCase() === 'replica' || mongoHosts.length > 0 || !!config.replicaSet;
              form.setFieldsValue({
                  type: configType,
                  name: initialValues.name,
                  host: primaryHost,
                  port: primaryPort,
                  user: config.user,
                  password: config.password,
                  database: config.database,
                  uri: config.uri || '',
                  includeDatabases: initialValues.includeDatabases,
                  includeRedisDatabases: initialValues.includeRedisDatabases,
                  useSSH: config.useSSH,
                  sshHost: config.ssh?.host,
                  sshPort: config.ssh?.port,
                  sshUser: config.ssh?.user,
                  sshPassword: config.ssh?.password,
                  sshKeyPath: config.ssh?.keyPath,
                  driver: config.driver,
                  dsn: config.dsn,
                  timeout: config.timeout || 30,
                  mysqlTopology: mysqlIsReplica ? 'replica' : 'single',
                  mysqlReplicaHosts: mysqlReplicaHosts,
                  mysqlReplicaUser: config.mysqlReplicaUser || '',
                  mysqlReplicaPassword: config.mysqlReplicaPassword || '',
                  mongoTopology: mongoIsReplica ? 'replica' : 'single',
                  mongoHosts: mongoHosts,
                  mongoSrv: !!config.mongoSrv,
                  mongoReplicaSet: config.replicaSet || '',
                  mongoAuthSource: config.authSource || '',
                  mongoReadPreference: config.readPreference || 'primary',
                  mongoAuthMechanism: config.mongoAuthMechanism || '',
                  savePassword: config.savePassword !== false,
                  mongoReplicaUser: config.mongoReplicaUser || '',
                  mongoReplicaPassword: config.mongoReplicaPassword || ''
              });
              setUseSSH(config.useSSH || false);
              setDbType(configType);
              // 如果是 Redis 编辑模式，设置已保存的 Redis 数据库列表
              if (configType === 'redis') {
                  setRedisDbList(Array.from({ length: 16 }, (_, i) => i));
              }
          } else {
              // Create mode: Start at step 1
              setStep(1);
              form.resetFields();
              setUseSSH(false);
              setDbType('mysql');
              setActiveGroup(0);
          }
      }
  }, [open, initialValues]);

  useEffect(() => {
      return () => {
          if (testTimerRef.current !== null) {
              window.clearTimeout(testTimerRef.current);
              testTimerRef.current = null;
          }
      };
  }, []);

  const handleOk = async () => {
    try {
      const values = await form.validateFields();
      setLoading(true);

      const config = await buildConfig(values, true);
      const displayHost = String((config as any).host || values.host || '').trim();

      const isRedisType = values.type === 'redis';
      const newConn = {
        id: initialValues ? initialValues.id : Date.now().toString(),
        name: values.name || (values.type === 'sqlite' ? 'SQLite DB' : (values.type === 'redis' ? `Redis ${displayHost}` : displayHost)),
        config: config,
        includeDatabases: values.includeDatabases,
        includeRedisDatabases: isRedisType ? values.includeRedisDatabases : undefined
      };

      if (initialValues) {
          updateConnection(newConn);
          message.success('配置已更新（未连接）');
      } else {
          addConnection(newConn);
          message.success('配置已保存（未连接）');
      }

      setLoading(false);
      form.resetFields();
      setUseSSH(false);
      setDbType('mysql');
      setStep(1);
      onClose();
    } catch (e) {
      setLoading(false);
    }
  };

  const requestTest = () => {
      if (loading) return;
      if (testTimerRef.current !== null) return;
      testTimerRef.current = window.setTimeout(() => {
          testTimerRef.current = null;
          handleTest();
      }, 0);
  };

  const handleTest = async () => {
      if (testInFlightRef.current) return;
      testInFlightRef.current = true;
      try {
          const values = await form.validateFields();
          setLoading(true);
          setTestResult(null);
          const config = await buildConfig(values, false);

          // Use different API for Redis
          const isRedisType = values.type === 'redis';
          const res = isRedisType
              ? await RedisConnect(config as any)
              : await TestConnection(config as any);

          if (res.success) {
              setTestResult({ type: 'success', message: res.message });
              if (isRedisType) {
                  // Redis: generate database list 0-15
                  setRedisDbList(Array.from({ length: 16 }, (_, i) => i));
              } else {
                  // Other databases: fetch database list
                  const dbRes = await DBGetDatabases(config as any);
                  if (dbRes.success) {
                      const dbs = (dbRes.data as any[]).map((row: any) => row.Database || row.database);
                      setDbList(dbs);
                  }
              }
          } else {
              setTestResult({ type: 'error', message: "测试失败: " + res.message });
          }
      } catch (e) {
          // ignore
      } finally {
          testInFlightRef.current = false;
          setLoading(false);
      }
  };

  const handleDiscoverMongoMembers = async () => {
      if (discoveringMembers || dbType !== 'mongodb') {
          return;
      }
      try {
          const values = await form.validateFields();
          setDiscoveringMembers(true);
          const config = await buildConfig(values, false);
          const result = await MongoDiscoverMembers(config as any);
          if (!result.success) {
              message.error(result.message || '成员发现失败');
              return;
          }
          const data = (result.data as Record<string, any>) || {};
          const membersRaw = Array.isArray(data.members) ? data.members : [];
          const members: MongoMemberInfo[] = membersRaw
              .map((item: any) => ({
                  host: String(item.host || '').trim(),
                  role: String(item.role || item.state || 'UNKNOWN').trim(),
                  state: String(item.state || item.role || 'UNKNOWN').trim(),
                  stateCode: Number(item.stateCode || 0),
                  healthy: !!item.healthy,
                  isSelf: !!item.isSelf,
              }))
              .filter((item: MongoMemberInfo) => !!item.host);
          setMongoMembers(members);
          if (!form.getFieldValue('mongoReplicaSet') && data.replicaSet) {
              form.setFieldValue('mongoReplicaSet', String(data.replicaSet));
          }
          message.success(result.message || `发现 ${members.length} 个成员`);
      } catch (error: any) {
          message.error(error?.message || '成员发现失败');
      } finally {
          setDiscoveringMembers(false);
      }
  };

  const buildConfig = async (values: any, forPersist: boolean) => {
      const mergedValues = { ...values };
      const parsedUriValues = parseUriToValues(mergedValues.uri, mergedValues.type);
      const isEmptyField = (value: unknown) => (
          value === undefined
          || value === null
          || value === ''
          || value === 0
          || (Array.isArray(value) && value.length === 0)
      );
      if (parsedUriValues) {
          Object.entries(parsedUriValues).forEach(([key, value]) => {
              if (isEmptyField((mergedValues as any)[key])) {
                  (mergedValues as any)[key] = value;
              }
          });
      }

      const type = String(mergedValues.type || '').toLowerCase();
      const defaultPort = getDefaultPortByType(type);
      const parsedPrimary = parseHostPort(
          toAddress(mergedValues.host || 'localhost', Number(mergedValues.port || defaultPort), defaultPort),
          defaultPort
      );
      const primaryHost = parsedPrimary?.host || 'localhost';
      const primaryPort = parsedPrimary?.port || defaultPort;

      let hosts: string[] = [];
      let topology: 'single' | 'replica' | undefined;
      let replicaSet = '';
      let authSource = '';
      let readPreference = '';
      let mysqlReplicaUser = '';
      let mysqlReplicaPassword = '';
      let mongoSrvEnabled = false;
      let mongoAuthMechanism = '';
      let mongoReplicaUser = '';
      let mongoReplicaPassword = '';
      const savePassword = type === 'mongodb'
          ? mergedValues.savePassword !== false
          : true;

      if (type === 'mysql' || type === 'mariadb') {
          const replicas = mergedValues.mysqlTopology === 'replica'
              ? normalizeAddressList(mergedValues.mysqlReplicaHosts, defaultPort)
              : [];
          const allHosts = normalizeAddressList([`${primaryHost}:${primaryPort}`, ...replicas], defaultPort);
          if (mergedValues.mysqlTopology === 'replica' || allHosts.length > 1) {
              hosts = allHosts;
              topology = 'replica';
              mysqlReplicaUser = String(mergedValues.mysqlReplicaUser || '').trim();
              mysqlReplicaPassword = String(mergedValues.mysqlReplicaPassword || '');
          } else {
              topology = 'single';
          }
      }

      if (type === 'mongodb') {
          mongoSrvEnabled = !!mergedValues.mongoSrv;
          const extraHosts = mergedValues.mongoTopology === 'replica'
              ? (mongoSrvEnabled
                  ? normalizeMongoSrvHostList(mergedValues.mongoHosts, defaultPort)
                  : normalizeAddressList(mergedValues.mongoHosts, defaultPort))
              : [];
          const primarySeed = mongoSrvEnabled ? primaryHost : `${primaryHost}:${primaryPort}`;
          const allHosts = mongoSrvEnabled
              ? normalizeMongoSrvHostList([primarySeed, ...extraHosts], defaultPort)
              : normalizeAddressList([primarySeed, ...extraHosts], defaultPort);
          if (mergedValues.mongoTopology === 'replica' || allHosts.length > 1 || mergedValues.mongoReplicaSet) {
              hosts = allHosts;
              topology = 'replica';
              mongoReplicaUser = String(mergedValues.mongoReplicaUser || '').trim();
              mongoReplicaPassword = String(mergedValues.mongoReplicaPassword || '');
          } else {
              topology = 'single';
          }
          replicaSet = String(mergedValues.mongoReplicaSet || '').trim();
          authSource = String(mergedValues.mongoAuthSource || mergedValues.database || 'admin').trim();
          readPreference = String(mergedValues.mongoReadPreference || 'primary').trim();
          mongoAuthMechanism = String(mergedValues.mongoAuthMechanism || '').trim().toUpperCase();
      }

      const sshConfig = mergedValues.useSSH ? {
          host: mergedValues.sshHost,
          port: Number(mergedValues.sshPort),
          user: mergedValues.sshUser,
          password: mergedValues.sshPassword || "",
          keyPath: mergedValues.sshKeyPath || ""
      } : { host: "", port: 22, user: "", password: "", keyPath: "" };

      const keepPassword = !forPersist || savePassword;

      return {
          type: mergedValues.type,
          host: primaryHost,
          port: Number(primaryPort || 0),
          user: mergedValues.user || "",
          password: keepPassword ? (mergedValues.password || "") : "",
          savePassword: savePassword,
          database: mergedValues.database || "",
          useSSH: !!mergedValues.useSSH,
          ssh: sshConfig,
          driver: mergedValues.driver,
          dsn: mergedValues.dsn,
          timeout: Number(mergedValues.timeout || 30),
          uri: String(mergedValues.uri || '').trim(),
          hosts: hosts,
          topology: topology,
          mysqlReplicaUser: mysqlReplicaUser,
          mysqlReplicaPassword: keepPassword ? mysqlReplicaPassword : "",
          replicaSet: replicaSet,
          authSource: authSource,
          readPreference: readPreference,
          mongoSrv: mongoSrvEnabled,
          mongoAuthMechanism: mongoAuthMechanism,
          mongoReplicaUser: mongoReplicaUser,
          mongoReplicaPassword: keepPassword ? mongoReplicaPassword : "",
      };
  };

  const handleTypeSelect = (type: string) => {
      setDbType(type);
      form.setFieldsValue({ type: type });

      const defaultPort = getDefaultPortByType(type);
      if (type !== 'sqlite' && type !== 'custom') {
          form.setFieldsValue({
              port: defaultPort,
              mysqlTopology: 'single',
              mongoTopology: 'single',
              mongoSrv: false,
              mongoReadPreference: 'primary',
              mongoReplicaSet: '',
              mongoAuthSource: '',
              mongoAuthMechanism: '',
              savePassword: true,
              mysqlReplicaHosts: [],
              mongoHosts: [],
              mysqlReplicaUser: '',
              mysqlReplicaPassword: '',
              mongoReplicaUser: '',
              mongoReplicaPassword: '',
          });
      }

      setMongoMembers([]);
      setStep(2);
  };

  const isSqlite = dbType === 'sqlite';
  const isCustom = dbType === 'custom';
  const isRedis = dbType === 'redis';

  const dbTypeGroups = [
      { label: '关系型数据库', items: [
          { key: 'mysql', name: 'MySQL', icon: <ConsoleSqlOutlined style={{ fontSize: 24, color: '#00758F' }} /> },
          { key: 'mariadb', name: 'MariaDB', icon: <ConsoleSqlOutlined style={{ fontSize: 24, color: '#003545' }} /> },
          { key: 'postgres', name: 'PostgreSQL', icon: <DatabaseOutlined style={{ fontSize: 24, color: '#336791' }} /> },
          { key: 'sqlserver', name: 'SQL Server', icon: <DatabaseOutlined style={{ fontSize: 24, color: '#CC2927' }} /> },
          { key: 'sqlite', name: 'SQLite', icon: <FileTextOutlined style={{ fontSize: 24, color: '#003B57' }} /> },
          { key: 'oracle', name: 'Oracle', icon: <DatabaseOutlined style={{ fontSize: 24, color: '#F80000' }} /> },
      ]},
      { label: '国产数据库', items: [
          { key: 'dameng', name: 'Dameng (达梦)', icon: <CloudServerOutlined style={{ fontSize: 24, color: '#1890ff' }} /> },
          { key: 'kingbase', name: 'Kingbase (人大金仓)', icon: <DatabaseOutlined style={{ fontSize: 24, color: '#faad14' }} /> },
          { key: 'highgo', name: 'HighGo (瀚高)', icon: <DatabaseOutlined style={{ fontSize: 24, color: '#00a854' }} /> },
          { key: 'vastbase', name: 'Vastbase (海量)', icon: <DatabaseOutlined style={{ fontSize: 24, color: '#1a6dff' }} /> },
      ]},
      { label: 'NoSQL', items: [
          { key: 'mongodb', name: 'MongoDB', icon: <CloudServerOutlined style={{ fontSize: 24, color: '#47A248' }} /> },
          { key: 'redis', name: 'Redis', icon: <CloudOutlined style={{ fontSize: 24, color: '#DC382D' }} /> },
      ]},
      { label: '时序数据库', items: [
          { key: 'tdengine', name: 'TDengine', icon: <DatabaseOutlined style={{ fontSize: 24, color: '#2F54EB' }} /> },
      ]},
      { label: '其他', items: [
          { key: 'custom', name: 'Custom (自定义)', icon: <AppstoreAddOutlined style={{ fontSize: 24, color: '#595959' }} /> },
      ]},
  ];

  const dbTypes = dbTypeGroups.flatMap(g => g.items);

  const renderStep1 = () => (
      <div style={{ display: 'flex', height: 360 }}>
          {/* 左侧分类导航 */}
          <div style={{ width: 120, borderRight: '1px solid #f0f0f0', paddingRight: 8, flexShrink: 0 }}>
              {dbTypeGroups.map((group, idx) => (
                  <div
                      key={group.label}
                      onClick={() => setActiveGroup(idx)}
                      style={{
                          padding: '10px 12px',
                          cursor: 'pointer',
                          borderRadius: 6,
                          marginBottom: 4,
                          background: activeGroup === idx ? '#e6f4ff' : 'transparent',
                          color: activeGroup === idx ? '#1677ff' : undefined,
                          fontWeight: activeGroup === idx ? 500 : 400,
                          transition: 'all 0.2s',
                          fontSize: 13,
                      }}
                  >
                      {group.label}
                  </div>
              ))}
          </div>
          {/* 右侧数据源卡片 */}
          <div style={{ flex: 1, paddingLeft: 16, overflowY: 'auto', overflowX: 'hidden' }}>
              <Row gutter={[12, 12]}>
                  {dbTypeGroups[activeGroup]?.items.map(item => (
                      <Col span={8} key={item.key}>
                          <Card
                              hoverable
                              onClick={() => handleTypeSelect(item.key)}
                              style={{ textAlign: 'center', cursor: 'pointer', height: 100 }}
                              styles={{ body: { padding: '16px 8px', display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', height: '100%' } }}
                          >
                              <div style={{ marginBottom: 8 }}>{item.icon}</div>
                              <Text strong style={{ fontSize: 12, whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis', maxWidth: '100%' }}>{item.name}</Text>
                          </Card>
                      </Col>
                  ))}
              </Row>
          </div>
      </div>
  );

  const renderStep2 = () => (
      <Form 
        form={form} 
        layout="vertical" 
        initialValues={{
            type: 'mysql',
            host: 'localhost',
            port: 3306,
            user: 'root',
            useSSH: false,
            sshPort: 22,
            timeout: 30,
            uri: '',
            mysqlTopology: 'single',
            mongoTopology: 'single',
            mongoSrv: false,
            mongoReadPreference: 'primary',
            mongoAuthMechanism: '',
            savePassword: true,
            mysqlReplicaHosts: [],
            mongoHosts: [],
            mysqlReplicaUser: '',
            mysqlReplicaPassword: '',
            mongoReplicaUser: '',
            mongoReplicaPassword: '',
        }}
        onValuesChange={(changed) => {
            if (testResult) setTestResult(null); // Clear result on change
            if (changed.useSSH !== undefined) setUseSSH(changed.useSSH);
            // Type change handled by step 1, but keep sync if select changes (hidden now)
            if (changed.type !== undefined) setDbType(changed.type);
            if (
                changed.type !== undefined
                || changed.host !== undefined
                || changed.port !== undefined
                || changed.mongoHosts !== undefined
                || changed.mongoTopology !== undefined
                || changed.mongoSrv !== undefined
            ) {
                setMongoMembers([]);
            }
        }}
      >
        {/* Hidden Type Field to keep form value synced */}
        <Form.Item name="type" hidden><Input /></Form.Item>

        <Form.Item name="name" label="连接名称">
            <Input placeholder="例如：本地测试库" />
        </Form.Item>
        <Form.Item
            name="uri"
            label="连接 URI（可复制粘贴）"
            help="支持从参数生成、复制到剪贴板，或粘贴后一键解析回填参数"
        >
            <Input.TextArea rows={2} placeholder={getUriPlaceholder()} />
        </Form.Item>
        <Space size={8} style={{ marginBottom: 12 }}>
            <Button onClick={handleGenerateURI}>生成 URI</Button>
            <Button onClick={handleParseURI}>从 URI 解析</Button>
            <Button onClick={handleCopyURI}>复制 URI</Button>
        </Space>
        
        {isCustom ? (
            <>
                <Form.Item name="driver" label="驱动名称 (Driver Name)" rules={[{ required: true, message: '请输入驱动名称' }]} help="已支持: mysql, postgres, sqlite, oracle, dm, kingbase">
                    <Input placeholder="例如: mysql, postgres" />
                </Form.Item>
                <Form.Item name="dsn" label="连接字符串 (DSN)" rules={[{ required: true, message: '请输入连接字符串' }]}>
                    <Input.TextArea rows={3} placeholder="例如: user:pass@tcp(localhost:3306)/dbname?charset=utf8" />
                </Form.Item>
            </>
        ) : (
        <>
        <div style={{ display: 'flex', gap: 16 }}>
            <Form.Item
                name="host"
                label={isSqlite ? "文件路径 (绝对路径)" : "主机地址 (Host)"}
                rules={[createUriAwareRequiredRule('请输入地址/路径')]}
                style={{ flex: 1 }}
            >
              <Input
                placeholder={isSqlite ? "/path/to/db.sqlite" : "localhost"}
                onDoubleClick={requestTest}
              />
            </Form.Item>
            {!isSqlite && (
            <Form.Item
                name="port"
                label="端口 (Port)"
                rules={[createUriAwareRequiredRule('请输入端口号', (value) => Number(value) > 0)]}
                style={{ width: 100 }}
            >
              <InputNumber style={{ width: '100%' }} />
            </Form.Item>
            )}
        </div>

        {(dbType === 'mysql' || dbType === 'mariadb') && (
        <>
            <Form.Item name="mysqlTopology" label="连接模式">
                <Select
                    options={[
                        { value: 'single', label: '单机模式' },
                        { value: 'replica', label: '主从模式（优先主库，可切换从库）' },
                    ]}
                />
            </Form.Item>
            {mysqlTopology === 'replica' && (
            <>
                <Form.Item
                    name="mysqlReplicaHosts"
                    label="从库地址列表"
                    help="可输入多个从库地址，格式：host:port（回车确认）"
                >
                    <Select mode="tags" placeholder="例如：10.10.0.12:3306、10.10.0.13:3306" tokenSeparators={[',', ';', ' ']} />
                </Form.Item>
                <div style={{ display: 'flex', gap: 16 }}>
                    <Form.Item name="mysqlReplicaUser" label="从库用户名（可选）" style={{ flex: 1 }}>
                        <Input placeholder="留空沿用主库用户名" />
                    </Form.Item>
                    <Form.Item name="mysqlReplicaPassword" label="从库密码（可选）" style={{ flex: 1 }}>
                        <Input.Password placeholder="留空沿用主库密码" />
                    </Form.Item>
                </div>
            </>
            )}
        </>
        )}

        {dbType === 'mongodb' && (
        <>
            <Form.Item name="mongoSrv" valuePropName="checked" style={{ marginBottom: 12 }}>
                <Checkbox>使用 SRV 记录（mongodb+srv）</Checkbox>
            </Form.Item>
            <Form.Item name="mongoTopology" label="连接模式">
                <Select
                    options={[
                        { value: 'single', label: '单机模式' },
                        { value: 'replica', label: '主从/副本集模式' },
                    ]}
                />
            </Form.Item>
            {mongoSrv && useSSH && (
                <Alert
                    type="warning"
                    showIcon
                    style={{ marginBottom: 12 }}
                    message="SRV 记录模式暂不支持 SSH 隧道，请关闭其中一项后再测试连接"
                />
            )}
            {mongoTopology === 'replica' && (
            <>
                {!mongoSrv && (
                <Form.Item
                    name="mongoHosts"
                    label="附加节点地址"
                    help="主节点使用上方主机地址；这里填写其余节点，格式：host:port"
                >
                    <Select mode="tags" placeholder="例如：10.10.0.22:27017、10.10.0.23:27017" tokenSeparators={[',', ';', ' ']} />
                </Form.Item>
                )}
                {mongoSrv && (
                <Alert
                    type="info"
                    showIcon
                    style={{ marginBottom: 12 }}
                    message="SRV 模式将通过 DNS 自动发现成员，无需手动填写附加节点地址"
                />
                )}
                <Form.Item name="mongoReplicaSet" label="Replica Set 名称">
                    <Input placeholder="例如：rs0" />
                </Form.Item>
                <div style={{ display: 'flex', gap: 16 }}>
                    <Form.Item name="mongoReplicaUser" label="从库用户名（可选）" style={{ flex: 1 }}>
                        <Input placeholder="留空沿用主库用户名" />
                    </Form.Item>
                    <Form.Item name="mongoReplicaPassword" label="从库密码（可选）" style={{ flex: 1 }}>
                        <Input.Password placeholder="留空沿用主库密码" />
                    </Form.Item>
                </div>
                <Space size={8} style={{ marginBottom: 12 }}>
                    <Button onClick={handleDiscoverMongoMembers} loading={discoveringMembers}>发现成员</Button>
                    <Text type="secondary">发现后可校验当前副本集状态</Text>
                </Space>
                {mongoMembers.length > 0 && (
                    <Table
                        size="small"
                        pagination={false}
                        rowKey={(record) => `${record.host}-${record.state}`}
                        dataSource={mongoMembers}
                        style={{ marginBottom: 12 }}
                        columns={[
                            {
                                title: '成员',
                                dataIndex: 'host',
                                width: '48%',
                                render: (value: string, record: MongoMemberInfo) => (
                                    <span>
                                        {value}
                                        {record.isSelf ? <Tag color="processing" style={{ marginLeft: 8 }}>当前</Tag> : null}
                                    </span>
                                ),
                            },
                            {
                                title: '状态',
                                dataIndex: 'state',
                                width: '32%',
                                render: (value: string) => {
                                    const state = String(value || '').toUpperCase();
                                    let color: string = 'default';
                                    if (state === 'PRIMARY') color = 'success';
                                    else if (state === 'SECONDARY' || state === 'PASSIVE') color = 'blue';
                                    else if (state === 'ARBITER') color = 'purple';
                                    else if (state === 'DOWN' || state === 'REMOVED' || state === 'UNKNOWN') color = 'error';
                                    return <Tag color={color}>{state || 'UNKNOWN'}</Tag>;
                                },
                            },
                            {
                                title: '健康',
                                dataIndex: 'healthy',
                                width: '20%',
                                render: (value: boolean) => (
                                    <Tag color={value ? 'success' : 'error'}>{value ? '正常' : '异常'}</Tag>
                                ),
                            },
                        ]}
                    />
                )}
            </>
            )}
            <Form.Item name="mongoAuthSource" label="认证库 (authSource)">
                <Input placeholder="默认使用 database 或 admin" />
            </Form.Item>
            <Form.Item name="mongoAuthMechanism" label="验证方式 (authMechanism)">
                <Select
                    allowClear
                    placeholder="默认由 MongoDB 驱动协商"
                    options={[
                        { value: 'SCRAM-SHA-1', label: 'SCRAM-SHA-1' },
                        { value: 'SCRAM-SHA-256', label: 'SCRAM-SHA-256' },
                        { value: 'MONGODB-AWS', label: 'MONGODB-AWS' },
                    ]}
                />
            </Form.Item>
            <Form.Item name="mongoReadPreference" label="读偏好 (readPreference)">
                <Select
                    options={[
                        { value: 'primary', label: 'primary' },
                        { value: 'primaryPreferred', label: 'primaryPreferred' },
                        { value: 'secondary', label: 'secondary' },
                        { value: 'secondaryPreferred', label: 'secondaryPreferred' },
                        { value: 'nearest', label: 'nearest' },
                    ]}
                />
            </Form.Item>
        </>
        )}

        {/* Redis specific: password only, no username */}
        {isRedis && (
        <>
            <Form.Item name="password" label="密码 (可选)">
              <Input.Password placeholder="Redis 密码（如果设置了 requirepass）" />
            </Form.Item>
            <Form.Item name="includeRedisDatabases" label="显示数据库 (留空显示全部)" help="连接测试成功后可选择">
                <Select mode="multiple" placeholder="选择显示的数据库 (0-15)" allowClear>
                    {redisDbList.map(db => <Select.Option key={db} value={db}>db{db}</Select.Option>)}
                </Select>
            </Form.Item>
        </>
        )}

        {/* Non-Redis, non-SQLite: username and password */}
        {!isSqlite && !isRedis && (
        <div style={{ display: 'flex', gap: 16 }}>
            <Form.Item
                name="user"
                label="用户名"
                rules={[createUriAwareRequiredRule('请输入用户名')]}
                style={{ flex: 1 }}
            >
              <Input />
            </Form.Item>
            <Form.Item name="password" label="密码" style={{ flex: 1 }}>
              <Input.Password />
            </Form.Item>
        </div>
        )}

        {dbType === 'mongodb' && (
        <Form.Item name="savePassword" valuePropName="checked" style={{ marginTop: -6 }}>
            <Checkbox>保存密码</Checkbox>
        </Form.Item>
        )}

        {!isSqlite && !isRedis && (
        <Form.Item name="includeDatabases" label="显示数据库 (留空显示全部)" help="连接测试成功后可选择">
            <Select mode="multiple" placeholder="选择显示的数据库" allowClear>
                {dbList.map(db => <Select.Option key={db} value={db}>{db}</Select.Option>)}
            </Select>
        </Form.Item>
        )}

        {!isSqlite && (
        <>
            <Divider style={{ margin: '12px 0' }} />
            <Form.Item name="useSSH" valuePropName="checked" style={{ marginBottom: 0 }}>
                <Checkbox>使用 SSH 隧道 (SSH Tunnel)</Checkbox>
            </Form.Item>

            {useSSH && (
                <div style={{ padding: '12px', background: '#f5f5f5', borderRadius: 6, marginTop: 12 }}>
                    <div style={{ display: 'flex', gap: 16 }}>
                        <Form.Item name="sshHost" label="SSH 主机 (域名或IP)" rules={[{ required: useSSH, message: '请输入SSH主机' }]} style={{ flex: 1 }}>
                            <Input placeholder="例如: ssh.example.com 或 192.168.1.100" />
                        </Form.Item>
                        <Form.Item name="sshPort" label="端口" rules={[{ required: useSSH, message: '请输入SSH端口' }]} style={{ width: 100 }}>
                            <InputNumber style={{ width: '100%' }} />
                        </Form.Item>
                    </div>
                    <div style={{ display: 'flex', gap: 16 }}>
                        <Form.Item name="sshUser" label="SSH 用户" rules={[{ required: useSSH, message: '请输入SSH用户' }]} style={{ flex: 1 }}>
                            <Input placeholder="root" />
                        </Form.Item>
                        <Form.Item name="sshPassword" label="SSH 密码" style={{ flex: 1 }}>
                            <Input.Password placeholder="密码" />
                        </Form.Item>
                    </div>
                     <Form.Item name="sshKeyPath" label="私钥路径 (可选)" help="例如: /Users/name/.ssh/id_rsa">
                        <Input placeholder="绝对路径" />
                    </Form.Item>
                </div>
            )}

            <Divider style={{ margin: '12px 0' }} />
            
            <Collapse 
                ghost 
                items={[{
                    key: 'advanced',
                    label: '高级连接',
                    children: (
                        <Form.Item 
                            name="timeout" 
                            label="连接超时 (秒)" 
                            help="数据库连接超时时间，默认 30 秒"
                            rules={[{ type: 'number', min: 1, max: 300, message: '超时时间范围: 1-300 秒' }]}
                        >
                            <InputNumber style={{ width: '100%' }} min={1} max={300} placeholder="30" />
                        </Form.Item>
                    )
                }]}
            />
        </>
        )}
        </>
        )}
        
        {testResult && (
          <Alert
              message={testResult.message}
              type={testResult.type}
              showIcon
              style={{ marginTop: 16 }}
          />
        )}
      </Form>
  );

  const getFooter = () => {
      if (step === 1) {
          return [
             <Button key="cancel" onClick={onClose}>取消</Button>
          ];
      }
      return [
          !initialValues && <Button key="back" onClick={() => setStep(1)} style={{ float: 'left' }}>上一步</Button>,
          <Button key="test" loading={loading} onClick={requestTest}>测试连接</Button>,
          <Button key="cancel" onClick={onClose}>取消</Button>,
          <Button key="submit" type="primary" loading={loading} onClick={handleOk}>保存</Button>
      ];
  };

  const getTitle = () => {
      if (step === 1) return "选择数据源类型";
      const typeName = dbTypes.find(t => t.key === dbType)?.name || dbType;
      return initialValues ? "编辑连接" : `新建 ${typeName} 连接`;
  };

  const modalBodyStyle = step === 1
      ? { padding: '16px 24px', overflow: 'hidden' as const }
      : {
          padding: '16px 24px',
          maxHeight: 'calc(100vh - 220px)',
          overflowY: 'auto' as const,
          overflowX: 'hidden' as const,
      };

  return (
    <Modal
        title={getTitle()}
        open={open}
        onCancel={onClose}
        footer={getFooter()}
        wrapClassName="connection-modal-wrap"
        width={step === 1 ? 650 : 600}
        zIndex={10001}
        destroyOnHidden
        maskClosable={false}
        styles={{ body: modalBodyStyle }}
    >
      {step === 1 ? renderStep1() : renderStep2()}
    </Modal>
  );
};

export default ConnectionModal;
