import React, { useState, useRef, useEffect } from 'react';
import * as d3 from 'd3';
import { IconZoomIn, IconZoomOut, IconMaximize, IconMinimize } from '@tabler/icons-react';

function GraphView({ servers, connections }) {
  // Debug helper (enable/disable via window.GRAPH_DEBUG = true/false in console)
  const isDebug = typeof window !== 'undefined' ? (window.GRAPH_DEBUG !== false) : true;
  const dbg = (...args) => { if (isDebug && typeof console !== 'undefined') console.log('[GraphView]', ...args); };

  const [nodePositions, setNodePositions] = useState({});
  const [draggedNode, setDraggedNode] = useState(null);
  const [dragOffset, setDragOffset] = useState({ x: 0, y: 0 });
  const [hoveredLinkId, setHoveredLinkId] = useState(null);
  const containerRef = useRef(null);
  const svgRef = useRef(null);
  const gRef = useRef(null);
  const zoomRef = useRef(null);
  const [isFullscreen, setIsFullscreen] = useState(false);

  // Размеры карточки узла
  const NODE_WIDTH = 160;
  const NODE_HEIGHT = 74;

  // Автоматическая раскладка (force-directed)
  useEffect(() => {
    if (!servers || servers.length === 0) return;

    const nodes = servers.map((s) => ({ id: s.ip }));
    const links = (connections || []).map((c) => ({ source: c.from, target: c.to, type: c.tunnelType }));

    const distanceForType = (t) => ({ GRE: 200, IPSec: 220, WireGuard: 180, OpenVPN: 200 }[t] || 210);

    const simulation = d3
      .forceSimulation(nodes)
      .force('link', d3.forceLink(links).id((d) => d.id).distance((d) => distanceForType(d.type)))
      .force('charge', d3.forceManyBody().strength(-500))
      .force('collide', d3.forceCollide(70))
      .force('center', d3.forceCenter(450, 300))
      .stop();

    for (let i = 0; i < 300; i += 1) simulation.tick();

    const pos = {};
    nodes.forEach((n) => {
      pos[n.id] = { x: n.x, y: n.y };
    });
    setNodePositions(pos);

    // После раскладки подгоняем граф по области видимости
    try {
      const svgEl = svgRef.current;
      const box = svgEl.getBoundingClientRect();
      const padding = 40;
      let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
      nodes.forEach((n) => {
        minX = Math.min(minX, n.x - NODE_WIDTH / 2);
        minY = Math.min(minY, n.y - NODE_HEIGHT / 2);
        maxX = Math.max(maxX, n.x + NODE_WIDTH / 2);
        maxY = Math.max(maxY, n.y + NODE_HEIGHT / 2);
      });
      const contentW = Math.max(1, maxX - minX);
      const contentH = Math.max(1, maxY - minY);
      const scale = Math.max(0.5, Math.min(1.4, Math.min((box.width - padding) / contentW, (box.height - padding) / contentH)));
      const tx = (box.width - scale * (minX + maxX)) / 2;
      const ty = (box.height - scale * (minY + maxY)) / 2;
      const t = d3.zoomIdentity.translate(tx, ty).scale(scale);
      d3.select(svgEl).transition().duration(350).call(zoomRef.current.transform, t);
    } catch (e) {
      // ignore
    }
  }, [servers, connections]);

  // Зум/панорамирование
  useEffect(() => {
    const svg = d3.select(svgRef.current);
    const g = d3.select(gRef.current);
    const width = svgRef.current?.clientWidth || 900;
    const height = svgRef.current?.clientHeight || 600;
    zoomRef.current = d3
      .zoom()
        .scaleExtent([0.3, 3])
        .extent([[0, 0], [width, height]])
        // Разрешаем панораму в широких пределах
        .translateExtent([[-10000, -10000], [10000, 10000]])
        .on('zoom', (event) => {
          g.attr('transform', event.transform);
          // dbg('onZoom', { k: event.transform.k, x: event.transform.x, y: event.transform.y });
        });
    svg.call(zoomRef.current);
    dbg('zoom initialized', { width, height });
  }, []);

  // Контролы масштабирования
  const zoomBy = (factor) => {
    if (!zoomRef.current || !svgRef.current) return;
    const svgEl = svgRef.current;
    const current = d3.zoomTransform(svgEl);
    const cx = svgEl.clientWidth / 2;
    const cy = svgEl.clientHeight / 2;
    const minK = 0.3;
    const maxK = 3;
    let nextK = current.k * factor;
    if (nextK < minK) nextK = minK;
    if (nextK > maxK) nextK = maxK;
    dbg('zoomBy click', { factor, current: { k: current.k, x: current.x, y: current.y }, nextK, center: { cx, cy } });
    const svgSel = d3.select(svgEl);
    svgSel.interrupt();
    svgSel
      .transition()
      .duration(220)
      .call(zoomRef.current.scaleTo, nextK, [cx, cy])
      .on('end', () => {
        const nt = d3.zoomTransform(svgEl);
        dbg('zoomBy end', { next: { k: nt.k, x: nt.x, y: nt.y } });
      });
  };

  const fitToView = () => {
    const svgEl = svgRef.current;
    if (!svgEl) return;
    const box = svgEl.getBoundingClientRect();
    const padding = 40;
    const positions = nodePositions;
    const ids = Object.keys(positions);
    if (ids.length === 0) return;
    let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
    ids.forEach((id) => {
      const p = positions[id];
      minX = Math.min(minX, p.x - NODE_WIDTH / 2);
      minY = Math.min(minY, p.y - NODE_HEIGHT / 2);
      maxX = Math.max(maxX, p.x + NODE_WIDTH / 2);
      maxY = Math.max(maxY, p.y + NODE_HEIGHT / 2);
    });
    const contentW = Math.max(1, maxX - minX);
    const contentH = Math.max(1, maxY - minY);
    const scale = Math.max(0.5, Math.min(1.6, Math.min((box.width - padding) / contentW, (box.height - padding) / contentH)));
    const tx = (box.width - scale * (minX + maxX)) / 2;
    const ty = (box.height - scale * (minY + maxY)) / 2;
    const t = d3.zoomIdentity.translate(tx, ty).scale(scale);
    d3.select(svgEl).transition().duration(250).call(zoomRef.current.transform, t);
  };

  // Полноэкранный режим
  useEffect(() => {
    const handler = () => setIsFullscreen(Boolean(document.fullscreenElement));
    document.addEventListener('fullscreenchange', handler);
    return () => document.removeEventListener('fullscreenchange', handler);
  }, []);

  const toggleFullscreen = () => {
    const el = containerRef.current;
    if (!el) return;
    dbg('toggleFullscreen', { isFullscreen: Boolean(document.fullscreenElement) });
    if (!document.fullscreenElement) {
      el.requestFullscreen?.();
    } else {
      document.exitFullscreen?.();
    }
  };

  if (servers.length === 0) {
    return (
      <div className="text-center py-5">
        <div className="text-muted">
          <h4>Нет серверов для отображения</h4>
          <p>Добавьте серверы, чтобы увидеть граф связей</p>
        </div>
      </div>
    );
  }

  // Инициализация позиций узлов при первом рендере
  const getInitialNodePosition = (index, total) => {
    if (nodePositions[servers[index]?.ip]) {
      return nodePositions[servers[index].ip];
    }
    
    const cols = Math.ceil(Math.sqrt(total));
    const row = Math.floor(index / cols);
    const col = index % cols;
    const spacing = 180;
    return {
      x: 120 + col * spacing,
      y: 120 + row * spacing
    };
  };

  // Обработчик начала перетаскивания
  const handleMouseDown = (e, serverIp) => {
    const target = gRef.current || svgRef.current;
    const pt = (gRef.current?.ownerSVGElement || svgRef.current).createSVGPoint();
    pt.x = e.clientX;
    pt.y = e.clientY;
    const svgP = pt.matrixTransform(target.getScreenCTM().inverse());
    
    const currentPos = nodePositions[serverIp] || getInitialNodePosition(
      servers.findIndex(s => s.ip === serverIp), 
      servers.length
    );
    
    setDragOffset({
      x: svgP.x - currentPos.x,
      y: svgP.y - currentPos.y
    });
    setDraggedNode(serverIp);
  };

  // Обработчик перетаскивания
  const handleMouseMove = (e) => {
    if (!draggedNode) return;
    const target = gRef.current || svgRef.current;
    const pt = (gRef.current?.ownerSVGElement || svgRef.current).createSVGPoint();
    pt.x = e.clientX;
    pt.y = e.clientY;
    const svgP = pt.matrixTransform(target.getScreenCTM().inverse());
    
    setNodePositions(prev => ({
      ...prev,
      [draggedNode]: {
        x: svgP.x - dragOffset.x,
        y: svgP.y - dragOffset.y
      }
    }));
  };

  // Обработчик окончания перетаскивания
  const handleMouseUp = () => {
    setDraggedNode(null);
  };

  // Получение позиции узла
  const getNodePosition = (serverIp) => {
    return nodePositions[serverIp] || getInitialNodePosition(
      servers.findIndex(s => s.ip === serverIp), 
      servers.length
    );
  };

  // Цвета для разных стран
  const getCountryColor = (country) => {
    const colors = {
      'RU': '#dc2626', // красный
      'US': '#2563eb', // синий
      'DE': '#059669', // зеленый
      'SE': '#7c3aed', // фиолетовый
      'NL': '#ea580c', // оранжевый
      'SG': '#0891b2'  // голубой
    };
    return colors[country] || '#6b7280';
  };

  // Цвета/стили для разных типов туннелей
  const getTunnelStyle = (tunnelType) => {
    const map = {
      GRE: { color: '#206bc4', dash: '0', width: 3 },
      IPSec: { color: '#f59f00', dash: '6,4', width: 3 },
      WireGuard: { color: '#2fb344', dash: '0', width: 4 },
      OpenVPN: { color: '#be4bdb', dash: '3,3', width: 3 },
    };
    return map[tunnelType] || { color: '#667382', dash: '5,5', width: 2 };
  };

  // Флаг страны по ISO-2 с поддержкой нестандартных кодов из списка (SWE->SE)
  const getFlagEmoji = (code) => {
    if (!code) return '';
    const map = { SWE: 'SE', UK: 'GB' };
    const ccRaw = String(code).trim().toUpperCase();
    const cc = (map[ccRaw] || ccRaw).slice(0, 2);
    if (cc.length !== 2) return ccRaw;
    return cc.replace(/./g, (ch) => String.fromCodePoint(127397 + ch.charCodeAt()));
  };

  return (
    <div ref={containerRef} style={{ width: '100%', height: '500px', border: '1px solid #e5e7eb', borderRadius: '8px', overflow: 'hidden', background: '#f8fafc', position: 'relative' }}>
      <svg 
        ref={svgRef}
        width="100%" 
        height="100%" 
        viewBox="0 0 900 600"
        onMouseMove={handleMouseMove}
        onMouseUp={handleMouseUp}
        onMouseLeave={handleMouseUp}
        style={{ cursor: draggedNode ? 'grabbing' : 'default' }}
      >
        <defs>
          {/* Лёгкая сетка фона */}
          <pattern id="grid" width="30" height="30" patternUnits="userSpaceOnUse">
            <path d="M 30 0 L 0 0 0 30" fill="none" stroke="#f2f4f6" strokeWidth="1" />
          </pattern>
          {/* Определение стрелок под цвет канала */}
          {Array.from(new Set(connections.map(c => c.tunnelType))).map((t) => {
            const style = getTunnelStyle(t);
            return (
              <marker key={`arrow-${t}`}
                id={`arrow-${t}`}
                markerWidth="12"
                markerHeight="8"
                refX="10"
                refY="4"
                orient="auto"
              >
                <polygon points="0 0, 12 4, 0 8" fill={style.color} />
              </marker>
            );
          })}
          {/* Свечение для выделения */}
          <filter id="glow" x="-50%" y="-50%" width="200%" height="200%">
            <feGaussianBlur stdDeviation="2" result="coloredBlur"/>
            <feMerge>
              <feMergeNode in="coloredBlur"/>
              <feMergeNode in="SourceGraphic"/>
            </feMerge>
          </filter>
          {/* Тень карточки */}
          <filter id="cardShadow" x="-50%" y="-50%" width="200%" height="200%">
            <feDropShadow dx="0" dy="2" stdDeviation="3" floodOpacity="0.2" />
          </filter>
          {/* Тень для ярлыков ссылок */}
          <filter id="labelShadow" x="-50%" y="-50%" width="200%" height="200%">
            <feDropShadow dx="0" dy="1" stdDeviation="2" floodOpacity="0.25" />
          </filter>
        </defs>
        {/* Подложка сетки */}
        <rect width="100%" height="100%" fill="url(#grid)" />
        <g ref={gRef}>
        {/* Связи */}
        {connections.map((connection, index) => {
          const fromServer = servers.find(s => s.ip === connection.from);
          const toServer = servers.find(s => s.ip === connection.to);
          
          if (!fromServer || !toServer) return null;

          const fromPos = getNodePosition(connection.from);
          const toPos = getNodePosition(connection.to);
          const style = getTunnelStyle(connection.tunnelType);
          const id = `${connection.from}-${connection.to}-${index}`;
          const isHovered = hoveredLinkId === id;

          // Кривая линия с небольшим отступом от прямой для читаемости
          const mx = (fromPos.x + toPos.x) / 2;
          const my = (fromPos.y + toPos.y) / 2;
          const dx = toPos.x - fromPos.x;
          const dy = toPos.y - fromPos.y;
          const len = Math.sqrt(dx * dx + dy * dy) || 1;
          const nx = (-dy / len) * 30; // перпендикуляр, 30px
          const ny = (dx / len) * 30;
          const cx = mx + nx;
          const cy = my + ny;

          return (
            <g key={`link-${index}`}
               onMouseEnter={() => setHoveredLinkId(id)}
               onMouseLeave={() => setHoveredLinkId(null)}
            >
              {/* Линия связи */}
              <path
                d={`M ${fromPos.x} ${fromPos.y} Q ${cx} ${cy} ${toPos.x} ${toPos.y}`}
                fill="none"
                stroke={style.color}
                strokeWidth={isHovered ? style.width + 1 : style.width}
                strokeDasharray={style.dash}
                markerEnd={`url(#arrow-${connection.tunnelType})`}
                filter={isHovered ? 'url(#glow)' : undefined}
                opacity={isHovered ? 1 : 0.9}
                style={{ vectorEffect: 'non-scaling-stroke' }}
              />
              
              {/* Подпись связи: ярлык с подложкой и halo */}
              {(() => {
                const labelW = 140;
                const labelH = 38;
                const lx = cx - labelW / 2;
                const ly = cy - labelH / 2 - 4; // слегка выше вершины
                return (
                  <g style={{ pointerEvents: 'none' }}>
                    <rect
                      x={lx}
                      y={ly}
                      width={labelW}
                      height={labelH}
                      rx="16"
                      fill="#ffffff"
                      stroke={style.color}
                      strokeWidth="1"
                      filter="url(#labelShadow)"
                    />
                    {/* Halo для заголовка */}
                    <text
                      x={cx}
                      y={ly + 16}
                      textAnchor="middle"
                      fontSize="12"
                      fontWeight="700"
                      stroke="#ffffff"
                      strokeWidth="3"
                      strokeLinejoin="round"
                      fill="#374151"
                      style={{ paintOrder: 'stroke fill' }}
                    >
                      {connection.tunnelType}
                    </text>
                    {/* Линия IP с более мелким шрифтом */}
                    <text
                      x={cx}
                      y={ly + 30}
                      textAnchor="middle"
                      fontSize="10"
                      fill="#6b7280"
                      stroke="#ffffff"
                      strokeWidth="2"
                      strokeLinejoin="round"
                      style={{ paintOrder: 'stroke fill' }}
                    >
                      {connection.ipA} ⇄ {connection.ipB}
                    </text>
                  </g>
                );
              })()}
            </g>
          );
        })}

        {/* Узлы (серверы) */}
        {servers.map((server) => {
          const pos = getNodePosition(server.ip);
          const countryColor = getCountryColor(server.country);
          const isDragging = draggedNode === server.ip;

          return (
            <g key={server.ip}>
              {/* Карточка узла */}
              <rect
                x={pos.x - NODE_WIDTH / 2}
                y={pos.y - NODE_HEIGHT / 2}
                rx={12}
                ry={12}
                width={NODE_WIDTH}
                height={NODE_HEIGHT}
                fill="#ffffff"
                stroke={countryColor}
                strokeWidth={2}
                filter="url(#cardShadow)"
                style={{ cursor: 'grab' }}
                onMouseDown={(e) => handleMouseDown(e, server.ip)}
              />
              {/* Плашка страны */}
              {(() => {
                const badgeX = pos.x - NODE_WIDTH / 2 + 8;
                const badgeY = pos.y - NODE_HEIGHT / 2 - 10;
                const flag = getFlagEmoji(server.country);
                return (
                  <g>
                    <rect
                      x={badgeX}
                      y={badgeY}
                      rx={8}
                      ry={8}
                      width={52}
                      height={20}
                      fill="#fff"
                      stroke={countryColor}
                    />
                    {/* Рендер флага через foreignObject как в HTML (надёжнее для Windows/Chrome) */}
                    <foreignObject x={badgeX + 4} y={badgeY + 3} width={16} height={14} style={{ pointerEvents: 'none' }}>
                      <div xmlns="http://www.w3.org/1999/xhtml"
                           style={{fontSize:'14px', lineHeight:'14px', fontFamily:'Segoe UI Emoji, Noto Color Emoji, Apple Color Emoji, "Twemoji Mozilla", "EmojiOne Color", sans-serif'}}>
                        {flag}
                      </div>
                    </foreignObject>
                    <text
                      x={badgeX + 30}
                      y={badgeY + 13}
                      fontSize="11"
                      fill="#475569"
                      dominantBaseline="middle"
                    >
                      {String(server.country).toUpperCase().slice(0,3)}
                    </text>
                  </g>
                );
              })()}
              {/* Название */}
              <text
                x={pos.x}
                y={pos.y - 4}
                textAnchor="middle"
                fontSize="13"
                fill="#1f2937"
                fontWeight="700"
              >
                {server.dns}
              </text>
              {/* IP */}
              <text
                x={pos.x}
                y={pos.y + 14}
                textAnchor="middle"
                fontSize="12"
                fill="#334155"
              >
                {server.ip}
              </text>
              {/* Провайдер */}
              <text
                x={pos.x}
                y={pos.y + 30}
                textAnchor="middle"
                fontSize="10"
                fill="#6b7280"
              >
                {server.provider}
              </text>
            </g>
          );
        })}

        {/* Доп. определения добавлены выше */}
        </g>
      </svg>
      {/* Контролы масштабирования */}
      <div className="position-absolute" style={{ right: 10, top: 10, zIndex: 1000, pointerEvents: 'auto' }}>
        <div className="btn-group btn-group-sm">
          <button className="btn btn-outline-secondary" onClick={() => zoomBy(1.2)} title="Увеличить">
            <IconZoomIn size={16} />
          </button>
          <button className="btn btn-outline-secondary" onClick={() => zoomBy(1/1.2)} title="Уменьшить">
            <IconZoomOut size={16} />
          </button>
          <button className="btn btn-outline-secondary" onClick={toggleFullscreen} title={isFullscreen ? 'Выйти из полноэкранного' : 'Во весь экран'}>
            {isFullscreen ? <IconMinimize size={16} /> : <IconMaximize size={16} />}
          </button>
        </div>
      </div>
    </div>
  );
}

export default GraphView; 