import { useState, useEffect, useRef, useMemo } from 'react';
import api from './lib/api.js';
import ConfirmDialog from './components/ConfirmDialog.jsx';
import ConfirmModal from './components/ConfirmModal.jsx';
import FormModal from './components/FormModal.jsx';
import FormField from './components/FormField.jsx';
import { useNotify } from './components/NotifyProvider.jsx';
import { 
  IconPlus, 
  IconEdit, 
  IconTrash, 
  IconDatabase,
  IconDownload,
  IconServer,
  IconFilter,
  IconList,
  IconLayoutGrid,
  IconGitBranch,
  IconSquare,
  IconSquareCheck,
  IconChevronRight
} from '@tabler/icons-react';
import GraphView from './GraphView';
import { 
  normalizeGateways, 
  needsGateways as NEEDS_GATEWAYS,
  SERVER_TYPE_OPTIONS 
} from './utils/serverUtils.js';
import { 
  ServerModal,
  LinkGeneratorModal,
  ServerSidebar,
  ServerCard,
  ServerGroupCard,
  ServerBulkActions
} from './components/server/index.js';
import PageHeader from './components/PageHeader.jsx';
import PageHeaderActions from './components/PageHeaderActions.jsx';

const API_URL = '/api';

// Преобразование кода страны в emoji-флаг
function countryToFlag(isoCode) {
  if (!isoCode) return '';
  return isoCode
    .toUpperCase()
    .replace(/./g, char =>
      String.fromCodePoint(127397 + char.charCodeAt())
    );
}

function ServerManager() {
  const notify = useNotify();
  
  // === Основные данные ===
  const [servers, setServers] = useState([]);
  const [connections, setConnections] = useState([]);
  const [networkConfigGateways, setNetworkConfigGateways] = useState([]);
  const [loading, setLoading] = useState(false);

  // === UI State ===
  const [activeTab, setActiveTab] = useState('servers'); // 'servers' | 'connections'
  const [viewMode, setViewMode] = useState('cards'); // 'table' | 'cards'

  // === Фильтры и поиск ===
  const [filters, setFilters] = useState({
    country: '',
    provider: '',
    type: '',
    tunnel: ''
  });
  const [searchTerm, setSearchTerm] = useState('');

  // === Сортировка и пагинация ===
  const [sortField, setSortField] = useState('ip');
  const [sortOrder, setSortOrder] = useState('asc');
  const [currentPage, setCurrentPage] = useState(1);
  const pageSize = 20;

  // === Выбор (bulk selection) ===
  const [selectedServers, setSelectedServers] = useState(new Set());

  // === Модальные окна ===
  const [addServerModalOpen, setAddServerModalOpen] = useState(false);
  const [editModalOpen, setEditModalOpen] = useState(false);
  const [editModalServer, setEditModalServer] = useState(null);
  const [showDeleteModal, setShowDeleteModal] = useState(false);
  const [serverToDelete, setServerToDelete] = useState(null);
  const [linkGeneratorOpen, setLinkGeneratorOpen] = useState(false);
  const [linkGeneratorServer, setLinkGeneratorServer] = useState(null);
  const [graphConnectionModalOpen, setGraphConnectionModalOpen] = useState(false);
  const [confirmState, setConfirmState] = useState({ open: false, text: '', onConfirm: null });

  // === Ошибки форм ===
  const [modalError, setModalError] = useState('');

  // === URL Settings ===
  const [urlSettings, setUrlSettings] = useState({
    baseUrl: 'https://functions.yandexcloud.net/d4eno3im0qgsr4tj5hdo',
    cloudflare_gateway: 'SWE-IHOR',
    bunny_gateway: 'SWE-IHOR',
    fastly_gateway: 'SWE-IHOR',
    telegram_gateway: '94.142.140.1',
    hetzner_gateway: '94.142.140.1',
    type: 'routes',
    version: 'v4.rsc'
  });

  // === Связи ===
  const [newConnection, setNewConnection] = useState({ from: '', to: '', tunnelType: 'GRE', ipA: '', ipB: '' });

  // === Фильтры графа ===
  const [graphTunnelFilters, setGraphTunnelFilters] = useState({ GRE: true, IPSec: true, WireGuard: true, OpenVPN: true });
  const [graphCountryFilter, setGraphCountryFilter] = useState('');
  const [graphProviderFilter, setGraphProviderFilter] = useState('');

  const presets = {
    default: { baseUrl: 'https://functions.yandexcloud.net/d4eno3im0qgsr4tj5hdo', cloudflare_gateway: 'SWE-IHOR', bunny_gateway: 'SWE-IHOR', fastly_gateway: 'SWE-IHOR', telegram_gateway: '94.142.140.1', hetzner_gateway: '94.142.140.1', type: 'routes', version: 'v4.rsc' },
    cloudflareSWE: { baseUrl: 'https://cf.example/api', cloudflare_gateway: 'SWE-IHOR', bunny_gateway: '', fastly_gateway: '', telegram_gateway: '', hetzner_gateway: '', type: 'routes', version: 'v4.rsc' },
    hetznerDE: { baseUrl: 'https://hetzner.example/api', cloudflare_gateway: '', bunny_gateway: '', fastly_gateway: '', telegram_gateway: '', hetzner_gateway: '94.142.140.1', type: 'routes', version: 'v4.rsc' }
  };

  // === Helpers ===
  const normalizeServerType = (type) => {
    const value = String(type || '').toLowerCase();
    return SERVER_TYPE_OPTIONS.some((opt) => opt.value === value) ? value : 'jumphost';
  };

  // === Загрузка данных ===
  const fetchNetworkConfig = async (abortSignal) => {
    const opts = abortSignal ? { signal: abortSignal } : {};
    try {
      const res = await api.get('/network-config', opts);
      const config = res.data || {};
      setNetworkConfigGateways(Array.isArray(config.gateways) ? config.gateways : []);
    } catch (e) {
      if (e?.name === 'CanceledError' || e?.name === 'AbortError' || e?.code === 'ERR_CANCELED') return;
      console.warn('Network config not loaded:', e?.message || e);
      setNetworkConfigGateways([]);
    }
  };

  useEffect(() => {
    const controller = new AbortController();
    const signal = controller.signal;
    fetchServers(signal);
    fetchConnections(signal);
    fetchNetworkConfig(signal);
    return () => controller.abort();
  }, []);

  useEffect(() => {
    const savedSettings = localStorage.getItem('urlSettings');
    if (savedSettings) {
      try {
        const parsed = JSON.parse(savedSettings);
        setUrlSettings(prev => ({ ...prev, ...parsed }));
      } catch (e) {
        console.error('Ошибка при загрузке настроек URL:', e);
      }
    }
  }, []);

  const fetchServers = async (abortSignal) => {
    setLoading(true);
    const opts = abortSignal ? { signal: abortSignal } : {};
    try {
      const response = await api.get(`/servers`, opts);
      const normalized = (Array.isArray(response.data) ? response.data : []).map((srv) => ({
        ...srv,
        type: normalizeServerType(srv.type),
        gateways: NEEDS_GATEWAYS(srv.type) ? normalizeGateways(srv.gateways, srv.gateway || srv.dns || srv.ip) : (Array.isArray(srv.gateways) ? srv.gateways : []),
      }));
      setServers(normalized);
    } catch (error) {
      if (error?.name === 'CanceledError' || error?.name === 'AbortError' || error?.code === 'ERR_CANCELED') return;
      console.error('Error fetching servers:', error);
      notify.error('Не удалось загрузить серверы. Проверьте, запущен ли бэкенд.');
    } finally {
      setLoading(false);
    }
  };

  const fetchConnections = async (abortSignal) => {
    const opts = abortSignal ? { signal: abortSignal } : {};
    try {
      const response = await api.get(`/server-connections`, opts);
      if (Array.isArray(response.data)) {
        setConnections(response.data);
      } else {
        setConnections([]);
      }
    } catch (error) {
      if (error?.name === 'CanceledError' || error?.name === 'AbortError' || error?.code === 'ERR_CANCELED') return;
      console.error('Error fetching server connections:', error);
    }
  };

  // Шлюзы для карточки: из /network-config по serverId, иначе из server.gateways
  const getGatewaysForServer = useMemo(() => {
    const gatewaysByServerId = {};
    (networkConfigGateways || []).forEach((gw) => {
      if (!gw.serverId) return;
      if (!gatewaysByServerId[gw.serverId]) gatewaysByServerId[gw.serverId] = [];
      gatewaysByServerId[gw.serverId].push(gw);
    });
    return (server) => {
      const ids = [server?.id, server?.ip, server?.dns].filter(Boolean);
      for (const id of ids) {
        const gws = gatewaysByServerId[id];
        if (gws?.length > 0) {
          return gws.map((gw, idx) => ({
            id: gw.id,
            name: gw.description || gw.id || gw.ip,
            ip: gw.ip,
            comment: gw.description,
            primary: idx === 0,
          }));
        }
      }
      return server?.gateways || [];
    };
  }, [networkConfigGateways]);

  // === Фильтрация и сортировка ===
  const filteredServers = useMemo(() => {
    let result = [...servers];

    // Применяем фильтры
    if (filters.country) {
      result = result.filter(s => s.country === filters.country);
    }
    if (filters.provider) {
      result = result.filter(s => s.provider === filters.provider);
    }
    if (filters.type) {
      result = result.filter(s => s.type === filters.type);
    }
    if (filters.tunnel) {
      result = result.filter(s => s.tunnel === filters.tunnel);
    }

    // Поиск
    if (searchTerm) {
      const term = searchTerm.toLowerCase();
      result = result.filter(s =>
        s.ip?.toLowerCase().includes(term) ||
        s.dns?.toLowerCase().includes(term) ||
        s.country?.toLowerCase().includes(term) ||
        s.provider?.toLowerCase().includes(term) ||
        (s.type || '').toLowerCase().includes(term)
      );
    }

    // Сортировка
    result.sort((a, b) => {
      let valA = a[sortField] || '';
      let valB = b[sortField] || '';
      if (typeof valA === 'string') valA = valA.toLowerCase();
      if (typeof valB === 'string') valB = valB.toLowerCase();
      if (valA < valB) return sortOrder === 'asc' ? -1 : 1;
      if (valA > valB) return sortOrder === 'asc' ? 1 : -1;
      return 0;
    });

    return result;
  }, [servers, filters, searchTerm, sortField, sortOrder]);

  // Элементы для отображения: серверы с одинаковым groupKey (и type home) объединяются в одну карточку
  const displayItems = useMemo(() => {
    const keyToServers = {};
    filteredServers.forEach((s) => {
      const k = (s.groupKey && String(s.groupKey).trim()) || null;
      if (k) {
        if (!keyToServers[k]) keyToServers[k] = [];
        keyToServers[k].push(s);
      }
    });
    const seenKeys = new Set();
    const items = [];
    for (const s of filteredServers) {
      const k = (s.groupKey && String(s.groupKey).trim()) || null;
      if (!k) {
        items.push({ type: 'server', server: s });
      } else {
        if (!seenKeys.has(k)) {
          seenKeys.add(k);
          const group = keyToServers[k];
          items.push(group.length >= 2 ? { type: 'group', groupKey: k, servers: group } : { type: 'server', server: group[0] });
        }
      }
    }
    return items;
  }, [filteredServers]);

  // === Пагинация ===
  const totalPagesCards = Math.ceil(displayItems.length / pageSize);
  const totalPagesTable = Math.ceil(filteredServers.length / pageSize);
  const totalPages = viewMode === 'cards' ? totalPagesCards : totalPagesTable;
  const paginatedDisplayItems = displayItems.slice((currentPage - 1) * pageSize, currentPage * pageSize);
  const paginatedServers = filteredServers.slice((currentPage - 1) * pageSize, currentPage * pageSize);
  const totalItemsCount = viewMode === 'cards' ? displayItems.length : filteredServers.length;

  useEffect(() => {
    if (currentPage > totalPages && totalPages >= 1) setCurrentPage(1);
  }, [viewMode, totalPages]);

  // === Handlers ===
  const handleSort = (field) => {
    if (sortField === field) {
      setSortOrder(sortOrder === 'asc' ? 'desc' : 'asc');
    } else {
      setSortField(field);
      setSortOrder('asc');
    }
  };

  const handleResetFilters = () => {
    setFilters({ country: '', provider: '', type: '', tunnel: '' });
    setSearchTerm('');
    setCurrentPage(1);
  };

  // === Selection ===
  const toggleServerSelection = (server) => {
    const newSet = new Set(selectedServers);
    if (newSet.has(server.ip)) {
      newSet.delete(server.ip);
    } else {
      newSet.add(server.ip);
    }
    setSelectedServers(newSet);
  };

  const toggleGroupSelection = (servers, selectAll) => {
    setSelectedServers((prev) => {
      const next = new Set(prev);
      servers.forEach((s) => {
        if (selectAll) next.add(s.ip);
        else next.delete(s.ip);
      });
      return next;
    });
  };

  const selectAllServers = () => {
    if (selectedServers.size === filteredServers.length) {
      setSelectedServers(new Set());
    } else {
      setSelectedServers(new Set(filteredServers.map((s) => s.ip)));
    }
  };

  const clearSelection = () => {
    setSelectedServers(new Set());
  };

  // === CRUD Operations ===
  const handleOpenAddServerModal = () => {
    setModalError('');
    setAddServerModalOpen(true);
  };

  const handleCloseAddServerModal = () => {
    setAddServerModalOpen(false);
    setModalError('');
  };

  const handleAddServerFromModal = (serverData) => {
    const nextId = `srv-${servers.length + 1}`;
    const serverToAdd = { 
      ...serverData, 
      id: serverData.id || nextId,
      type: normalizeServerType(serverData.type),
    };
    
    setServers([...servers, serverToAdd]);
    handleCloseAddServerModal();
    
    // Автоматически создаём запись в биллинге
    (async () => {
      try {
        const billingRes = await api.get(`/billing`);
        const billingData = Array.isArray(billingRes.data) ? billingRes.data : [];
        
        // Проверяем, нет ли уже такого сервера в биллинге
        const alreadyExists = billingData.some(b => b.serverId === serverToAdd.id);
        if (!alreadyExists) {
          const newBillingItem = {
            id: `billing-${serverToAdd.id}`,
            hostName: serverToAdd.dns || serverToAdd.ip,
            purpose: '',
            country: serverToAdd.country || '',
            provider: serverToAdd.provider || '',
            loginUrl: '',
            monthlyCost: 0,
            monthlyCostCurrency: 'USD',
            nextPaymentDate: '',
            lastPaymentDate: '',
            lastPaymentAmount: 0,
            lastPaymentCurrency: 'USD',
            status: 'active',
            notes: 'Автоматически добавлен из серверов',
            serverId: serverToAdd.id,
            payments: []
          };
          await api.post(`/billing`, { domains: [...billingData, newBillingItem] });
        }
      } catch (e) {
        console.warn('Не удалось создать запись биллинга:', e);
      }
    })();
    
    notify.success('Сервер успешно добавлен!');
  };

  const handleEdit = (server) => {
    setModalError('');
    setEditModalServer({ ...server });
    setEditModalOpen(true);
  };

  const handleEditModalSave = (updatedServer) => {
    const normalizedServer = { 
      ...updatedServer, 
      type: normalizeServerType(updatedServer.type),
    };
    setServers(servers.map(s => {
      const sameId = s.id && updatedServer.id ? s.id === updatedServer.id : false;
      const sameIp = s.ip === updatedServer.ip;
      return (sameId || sameIp) ? { ...s, ...normalizedServer } : s;
    }));
    setEditModalOpen(false);
    setEditModalServer(null);
    notify.success('Сервер успешно обновлён!');
  };

  const handleEditModalClose = () => {
    setEditModalOpen(false);
    setEditModalServer(null);
    setModalError('');
  };

  const confirmDelete = (server) => {
    setServerToDelete(server);
    setShowDeleteModal(true);
  };

  const executeDelete = () => {
    if (serverToDelete) {
      setServers(servers.filter(s => s.ip !== serverToDelete.ip));
      setShowDeleteModal(false);
      setServerToDelete(null);
      notify.success('Сервер удалён!');
    }
  };

  // === Bulk Actions ===
  const handleBulkDelete = () => {
    if (selectedServers.size === 0) return;
    setConfirmState({
      open: true,
      text: `Вы уверены, что хотите удалить ${selectedServers.size} сервер(ов)? Это действие необратимо.`,
      onConfirm: () => {
        setServers(servers.filter(s => !selectedServers.has(s.ip)));
        setSelectedServers(new Set());
        setConfirmState({ open: false, text: '', onConfirm: null });
        notify.success(`Удалено ${selectedServers.size} серверов. Не забудьте сохранить!`);
      },
    });
  };

  const handleBulkChangeTunnel = (newTunnel) => {
    if (selectedServers.size === 0) return;
    setServers(servers.map(s => 
      selectedServers.has(s.ip) ? { ...s, tunnel: newTunnel } : s
    ));
    notify.success(`Туннель изменён на ${newTunnel} для ${selectedServers.size} серверов. Не забудьте сохранить!`);
  };

  const handleBulkExport = () => {
    const selected = servers.filter(s => selectedServers.has(s.ip));
    exportServersToCSV(selected);
  };

  const handleBulkCopyLinks = async () => {
    const selected = servers.filter(s => selectedServers.has(s.ip));
    const links = selected.map(s => generateServerUrl(s)).join('\n');
    await copyToClipboard(links);
  };

  // === URL Generation ===
  const generateServerUrl = (server) => {
    const params = new URLSearchParams();
    Object.entries(urlSettings).forEach(([key, value]) => {
      if (key !== 'baseUrl' && value) {
        params.append(key, value);
      }
    });
    params.append('server', server.ip);
    return `${urlSettings.baseUrl}?${params.toString()}`;
  };

  const handleGenerateLink = (server) => {
    setLinkGeneratorServer(server);
    setLinkGeneratorOpen(true);
  };

  const handleLinkGeneratorClose = () => {
    setLinkGeneratorOpen(false);
    setLinkGeneratorServer(null);
  };

  const applyPreset = (name) => {
    const p = presets[name];
    if (!p) return;
    setUrlSettings(prev => ({ ...prev, ...p }));
    localStorage.setItem('urlSettings', JSON.stringify({ ...urlSettings, ...p }));
    notify.success('Профиль применён');
  };

  // === Clipboard ===
  const fallbackCopyToClipboard = (text) => {
    const textArea = document.createElement('textarea');
    textArea.value = text;
    document.body.appendChild(textArea);
    textArea.focus();
    textArea.select();
    try {
      document.execCommand('copy');
      notify.success('Скопировано в буфер обмена!');
    } catch (err) {
      notify.error('Не удалось скопировать');
    }
    document.body.removeChild(textArea);
  };

  const copyToClipboard = async (text) => {
    if (navigator.clipboard) {
      try {
        await navigator.clipboard.writeText(text);
        notify.success('Скопировано в буфер обмена!');
        return;
      } catch (err) {
        // fallback
      }
    }
    fallbackCopyToClipboard(text);
  };

  const quickCopyLink = async (server) => {
    const url = generateServerUrl(server);
    await copyToClipboard(url);
  };

  // === Export ===
  const exportServersToCSV = (serversList) => {
    const csvContent = [
      ['ID', 'IP', 'Ext IP', 'Internal IP', 'DNS', 'Country', 'Provider', 'Type', 'Tunnel', 'Generated URL'],
      ...serversList.map(server => [
        server.id || '',
        server.ip,
        server.extIp || '',
        server.internalIp || '',
        server.dns,
        server.country,
        server.provider,
        server.type,
        server.tunnel,
        generateServerUrl(server)
      ])
    ].map(row => row.map(cell => `"${cell}"`).join(',')).join('\n');

    const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' });
    const link = document.createElement('a');
    const url = URL.createObjectURL(blob);
    link.setAttribute('href', url);
    link.setAttribute('download', `server_links_${new Date().toISOString().split('T')[0]}.csv`);
    link.style.visibility = 'hidden';
    document.body.appendChild(link);
    link.click();
    document.body.removeChild(link);
  };

  const exportAllLinks = () => {
    exportServersToCSV(filteredServers);
  };

  // === Save ===
  const handleSaveChanges = async () => {
    setLoading(true);
    try {
      const normalizedServers = servers.map((s) => ({ ...s, type: normalizeServerType(s.type) }));
      setServers(normalizedServers);
      await api.post(`/servers`, { domains: normalizedServers });
      await api.post(`/server-connections`, { domains: connections });
      
      // Синхронизация с биллингом: добавляем новые серверы
      try {
        const billingRes = await api.get(`/billing`);
        const billingData = Array.isArray(billingRes.data) ? billingRes.data : [];
        
        // Создаём Set из существующих serverId в биллинге
        const existingServerIds = new Set(
          billingData.map(b => b.serverId).filter(Boolean)
        );
        
        // Находим серверы, которых нет в биллинге
        const newBillingItems = normalizedServers
          .filter(server => server.id && !existingServerIds.has(server.id))
          .map(server => ({
            id: `billing-${server.id}`,
            hostName: server.dns || server.ip,
            purpose: '',
            country: server.country || '',
            provider: server.provider || '',
            loginUrl: '',
            monthlyCost: 0,
            monthlyCostCurrency: 'USD',
            nextPaymentDate: '',
            lastPaymentDate: '',
            lastPaymentAmount: 0,
            lastPaymentCurrency: 'USD',
            status: 'active',
            notes: `Автоматически добавлен из серверов`,
            serverId: server.id,
            payments: []
          }));
        
        if (newBillingItems.length > 0) {
          const updatedBilling = [...billingData, ...newBillingItems];
          await api.post(`/billing`, { domains: updatedBilling });
          console.log(`Добавлено ${newBillingItems.length} записей в биллинг`);
        }
      } catch (billingError) {
        console.warn('Не удалось синхронизировать биллинг:', billingError);
        // Не прерываем основную операцию
      }
      
      notify.success('Изменения успешно сохранены!');
    } catch (error) {
      console.error('Error saving changes:', error);
      notify.error('Не удалось сохранить изменения.');
    } finally {
      setLoading(false);
    }
  };

  const handleRefreshAll = () => {
    fetchServers();
    fetchConnections();
    fetchNetworkConfig();
  };

  // === Connections ===
  const addConnectionInternal = (draft) => {
    if (!draft.from || !draft.to || !draft.tunnelType || !draft.ipA || !draft.ipB) {
      notify.error('Все поля для связи должны быть заполнены.');
      return false;
    }
    if (draft.from === draft.to) {
      notify.error('Сервер A и сервер B не могут быть одинаковыми.');
      return false;
    }
    const next = { ...draft };
    setConnections((prev) => [...prev, next]);
    notify.success('Связь успешно добавлена!');
    return true;
  };

  const handleAddConnection = () => {
    const ok = addConnectionInternal(newConnection);
    if (!ok) return;
    setNewConnection({ from: '', to: '', tunnelType: 'GRE', ipA: '', ipB: '' });
  };

  const handleConfirmGraphConnection = () => {
    const ok = addConnectionInternal(newConnection);
    if (!ok) return;
    setGraphConnectionModalOpen(false);
    setNewConnection({ from: '', to: '', tunnelType: 'GRE', ipA: '', ipB: '' });
  };

  const handleDeleteConnection = (index) => {
    setConnections(connections.filter((_, i) => i !== index));
    notify.success('Связь удалена!');
  };

  return (
    <div className="server-manager-layout">
      <PageHeader
        pretitle="Управление"
        title="Серверы"
        icon={<IconServer size={24} />}
        meta={
          <span className="text-muted">
            Всего серверов: <strong>{servers.length}</strong>
          </span>
        }
        actions={
          <>
            <PageHeaderActions
              loading={loading}
              onRefresh={handleRefreshAll}
              disableRefresh={loading}
              onExport={exportAllLinks}
              disableExport={servers.length === 0}
              onSave={handleSaveChanges}
              disableSave={loading}
            />
            <div className="mt-3 d-flex flex-wrap align-items-center justify-content-end gap-2">
              <div className="btn-group" role="group" aria-label="Переключение режимов">
                <button
                  className={`btn ${activeTab === 'servers' ? 'btn-primary' : 'btn-outline-primary'}`}
                  onClick={() => setActiveTab('servers')}
                  type="button"
                >
                  <IconServer size={16} className="me-1" />
                  Серверы
                </button>
                <button
                  className={`btn ${activeTab === 'connections' ? 'btn-primary' : 'btn-outline-primary'}`}
                  onClick={() => setActiveTab('connections')}
                  type="button"
                >
                  <IconGitBranch size={16} className="me-1" />
                  Связи
                </button>
              </div>
              <button
                type="button"
                className="btn btn-primary"
                onClick={handleOpenAddServerModal}
              >
                <IconPlus size={16} className="me-1" /> Добавить
              </button>
            </div>
          </>
        }
      />

      {/* Offcanvas: фильтры (только для вкладки «Серверы») */}
      {activeTab === 'servers' && (
        <>
          <div
            className="offcanvas offcanvas-start"
            id="server-filters-offcanvas"
            tabIndex={-1}
            aria-labelledby="server-filters-offcanvas-label"
            style={{ width: '340px', maxWidth: '90vw' }}
          >
            <div className="offcanvas-header border-bottom">
              <h5 className="offcanvas-title d-flex align-items-center" id="server-filters-offcanvas-label">
                <IconFilter size={18} className="text-primary me-2" />
                Фильтры
                {(Object.values(filters).filter(Boolean).length + (searchTerm ? 1 : 0)) > 0 && (
                  <span className="badge bg-primary-lt text-primary ms-2">
                    {Object.values(filters).filter(Boolean).length + (searchTerm ? 1 : 0)}
                  </span>
                )}
              </h5>
              <div className="d-flex align-items-center gap-1">
                {(Object.values(filters).filter(Boolean).length + (searchTerm ? 1 : 0)) > 0 && (
                  <button
                    type="button"
                    className="btn btn-ghost-secondary btn-sm"
                    onClick={handleResetFilters}
                    title="Сбросить фильтры"
                  >
                    Сбросить
                  </button>
                )}
                <button
                  type="button"
                  className="btn-close"
                  data-bs-dismiss="offcanvas"
                  aria-label="Закрыть"
                />
              </div>
            </div>
            <div className="offcanvas-body px-3 py-0 d-flex flex-column overflow-hidden">
              <ServerSidebar
                servers={servers}
                filters={filters}
                onFiltersChange={(newFilters) => { setFilters(newFilters); setCurrentPage(1); }}
                searchTerm={searchTerm}
                onSearch={(term) => { setSearchTerm(term); setCurrentPage(1); }}
                onReset={handleResetFilters}
                embedded
              />
            </div>
          </div>
        </>
      )}

      {/* Main content */}
      {activeTab === 'servers' && (
        <div>
          <div className="flex-grow-1">
            {/* Toolbar */}
            <div className="card mb-3">
              <div className="card-body py-2">
                <div className="d-flex align-items-center justify-content-between">
                  <div className="d-flex align-items-center gap-2">
                    {/* Открыть фильтры (offcanvas) */}
                    <button
                      type="button"
                      className="btn btn-outline-secondary btn-sm d-flex align-items-center"
                      data-bs-toggle="offcanvas"
                      data-bs-target="#server-filters-offcanvas"
                      aria-controls="server-filters-offcanvas"
                      title="Фильтры"
                    >
                      <IconFilter size={16} className="me-1" />
                      Фильтры
                      {(Object.values(filters).filter(Boolean).length + (searchTerm ? 1 : 0)) > 0 && (
                        <span className="badge bg-primary-lt text-primary ms-1">
                          {Object.values(filters).filter(Boolean).length + (searchTerm ? 1 : 0)}
                        </span>
                      )}
                    </button>
                    {/* Select all */}
                    <button
                      className="btn btn-outline-secondary btn-sm d-flex align-items-center"
                      onClick={selectAllServers}
                    >
                      {selectedServers.size === filteredServers.length && filteredServers.length > 0 ? (
                        <IconSquareCheck size={16} className="me-1 text-primary" />
                      ) : (
                        <IconSquare size={16} className="me-1 text-muted" />
                      )}
                      Выбрать все
                    </button>

                    {/* Sort dropdown */}
                    <div className="dropdown">
                      <button className="btn btn-outline-secondary btn-sm dropdown-toggle" type="button" data-bs-toggle="dropdown">
                        Сортировка: {sortField}
                      </button>
                      <div className="dropdown-menu">
                        {['ip', 'dns', 'country', 'provider', 'type', 'tunnel'].map(field => (
                          <button key={field} className="dropdown-item" onClick={() => handleSort(field)}>
                            {field} {sortField === field ? (sortOrder === 'asc' ? '▲' : '▼') : ''}
                          </button>
                        ))}
                      </div>
                    </div>
                  </div>

                  {/* View mode toggle */}
                  <div className="btn-group btn-group-sm" role="group">
                    <button
                      className={`btn ${viewMode === 'cards' ? 'btn-primary' : 'btn-outline-secondary'}`}
                      onClick={() => setViewMode('cards')}
                      title="Карточки"
                    >
                      <IconLayoutGrid size={16} />
                    </button>
                    <button
                      className={`btn ${viewMode === 'table' ? 'btn-primary' : 'btn-outline-secondary'}`}
                      onClick={() => setViewMode('table')}
                      title="Таблица"
                    >
                      <IconList size={16} />
                    </button>
                  </div>
                </div>
              </div>
            </div>

            {/* Content */}
            {loading ? (
              <div className="card">
                <div className="card-body text-center py-5">
                  <div className="spinner-border text-primary" role="status"></div>
                  <div className="text-muted mt-2">Загрузка серверов...</div>
                </div>
              </div>
            ) : filteredServers.length === 0 ? (
              <div className="card">
                <div className="card-body text-center py-5">
                  <IconServer size={48} className="text-muted mb-3" />
                  <h3>Серверы не найдены</h3>
                  <p className="text-muted">Попробуйте изменить фильтры или добавьте новый сервер</p>
                  <button className="btn btn-primary" onClick={handleOpenAddServerModal}>
                    <IconPlus size={16} className="me-1" /> Добавить сервер
                  </button>
                </div>
              </div>
            ) : viewMode === 'cards' ? (
              /* Cards view: одиночные серверы и группы (groupKey) */
              <div>
                {paginatedDisplayItems.map((item) =>
                  item.type === 'group' ? (
                    <ServerGroupCard
                      key={`group-${item.groupKey}`}
                      groupKey={item.groupKey}
                      servers={item.servers.map((s) => ({ ...s, gateways: getGatewaysForServer(s) }))}
                      selectedSet={selectedServers}
                      onSelect={toggleServerSelection}
                      onSelectGroup={toggleGroupSelection}
                      onEdit={handleEdit}
                      onDelete={confirmDelete}
                    />
                  ) : (
                    <ServerCard
                      key={item.server.id || item.server.ip}
                      server={{ ...item.server, gateways: getGatewaysForServer(item.server) }}
                      selected={selectedServers.has(item.server.ip)}
                      onSelect={toggleServerSelection}
                      onEdit={handleEdit}
                      onDelete={confirmDelete}
                    />
                  )
                )}
              </div>
            ) : (
              /* Table view */
              <div className="card">
                <div className="table-responsive">
                  <table className="table card-table table-vcenter table-nowrap mb-0">
                    <thead>
                      <tr>
                        <th style={{ width: 40 }}>
                          <button className="btn btn-ghost-secondary btn-icon btn-sm p-0" onClick={selectAllServers}>
                            {selectedServers.size === filteredServers.length && filteredServers.length > 0 ? (
                              <IconSquareCheck size={18} className="text-primary" />
                            ) : (
                              <IconSquare size={18} />
                            )}
                          </button>
                        </th>
                        <th className="cursor-pointer" onClick={() => handleSort('ip')}>
                          IP {sortField === 'ip' && (sortOrder === 'asc' ? '▲' : '▼')}
                        </th>
                        <th className="cursor-pointer" onClick={() => handleSort('type')}>
                          Тип {sortField === 'type' && (sortOrder === 'asc' ? '▲' : '▼')}
                        </th>
                        <th className="cursor-pointer" onClick={() => handleSort('dns')}>
                          DNS {sortField === 'dns' && (sortOrder === 'asc' ? '▲' : '▼')}
                        </th>
                        <th className="cursor-pointer" onClick={() => handleSort('country')}>
                          Страна {sortField === 'country' && (sortOrder === 'asc' ? '▲' : '▼')}
                        </th>
                        <th className="cursor-pointer" onClick={() => handleSort('provider')}>
                          Провайдер {sortField === 'provider' && (sortOrder === 'asc' ? '▲' : '▼')}
                        </th>
                        <th className="cursor-pointer" onClick={() => handleSort('tunnel')}>
                          Туннель {sortField === 'tunnel' && (sortOrder === 'asc' ? '▲' : '▼')}
                        </th>
                        <th className="text-end" style={{ width: 140 }}>Действия</th>
                      </tr>
                    </thead>
                    <tbody>
                      {paginatedServers.map((server) => (
                        <tr key={server.id || server.ip}>
                          <td>
                            <button className="btn btn-ghost-secondary btn-icon btn-sm p-0" onClick={() => toggleServerSelection(server)}>
                              {selectedServers.has(server.ip) ? (
                                <IconSquareCheck size={18} className="text-primary" />
                              ) : (
                                <IconSquare size={18} />
                              )}
                            </button>
                          </td>
                          <td><code className="fw-bold">{server.ip}</code></td>
                          <td>
                            <span className={`badge ${server.type === 'exit' ? 'bg-red-lt text-danger' : 'bg-blue-lt text-blue'}`}>
                              {server.type === 'exit' ? 'Exit' : 'Jumphost'}
                            </span>
                          </td>
                          <td>{server.dns}</td>
                          <td>
                            <span className="badge bg-secondary-lt">
                              {countryToFlag(server.country)} {server.country}
                            </span>
                          </td>
                          <td>{server.provider}</td>
                          <td><span className="badge bg-green-lt text-green">{server.tunnel}</span></td>
                          <td className="text-end">
                            <div className="btn-list gap-1 mb-0 justify-content-end">
                              <button className="btn btn-outline-secondary btn-sm" title="Редактировать" onClick={() => handleEdit(server)}>
                                <IconEdit size={14} className="me-1" />
                                Изменить
                              </button>
                              <button className="btn btn-outline-danger btn-icon btn-sm" title="Удалить" onClick={() => confirmDelete(server)}>
                                <IconTrash size={14} />
                              </button>
                            </div>
                          </td>
                        </tr>
                      ))}
                    </tbody>
                  </table>
                </div>
              </div>
            )}

            {/* Pagination */}
            {totalPages > 1 && (
              <div className="card mt-3">
                <div className="card-footer d-flex align-items-center justify-content-between">
                  <div className="text-muted">
                    {(currentPage - 1) * pageSize + 1} – {Math.min(currentPage * pageSize, totalItemsCount)} из {totalItemsCount}
                  </div>
                  <ul className="pagination m-0">
                    <li className={`page-item ${currentPage === 1 ? 'disabled' : ''}`}>
                      <button className="page-link" onClick={() => setCurrentPage(1)} disabled={currentPage === 1}>«</button>
                    </li>
                    <li className={`page-item ${currentPage === 1 ? 'disabled' : ''}`}>
                      <button className="page-link" onClick={() => setCurrentPage(currentPage - 1)} disabled={currentPage === 1}>‹</button>
                    </li>
                    {[...Array(Math.min(5, totalPages))].map((_, i) => {
                      const pageNum = Math.max(1, Math.min(currentPage - 2, totalPages - 4)) + i;
                      if (pageNum > totalPages) return null;
                      return (
                        <li key={pageNum} className={`page-item ${currentPage === pageNum ? 'active' : ''}`}>
                          <button className="page-link" onClick={() => setCurrentPage(pageNum)}>{pageNum}</button>
                        </li>
                      );
                    })}
                    <li className={`page-item ${currentPage === totalPages ? 'disabled' : ''}`}>
                      <button className="page-link" onClick={() => setCurrentPage(currentPage + 1)} disabled={currentPage === totalPages}>›</button>
                    </li>
                    <li className={`page-item ${currentPage === totalPages ? 'disabled' : ''}`}>
                      <button className="page-link" onClick={() => setCurrentPage(totalPages)} disabled={currentPage === totalPages}>»</button>
                    </li>
                  </ul>
                </div>
              </div>
            )}
          </div>
        </div>
      )}

      {/* Connections Tab */}
      {activeTab === 'connections' && (
        <div>
          {/* Graph */}
          <div className="card w-100 mb-4">
            <div className="card-header d-flex justify-content-between align-items-center">
              <h3 className="card-title mb-0">Граф связей между серверами</h3>
              <div className="text-muted small">Поддержка: GRE, IPSec, WireGuard, OpenVPN</div>
            </div>
            <div className="card-body">
              <div className="mb-3 d-flex flex-wrap gap-2 align-items-center">
                <span className="text-muted small d-flex align-items-center"><IconFilter size={16} className="me-1" />Фильтры:</span>
                <div className="btn-group btn-group-sm" role="group">
                  {['GRE','IPSec','WireGuard','OpenVPN'].map(t => (
                    <button key={t} className={`btn ${graphTunnelFilters[t] ? 'btn-primary' : 'btn-outline-primary'}`} onClick={() => setGraphTunnelFilters(prev => ({...prev, [t]: !prev[t]}))}>{t}</button>
                  ))}
                </div>
                <select className="form-select form-select-sm" style={{ width: 'auto' }} value={graphCountryFilter} onChange={e => setGraphCountryFilter(e.target.value)}>
                  <option value="">Все страны</option>
                  {Array.from(new Set(servers.map(s => s.country).filter(Boolean))).map(c => (<option key={c} value={c}>{c}</option>))}
                </select>
                <select className="form-select form-select-sm" style={{ width: 'auto' }} value={graphProviderFilter} onChange={e => setGraphProviderFilter(e.target.value)}>
                  <option value="">Все провайдеры</option>
                  {Array.from(new Set(servers.map(s => s.provider).filter(Boolean))).map(p => (<option key={p} value={p}>{p}</option>))}
                </select>
              </div>
              <GraphView 
                servers={servers.filter(s => (!graphCountryFilter || s.country === graphCountryFilter) && (!graphProviderFilter || s.provider === graphProviderFilter))} 
                connections={connections.filter(c => graphTunnelFilters[c.tunnelType])}
                onCreateConnection={({ from, to }) => {
                  setNewConnection(prev => ({
                    ...prev,
                    from,
                    to,
                    tunnelType: prev.tunnelType || servers.find(s => s.ip === from)?.tunnel || 'GRE',
                  }));
                  setGraphConnectionModalOpen(true);
                }} 
              />
              <div className="mt-3 d-flex gap-3 flex-wrap">
                <div className="d-flex align-items-center gap-2"><span style={{display:'inline-block',width:24,height:4,background:'#206bc4'}}></span><span className="text-muted small">GRE</span></div>
                <div className="d-flex align-items-center gap-2"><span style={{display:'inline-block',width:24,height:4,background:'#f59f00'}}></span><span className="text-muted small">IPSec</span></div>
                <div className="d-flex align-items-center gap-2"><span style={{display:'inline-block',width:24,height:4,background:'#2fb344'}}></span><span className="text-muted small">WireGuard</span></div>
                <div className="d-flex align-items-center gap-2"><span style={{display:'inline-block',width:24,height:4,background:'#be4bdb'}}></span><span className="text-muted small">OpenVPN</span></div>
              </div>
            </div>
          </div>

          {/* Add connection */}
          <div className="card w-100 mb-4">
            <div className="card-header">
              <h3 className="card-title mb-0">Добавить связь между серверами</h3>
            </div>
            <div className="card-body">
              <div className="row g-3">
                <div className="col-md-6">
                  <label className="form-label">Сервер A</label>
                  <select className="form-select" value={newConnection.from} onChange={e => setNewConnection({ ...newConnection, from: e.target.value })}>
                    <option value="">Выберите сервер</option>
                    {servers.map(s => <option key={s.ip} value={s.ip}>{s.ip} ({s.dns})</option>)}
                  </select>
                </div>
                <div className="col-md-6">
                  <label className="form-label">Сервер B</label>
                  <select className="form-select" value={newConnection.to} onChange={e => setNewConnection({ ...newConnection, to: e.target.value })}>
                    <option value="">Выберите сервер</option>
                    {servers.map(s => <option key={s.ip} value={s.ip}>{s.ip} ({s.dns})</option>)}
                  </select>
                </div>
                <div className="col-md-4">
                  <label className="form-label">Тип туннеля</label>
                  <select className="form-select" value={newConnection.tunnelType} onChange={e => setNewConnection({ ...newConnection, tunnelType: e.target.value })}>
                    <option value="GRE">GRE</option>
                    <option value="IPSec">IPSec</option>
                    <option value="WireGuard">WireGuard</option>
                    <option value="OpenVPN">OpenVPN</option>
                  </select>
                </div>
                <div className="col-md-4">
                  <label className="form-label">IP туннеля A</label>
                  <input className="form-control" value={newConnection.ipA} onChange={e => setNewConnection({ ...newConnection, ipA: e.target.value })} placeholder="10.10.100.1" />
                </div>
                <div className="col-md-4">
                  <label className="form-label">IP туннеля B</label>
                  <input className="form-control" value={newConnection.ipB} onChange={e => setNewConnection({ ...newConnection, ipB: e.target.value })} placeholder="10.10.100.2" />
                </div>
                <div className="col-12">
                  <button className="btn btn-primary" type="button" onClick={handleAddConnection}>
                    <IconPlus size={16} className="me-1" /> Добавить связь
                  </button>
                </div>
              </div>
            </div>
          </div>

          {/* Connections list */}
          {connections.length > 0 && (
            <div className="card w-100">
              <div className="card-header">
                <h3 className="card-title mb-0">Существующие связи ({connections.length})</h3>
              </div>
              <div className="table-responsive">
                <table className="table card-table table-vcenter table-nowrap mb-0">
                  <thead>
                    <tr>
                      <th>Сервер A</th>
                      <th>Сервер B</th>
                      <th>Туннель</th>
                      <th>IP адреса</th>
                      <th className="text-end">Действия</th>
                    </tr>
                  </thead>
                  <tbody>
                    {connections.map((conn, index) => {
                      const serverA = servers.find(s => s.ip === conn.from);
                      const serverB = servers.find(s => s.ip === conn.to);
                      return (
                        <tr key={index}>
                          <td>
                            <div className="fw-bold">{conn.from}</div>
                            <div className="text-muted small">{serverA?.dns || '—'}</div>
                          </td>
                          <td>
                            <div className="fw-bold">{conn.to}</div>
                            <div className="text-muted small">{serverB?.dns || '—'}</div>
                          </td>
                          <td><span className="badge bg-green-lt text-green">{conn.tunnelType}</span></td>
                          <td><code>{conn.ipA} ⇄ {conn.ipB}</code></td>
                          <td className="text-end">
                            <button className="btn btn-ghost-danger btn-icon btn-sm" onClick={() => handleDeleteConnection(index)}>
                              <IconTrash size={16} />
                            </button>
                          </td>
                        </tr>
                      );
                    })}
                  </tbody>
                </table>
              </div>
            </div>
          )}
        </div>
      )}

      {/* Bulk Actions Bar */}
      <ServerBulkActions
        selectedCount={selectedServers.size}
        onClearSelection={clearSelection}
        onBulkDelete={handleBulkDelete}
        onBulkChangeTunnel={handleBulkChangeTunnel}
        onBulkExport={handleBulkExport}
        onBulkCopyLinks={handleBulkCopyLinks}
      />

      {/* Modals */}
      <ConfirmModal
        show={showDeleteModal}
        onClose={() => setShowDeleteModal(false)}
        onConfirm={executeDelete}
        title="Подтверждение удаления"
        message={
          serverToDelete ? (
            <>
              <p>Вы уверены, что хотите удалить сервер <strong>{serverToDelete.ip}</strong>?</p>
              <p className="text-muted mb-0">Это действие нельзя отменить.</p>
            </>
          ) : null
        }
        confirmLabel="Удалить"
        variant="danger"
        icon={IconTrash}
      />

      {/* Единое модальное окно для редактирования */}
      <ServerModal
        show={editModalOpen}
        mode="edit"
        server={editModalServer}
        onSave={handleEditModalSave}
        onClose={handleEditModalClose}
        error={modalError}
      />

      <LinkGeneratorModal
        show={linkGeneratorOpen}
        server={linkGeneratorServer}
        urlSettings={urlSettings}
        onUrlSettingsChange={setUrlSettings}
        onGenerateUrl={generateServerUrl}
        onCopyToClipboard={copyToClipboard}
        onClose={handleLinkGeneratorClose}
      />

      {/* Единое модальное окно для добавления */}
      <ServerModal
        show={addServerModalOpen}
        mode="add"
        onSave={handleAddServerFromModal}
        onClose={handleCloseAddServerModal}
        error={modalError}
      />

      {/* Graph Connection Modal */}
      <FormModal
        show={graphConnectionModalOpen}
        onClose={() => {
          setGraphConnectionModalOpen(false);
          setNewConnection({ from: '', to: '', tunnelType: 'GRE', ipA: '', ipB: '' });
        }}
        onSubmit={handleConfirmGraphConnection}
        title="Создать связь"
        submitLabel="Создать"
        submitIcon={IconPlus}
        size="md"
      >
        <div className="mb-3">
          <div className="d-flex align-items-center gap-2">
            <span className="badge bg-blue-lt text-blue">{newConnection.from || 'A'}</span>
            <span className="text-muted">⇄</span>
            <span className="badge bg-blue-lt text-blue">{newConnection.to || 'B'}</span>
          </div>
        </div>
        <div className="row g-3">
          <div className="col-md-4">
            <FormField
              label="Туннель"
              name="tunnelType"
              type="select"
              value={newConnection.tunnelType}
              onChange={(val) => setNewConnection({ ...newConnection, tunnelType: val })}
              required
              options={[
                { value: 'GRE', label: 'GRE' },
                { value: 'IPSec', label: 'IPSec' },
                { value: 'WireGuard', label: 'WireGuard' },
                { value: 'OpenVPN', label: 'OpenVPN' },
              ]}
            />
          </div>
          <div className="col-md-4">
            <FormField
              label="IP сервера A"
              name="ipA"
              value={newConnection.ipA}
              onChange={(val) => setNewConnection({ ...newConnection, ipA: val })}
              placeholder="10.10.100.1"
              required
            />
          </div>
          <div className="col-md-4">
            <FormField
              label="IP сервера B"
              name="ipB"
              value={newConnection.ipB}
              onChange={(val) => setNewConnection({ ...newConnection, ipB: val })}
              placeholder="10.10.100.2"
              required
            />
          </div>
        </div>
      </FormModal>

      <ConfirmDialog
        open={confirmState.open}
        title="Подтверждение"
        message={confirmState.text}
        onCancel={() => setConfirmState({ open: false, text: '', onConfirm: null })}
        onConfirm={() => confirmState.onConfirm?.()}
      />
    </div>
  );
}

export default ServerManager;
