using System;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;
using AGVNavigationCore.Models;

namespace AGVNavigationCore.Controls
{
    public partial class UnifiedAGVCanvas
    {
        #region Mouse Events

        private void UnifiedAGVCanvas_MouseClick(object sender, MouseEventArgs e)
        {
            Focus(); // 포커스 설정

            var worldPoint = ScreenToWorld(e.Location);
            var hitNode = GetNodeAt(worldPoint);

            switch (_editMode)
            {
                case EditMode.Select:
                    HandleSelectClick(hitNode, worldPoint);
                    break;

                case EditMode.AddNode:
                    HandleAddNodeClick(worldPoint);
                    break;

                case EditMode.AddLabel:
                    HandleAddLabelClick(worldPoint);
                    break;

                case EditMode.AddImage:
                    HandleAddImageClick(worldPoint);
                    break;

                case EditMode.Connect:
                    HandleConnectClick(hitNode);
                    break;

                case EditMode.Delete:
                    HandleDeleteClick(hitNode);
                    break;

                case EditMode.DeleteConnection:
                    HandleDeleteConnectionClick(worldPoint);
                    break;
            }
        }

        private void UnifiedAGVCanvas_MouseDoubleClick(object sender, MouseEventArgs e)
        {
            var worldPoint = ScreenToWorld(e.Location);
            var hitNode = GetNodeAt(worldPoint);

            if (hitNode != null)
            {
                // 노드 타입별 더블클릭 액션
                switch (hitNode.Type)
                {
                    case NodeType.Normal:
                    case NodeType.Rotation:
                    case NodeType.Docking:
                    case NodeType.Charging:
                        HandleNormalNodeDoubleClick(hitNode);
                        break;

                    case NodeType.Label:
                        HandleLabelNodeDoubleClick(hitNode);
                        break;

                    case NodeType.Image:
                        HandleImageNodeDoubleClick(hitNode);
                        break;

                    default:
                        // 기본 동작: 노드 선택 이벤트 발생
                        NodeSelected?.Invoke(this, hitNode);
                        break;
                }
            }
        }

        private void HandleNormalNodeDoubleClick(MapNode node)
        {
            // RFID 입력창 표시
            string currentRfid = node.RfidId ?? "";
            string newRfid = Microsoft.VisualBasic.Interaction.InputBox(
                $"노드 '{node.Name}'의 RFID를 입력하세요:",
                "RFID 설정",
                currentRfid);

            if (!string.IsNullOrWhiteSpace(newRfid) && newRfid != currentRfid)
            {
                node.RfidId = newRfid.Trim();
                MapChanged?.Invoke(this, EventArgs.Empty);
                Invalidate();
            }

            // 노드 선택 이벤트도 발생 (속성창 업데이트)
            NodeSelected?.Invoke(this, node);
        }

        private void HandleLabelNodeDoubleClick(MapNode node)
        {
            // 라벨 텍스트 입력창 표시
            string currentText = node.LabelText ?? "새 라벨";
            string newText = Microsoft.VisualBasic.Interaction.InputBox(
                "라벨 텍스트를 입력하세요:",
                "라벨 편집",
                currentText);

            if (!string.IsNullOrWhiteSpace(newText) && newText != currentText)
            {
                node.LabelText = newText.Trim();
                MapChanged?.Invoke(this, EventArgs.Empty);
                Invalidate();
            }

            // 노드 선택 이벤트도 발생 (속성창 업데이트)
            NodeSelected?.Invoke(this, node);
        }

        private void HandleImageNodeDoubleClick(MapNode node)
        {
            // 이미지 노드는 선택 이벤트만 발생 (MainForm에서 이미지 편집 버튼 활성화됨)
            NodeSelected?.Invoke(this, node);

            // 이미지 편집 이벤트 발생 (MainForm에서 처리)
            ImageNodeDoubleClicked?.Invoke(this, node);
        }

        private void UnifiedAGVCanvas_MouseDown(object sender, MouseEventArgs e)
        {
            var worldPoint = ScreenToWorld(e.Location);

            if (e.Button == MouseButtons.Left)
            {
                if (_editMode == EditMode.Move)
                {
                    var hitNode = GetNodeAt(worldPoint);
                    if (hitNode != null)
                    {
                        _isDragging = true;
                        _isPanning = false; // 🔥 팬 모드 비활성화 - 중요!
                        _selectedNode = hitNode;
                        _dragStartPosition = hitNode.Position; // 원래 위치 저장 (고스트용)
                        _dragOffset = new Point(
                            worldPoint.X - hitNode.Position.X,
                            worldPoint.Y - hitNode.Position.Y
                        );
                        _mouseMoveCounter = 0; // 디버그: 카운터 리셋
                        Cursor = Cursors.SizeAll;
                        Capture = true; // 🔥 마우스 캡처 활성화 - 이게 핵심!
                        //System.Diagnostics.Debug.WriteLine($"MouseDown: 드래그 시작! Capture={Capture}, isDragging={_isDragging}, isPanning={_isPanning}, Node={hitNode.NodeId}");
                        Invalidate();
                        return;
                    }
                }

                // 팬 시작 (좌클릭 - 모드에 따라)
                _isPanning = true;
                _lastMousePosition = e.Location;
                Cursor = Cursors.SizeAll;
                Capture = true; // 🔥 마우스 캡처 활성화
            }
            else if (e.Button == MouseButtons.Middle)
            {
                // 중간 버튼: 항상 팬 (어떤 모드든 상관없이)
                _isPanning = true;
                _lastMousePosition = e.Location;
                Cursor = Cursors.Hand;
                Capture = true; // 🔥 마우스 캡처 활성화
            }
            else if (e.Button == MouseButtons.Right)
            {
                // 컨텍스트 메뉴 (편집 모드에서만)
                if (_canvasMode == CanvasMode.Edit)
                {
                    var hitNode = GetNodeAt(worldPoint);
                    ShowContextMenu(e.Location, hitNode);
                }
            }
        }

        private void UnifiedAGVCanvas_MouseMove(object sender, MouseEventArgs e)
        {
            var worldPoint = ScreenToWorld(e.Location);

            // 디버그: MouseMove 카운터 증가
            if (_isDragging)
            {
                _mouseMoveCounter++;
            }

            // 호버 노드 업데이트
            var newHoveredNode = GetNodeAt(worldPoint);
            if (newHoveredNode != _hoveredNode)
            {
                _hoveredNode = newHoveredNode;
                Invalidate();
            }

            if (_isPanning)
            {
                // 팬 처리 - 줌 레벨 보정 적용
                var deltaX = e.X - _lastMousePosition.X;
                var deltaY = e.Y - _lastMousePosition.Y;

                // 🔥 스크린 좌표 델타를 월드 좌표 델타로 변환 (줌 레벨 보정)
                // PointF로 float 정밀도 유지 (소수점 손실 방지)
                _panOffset.X += deltaX / _zoomFactor;
                _panOffset.Y += deltaY / _zoomFactor;

                _lastMousePosition = e.Location;
                Invalidate();
                Update(); // 팬도 실시간 업데이트
            }
            else if (_isDragging && _canvasMode == CanvasMode.Edit)
            {
                // 노드 드래그
                if (_selectedNode != null)
                {
                    var oldPosition = _selectedNode.Position;
                    var newPosition = new Point(
                        worldPoint.X - _dragOffset.X,
                        worldPoint.Y - _dragOffset.Y
                    );

                    // 그리드 스냅
                    if (ModifierKeys.HasFlag(Keys.Control))
                    {
                        newPosition.X = (newPosition.X / GRID_SIZE) * GRID_SIZE;
                        newPosition.Y = (newPosition.Y / GRID_SIZE) * GRID_SIZE;
                    }

                    _selectedNode.Position = newPosition;
                    NodeMoved?.Invoke(this, _selectedNode);
                    MapChanged?.Invoke(this, EventArgs.Empty);
                    Invalidate();
                    Update(); // 🔥 즉시 Paint 이벤트 처리하여 화면 업데이트
                }
            }
            else if (_isConnectionMode && _canvasMode == CanvasMode.Edit)
            {
                // 임시 연결선 업데이트
                _connectionEndPoint = worldPoint;
                Invalidate();
            }

            // 툴팁 표시 (호버된 노드/AGV 정보)
            UpdateTooltip(worldPoint);
        }

        private void UnifiedAGVCanvas_MouseUp(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Left)
            {
                if (_isDragging && _canvasMode == CanvasMode.Edit)
                {
                    _isDragging = false;
                    Capture = false; // 🔥 마우스 캡처 해제
                    Cursor = GetCursorForMode(_editMode);
                }

                if (_isPanning)
                {
                    _isPanning = false;
                    Capture = false; // 🔥 마우스 캡처 해제
                    Cursor = Cursors.Default;
                }
            }
            else if (e.Button == MouseButtons.Middle)
            {
                // 중간 버튼 드래그 종료
                if (_isPanning)
                {
                    _isPanning = false;
                    Capture = false; // 🔥 마우스 캡처 해제
                    Cursor = Cursors.Default;
                }
            }
        }

        private void UnifiedAGVCanvas_MouseWheel(object sender, MouseEventArgs e)
        {
            // 마우스 커서 위치 (스크린 좌표)
            Point mouseScreenPos = e.Location;

            // 줌 전 마우스가 가리키는 월드 좌표
            // Graphics Transform: Screen = (World + Pan) * Zoom
            // 역변환: World = Screen / Zoom - Pan
            float worldX_before = mouseScreenPos.X / _zoomFactor - _panOffset.X;
            float worldY_before = mouseScreenPos.Y / _zoomFactor - _panOffset.Y;

            // 이전 줌 팩터 저장
            float oldZoom = _zoomFactor;

            // 줌 팩터 계산 (휠 델타 기반)
            if (e.Delta > 0)
                _zoomFactor = Math.Min(_zoomFactor * 1.15f, 5.0f);   // 확대
            else
                _zoomFactor = Math.Max(_zoomFactor / 1.15f, 0.1f);   // 축소

            // 줌 후에도 마우스가 같은 월드 좌표를 가리키도록 팬 오프셋 조정
            // mouseScreen = (worldBefore + newPan) * newZoom
            // newPan = mouseScreen / newZoom - worldBefore
            _panOffset.X = (int)(mouseScreenPos.X / _zoomFactor - worldX_before);
            _panOffset.Y = (int)(mouseScreenPos.Y / _zoomFactor - worldY_before);

            Invalidate();
        }

        #endregion

        #region Mouse Helper Methods

        private Point ScreenToWorld(Point screenPoint)
        {
            // 스크린 좌표를 월드 좌표로 변환
            // Graphics Transform: Screen = (World + Pan) * Zoom
            // 역변환: World = Screen / Zoom - Pan
            float worldX = screenPoint.X / _zoomFactor - _panOffset.X;
            float worldY = screenPoint.Y / _zoomFactor - _panOffset.Y;

            return new Point((int)worldX, (int)worldY);
        }

        private Point WorldToScreen(Point worldPoint)
        {
            // 월드 좌표를 스크린 좌표로 변환
            // Graphics Transform: Screen = (World + Pan) * Zoom
            return new Point(
                (int)((worldPoint.X + _panOffset.X) * _zoomFactor),
                (int)((worldPoint.Y + _panOffset.Y) * _zoomFactor)
            );
        }

        private MapNode GetNodeAt(Point worldPoint)
        {
            if (_nodes == null) return null;

            // 역순으로 검사하여 위에 그려진 노드부터 확인
            for (int i = _nodes.Count - 1; i >= 0; i--)
            {
                var node = _nodes[i];
                if (IsPointInNode(worldPoint, node))
                    return node;
            }

            return null;
        }

        private bool IsPointInNode(Point point, MapNode node)
        {
            switch (node.Type)
            {
                case NodeType.Label:
                    return IsPointInLabelNode(point, node);
                case NodeType.Image:
                    return IsPointInImageNode(point, node);
                default:
                    return IsPointInCircularNode(point, node);
            }
        }

        private bool IsPointInCircularNode(Point point, MapNode node)
        {
            switch (node.Type)
            {
                case NodeType.Docking:
                    return IsPointInPentagon(point, node);
                case NodeType.Charging:
                    return IsPointInTriangle(point, node);
                default:
                    return IsPointInCircle(point, node);
            }
        }

        private bool IsPointInCircle(Point point, MapNode node)
        {
            // 화면에서 최소 20픽셀 정도의 히트 영역을 확보하되, 노드 크기보다 작아지지 않게 함
            var minHitRadiusInScreen = 20;
            var hitRadius = Math.Max(NODE_RADIUS, minHitRadiusInScreen / _zoomFactor);

            var distance = Math.Sqrt(
                Math.Pow(node.Position.X - point.X, 2) +
                Math.Pow(node.Position.Y - point.Y, 2)
            );
            return distance <= hitRadius;
        }

        private bool IsPointInPentagon(Point point, MapNode node)
        {
            // 화면에서 최소 20픽셀 정도의 히트 영역을 확보
            var minHitRadiusInScreen = 20;
            var radius = Math.Max(NODE_RADIUS, minHitRadiusInScreen / _zoomFactor);
            var center = node.Position;

            // 5각형 꼭짓점 계산
            var points = new Point[5];
            for (int i = 0; i < 5; i++)
            {
                var angle = (Math.PI * 2 * i / 5) - Math.PI / 2;
                points[i] = new Point(
                    (int)(center.X + radius * Math.Cos(angle)),
                    (int)(center.Y + radius * Math.Sin(angle))
                );
            }

            return IsPointInPolygon(point, points);
        }

        private bool IsPointInTriangle(Point point, MapNode node)
        {
            // 화면에서 최소 20픽셀 정도의 히트 영역을 확보하되, 노드 크기보다 작아지지 않게 함
            var minHitRadiusInScreen = 20;
            var radius = Math.Max(NODE_RADIUS, minHitRadiusInScreen / _zoomFactor);
            var center = node.Position;

            // 삼각형 꼭짓점 계산
            var points = new Point[3];
            for (int i = 0; i < 3; i++)
            {
                var angle = (Math.PI * 2 * i / 3) - Math.PI / 2;
                points[i] = new Point(
                    (int)(center.X + radius * Math.Cos(angle)),
                    (int)(center.Y + radius * Math.Sin(angle))
                );
            }

            return IsPointInPolygon(point, points);
        }

        private bool IsPointInPolygon(Point point, Point[] polygon)
        {
            // Ray casting 알고리즘 사용
            bool inside = false;
            int j = polygon.Length - 1;

            for (int i = 0; i < polygon.Length; i++)
            {
                var xi = polygon[i].X;
                var yi = polygon[i].Y;
                var xj = polygon[j].X;
                var yj = polygon[j].Y;

                if (((yi > point.Y) != (yj > point.Y)) &&
                    (point.X < (xj - xi) * (point.Y - yi) / (yj - yi) + xi))
                {
                    inside = !inside;
                }
                j = i;
            }

            return inside;
        }

        private bool IsPointInLabelNode(Point point, MapNode node)
        {
            var text = string.IsNullOrEmpty(node.LabelText) ? node.NodeId : node.LabelText;

            // 임시 Graphics로 텍스트 크기 측정
            using (var tempBitmap = new Bitmap(1, 1))
            using (var tempGraphics = Graphics.FromImage(tempBitmap))
            {
                var font = new Font(node.FontFamily, node.FontSize, node.FontStyle);
                var textSize = tempGraphics.MeasureString(text, font);

                var textRect = new Rectangle(
                    (int)(node.Position.X - textSize.Width / 2),
                    (int)(node.Position.Y - textSize.Height / 2),
                    (int)textSize.Width,
                    (int)textSize.Height
                );

                font.Dispose();
                return textRect.Contains(point);
            }
        }

        private bool IsPointInImageNode(Point point, MapNode node)
        {
            var displaySize = node.GetDisplaySize();
            if (displaySize.IsEmpty)
                displaySize = new Size(50, 50); // 기본 크기

            var imageRect = new Rectangle(
                node.Position.X - displaySize.Width / 2,
                node.Position.Y - displaySize.Height / 2,
                displaySize.Width,
                displaySize.Height
            );

            return imageRect.Contains(point);
        }

        private IAGV GetAGVAt(Point worldPoint)
        {
            if (_agvList == null) return null;

            var hitRadius = Math.Max(AGV_SIZE / 2, 15 / _zoomFactor);

            return _agvList.FirstOrDefault(agv =>
            {
                if (!_agvPositions.ContainsKey(agv.AgvId)) return false;

                var agvPos = _agvPositions[agv.AgvId];
                var distance = Math.Sqrt(
                    Math.Pow(agvPos.X - worldPoint.X, 2) +
                    Math.Pow(agvPos.Y - worldPoint.Y, 2)
                );
                return distance <= hitRadius;
            });
        }

        private void HandleSelectClick(MapNode hitNode, Point worldPoint)
        {
            if (hitNode != null)
            {
                // 노드 선택
                if (hitNode != _selectedNode)
                {
                    _selectedNode = hitNode;
                    NodeSelected?.Invoke(this, hitNode);
                    Invalidate();
                }
            }
            else
            {
                // 노드가 없으면 연결선 체크
                var connection = GetConnectionAt(worldPoint);
                if (connection != null)
                {
                    // 연결선을 클릭했을 때 삭제 확인
                    var (fromNode, toNode) = connection.Value;
                    string fromDisplay = !string.IsNullOrEmpty(fromNode.RfidId) ? fromNode.RfidId : fromNode.NodeId;
                    string toDisplay = !string.IsNullOrEmpty(toNode.RfidId) ? toNode.RfidId : toNode.NodeId;

                    var result = MessageBox.Show(
                        $"연결을 삭제하시겠습니까?\n\n{fromDisplay} ↔ {toDisplay}",
                        "연결 삭제 확인",
                        MessageBoxButtons.YesNo,
                        MessageBoxIcon.Question);

                    if (result == DialogResult.Yes)
                    {
                        // 단일 연결 삭제 (어느 방향에 저장되어 있는지 확인 후 삭제)
                        if (fromNode.ConnectedNodes.Contains(toNode.NodeId))
                        {
                            fromNode.RemoveConnection(toNode.NodeId);
                        }
                        else if (toNode.ConnectedNodes.Contains(fromNode.NodeId))
                        {
                            toNode.RemoveConnection(fromNode.NodeId);
                        }

                        // 이벤트 발생
                        ConnectionDeleted?.Invoke(this, (fromNode, toNode));
                        MapChanged?.Invoke(this, EventArgs.Empty);
                        Invalidate();
                    }
                }
                else
                {
                    // 빈 공간 클릭 시 선택 해제
                    if (_selectedNode != null)
                    {
                        _selectedNode = null;
                        NodeSelected?.Invoke(this, null);
                        Invalidate();
                    }
                }
            }
        }

        private void HandleAddNodeClick(Point worldPoint)
        {
            // 그리드 스냅
            if (ModifierKeys.HasFlag(Keys.Control))
            {
                worldPoint.X = (worldPoint.X / GRID_SIZE) * GRID_SIZE;
                worldPoint.Y = (worldPoint.Y / GRID_SIZE) * GRID_SIZE;
            }

            // 고유한 NodeId 생성
            string newNodeId = GenerateUniqueNodeId();

            var newNode = new MapNode
            {
                NodeId = newNodeId,
                Position = worldPoint,
                Type = NodeType.Normal
            };

            _nodes.Add(newNode);

            NodeAdded?.Invoke(this, newNode);
            MapChanged?.Invoke(this, EventArgs.Empty);
            Invalidate();
        }

        private void HandleAddLabelClick(Point worldPoint)
        {
            // 그리드 스냅
            if (ModifierKeys.HasFlag(Keys.Control))
            {
                worldPoint.X = (worldPoint.X / GRID_SIZE) * GRID_SIZE;
                worldPoint.Y = (worldPoint.Y / GRID_SIZE) * GRID_SIZE;
            }

            // 고유한 NodeId 생성
            string newNodeId = GenerateUniqueNodeId();

            var newNode = new MapNode
            {
                NodeId = newNodeId,
                Position = worldPoint,
                Type = NodeType.Label,
                Name = "새 라벨"
            };

            _nodes.Add(newNode);

            NodeAdded?.Invoke(this, newNode);
            MapChanged?.Invoke(this, EventArgs.Empty);
            Invalidate();
        }

        private void HandleAddImageClick(Point worldPoint)
        {
            // 그리드 스냅
            if (ModifierKeys.HasFlag(Keys.Control))
            {
                worldPoint.X = (worldPoint.X / GRID_SIZE) * GRID_SIZE;
                worldPoint.Y = (worldPoint.Y / GRID_SIZE) * GRID_SIZE;
            }

            // 고유한 NodeId 생성
            string newNodeId = GenerateUniqueNodeId();

            var newNode = new MapNode
            {
                NodeId = newNodeId,
                Position = worldPoint,
                Type = NodeType.Image,
                Name = "새 이미지"
            };

            _nodes.Add(newNode);

            NodeAdded?.Invoke(this, newNode);
            MapChanged?.Invoke(this, EventArgs.Empty);
            Invalidate();
        }

        /// <summary>
        /// 중복되지 않는 고유한 NodeId 생성
        /// </summary>
        private string GenerateUniqueNodeId()
        {
            string nodeId;
            int counter = _nodeCounter;

            do
            {
                nodeId = $"N{counter:D3}";
                counter++;
            }
            while (_nodes.Any(n => n.NodeId == nodeId));

            _nodeCounter = counter;
            return nodeId;
        }

        private void HandleConnectClick(MapNode hitNode)
        {
            if (hitNode == null) return;

            if (!_isConnectionMode)
            {
                // 연결 시작
                _isConnectionMode = true;
                _connectionStartNode = hitNode;
                _selectedNode = hitNode;
            }
            else
            {
                // 연결 완료
                if (_connectionStartNode != null && _connectionStartNode != hitNode)
                {
                    CreateConnection(_connectionStartNode, hitNode);
                }
                CancelConnection();
            }

            Invalidate();
        }

        private void HandleDeleteClick(MapNode hitNode)
        {
            if (hitNode == null) return;

            // 연결된 모든 연결선도 제거
            foreach (var node in _nodes)
            {
                node.RemoveConnection(hitNode.NodeId);
            }

            _nodes.Remove(hitNode);

            if (_selectedNode == hitNode)
                _selectedNode = null;

            NodeDeleted?.Invoke(this, hitNode);
            MapChanged?.Invoke(this, EventArgs.Empty);
            Invalidate();
        }

        private void CreateConnection(MapNode fromNode, MapNode toNode)
        {
            // 중복 연결 체크 (양방향)
            if (fromNode.ConnectedNodes.Contains(toNode.NodeId) ||
                toNode.ConnectedNodes.Contains(fromNode.NodeId))
                return;

            // 양방향 연결 생성 (AGV가 양쪽 방향으로 이동 가능하도록)
            fromNode.AddConnection(toNode.NodeId);
            toNode.AddConnection(fromNode.NodeId);

            MapChanged?.Invoke(this, EventArgs.Empty);
        }

        private float CalculateDistance(Point from, Point to)
        {
            var dx = to.X - from.X;
            var dy = to.Y - from.Y;
            return (float)Math.Sqrt(dx * dx + dy * dy);
        }

        private void ShowContextMenu(Point location, MapNode hitNode)
        {
            _contextMenu.Items.Clear();

            if (hitNode != null)
            {
                _contextMenu.Items.Add("노드 속성...", null, (s, e) => NodeSelected?.Invoke(this, hitNode));
                _contextMenu.Items.Add("노드 삭제", null, (s, e) => HandleDeleteClick(hitNode));
                _contextMenu.Items.Add("-");
            }

            _contextMenu.Items.Add("노드 추가", null, (s, e) =>
            {
                var worldPoint = ScreenToWorld(location);
                HandleAddNodeClick(worldPoint);
            });

            _contextMenu.Items.Add("전체 맞춤", null, (s, e) => FitToNodes());
            _contextMenu.Items.Add("줌 리셋", null, (s, e) => ResetZoom());

            _contextMenu.Show(this, location);
        }

        private void HandleDeleteConnectionClick(Point worldPoint)
        {
            // 클릭한 위치 근처의 연결선을 찾아 삭제
            var connection = GetConnectionAt(worldPoint);
            if (connection != null)
            {
                var (fromNode, toNode) = connection.Value;

                // 단일 연결 삭제 (어느 방향에 저장되어 있는지 확인 후 삭제)
                if (fromNode.ConnectedNodes.Contains(toNode.NodeId))
                {
                    fromNode.RemoveConnection(toNode.NodeId);
                }
                else if (toNode.ConnectedNodes.Contains(fromNode.NodeId))
                {
                    toNode.RemoveConnection(fromNode.NodeId);
                }

                // 이벤트 발생
                ConnectionDeleted?.Invoke(this, (fromNode, toNode));
                MapChanged?.Invoke(this, EventArgs.Empty);
                Invalidate();
            }
        }

        private (MapNode From, MapNode To)? GetConnectionAt(Point worldPoint)
        {
            const int CONNECTION_HIT_TOLERANCE = 10;

            // 모든 연결선을 확인하여 클릭한 위치와 가장 가까운 연결선 찾기
            foreach (var fromNode in _nodes)
            {
                foreach (var toNodeId in fromNode.ConnectedNodes)
                {
                    var toNode = _nodes.FirstOrDefault(n => n.NodeId == toNodeId);
                    if (toNode != null)
                    {
                        // 연결선과 클릭 위치 간의 거리 계산
                        var distance = CalculatePointToLineDistance(worldPoint, fromNode.Position, toNode.Position);
                        if (distance <= CONNECTION_HIT_TOLERANCE / _zoomFactor)
                        {
                            return (fromNode, toNode);
                        }
                    }
                }
            }

            return null;
        }

        private float CalculatePointToLineDistance(Point point, Point lineStart, Point lineEnd)
        {
            // 점에서 선분까지의 거리 계산
            var A = point.X - lineStart.X;
            var B = point.Y - lineStart.Y;
            var C = lineEnd.X - lineStart.X;
            var D = lineEnd.Y - lineStart.Y;

            var dot = A * C + B * D;
            var lenSq = C * C + D * D;

            if (lenSq == 0) return CalculateDistance(point, lineStart);

            var param = dot / lenSq;

            Point xx, yy;
            if (param < 0)
            {
                xx = lineStart;
                yy = lineStart;
            }
            else if (param > 1)
            {
                xx = lineEnd;
                yy = lineEnd;
            }
            else
            {
                xx = new Point((int)(lineStart.X + param * C), (int)(lineStart.Y + param * D));
                yy = xx;
            }

            return CalculateDistance(point, xx);
        }

        private void UpdateTooltip(Point worldPoint)
        {
            string tooltipText = "";

            // 노드 툴팁
            var hitNode = GetNodeAt(worldPoint);
            if (hitNode != null)
            {
                tooltipText = $"노드: {hitNode.NodeId}\n타입: {hitNode.Type}\n위치: ({hitNode.Position.X}, {hitNode.Position.Y})";
            }
            else
            {
                // AGV 툴팁
                var hitAGV = GetAGVAt(worldPoint);
                if (hitAGV != null)
                {
                    var state = _agvStates.ContainsKey(hitAGV.AgvId) ? _agvStates[hitAGV.AgvId] : AGVState.Idle;
                    tooltipText = $"AGV: {hitAGV.AgvId}\n상태: {state}\n배터리: {hitAGV.BatteryLevel:F1}%\n위치: ({hitAGV.CurrentPosition.X}, {hitAGV.CurrentPosition.Y})";
                }
            }

            // 툴팁 업데이트 (기존 ToolTip 컨트롤 사용)
            if (!string.IsNullOrEmpty(tooltipText))
            {
                // ToolTip 설정 (필요시 추가 구현)
            }
        }

        #endregion

        #region View Control Methods

        /// <summary>
        /// 모든 노드가 보이도록 뷰 조정
        /// </summary>
        public void FitToNodes()
        {
            if (_nodes == null || _nodes.Count == 0) return;

            var minX = _nodes.Min(n => n.Position.X);
            var maxX = _nodes.Max(n => n.Position.X);
            var minY = _nodes.Min(n => n.Position.Y);
            var maxY = _nodes.Max(n => n.Position.Y);

            var margin = 50;
            var contentWidth = maxX - minX + margin * 2;
            var contentHeight = maxY - minY + margin * 2;

            var zoomX = (float)Width / contentWidth;
            var zoomY = (float)Height / contentHeight;
            _zoomFactor = Math.Min(zoomX, zoomY) * 0.9f;

            var centerX = (minX + maxX) / 2;
            var centerY = (minY + maxY) / 2;

            // Graphics Transform: Screen = (World + Pan) * Zoom
            // 중앙에 위치: Width/2 = (centerX + Pan) * Zoom
            // Pan = Width/2 / Zoom - centerX
            _panOffset.X = (int)(Width / 2 / _zoomFactor - centerX);
            _panOffset.Y = (int)(Height / 2 / _zoomFactor - centerY);

            Invalidate();
        }

        /// <summary>
        /// 줌 리셋
        /// </summary>
        public void ResetZoom()
        {
            _zoomFactor = 1.0f;
            _panOffset = PointF.Empty;
            Invalidate();
        }

        /// <summary>
        /// 특정 위치로 이동
        /// </summary>
        public void PanTo(Point worldPoint)
        {
            // Graphics Transform: Screen = (World + Pan) * Zoom
            // 중앙에 위치: Width/2 = (worldPoint + Pan) * Zoom
            // Pan = Width/2 / Zoom - worldPoint
            _panOffset.X = (int)(Width / 2 / _zoomFactor - worldPoint.X);
            _panOffset.Y = (int)(Height / 2 / _zoomFactor - worldPoint.Y);
            Invalidate();
        }

        /// <summary>
        /// 특정 노드로 이동
        /// </summary>
        public void PanToNode(string nodeId)
        {
            var node = _nodes?.FirstOrDefault(n => n.NodeId == nodeId);
            if (node != null)
            {
                PanTo(node.Position);
            }
        }

        /// <summary>
        /// 특정 AGV로 이동
        /// </summary>
        public void PanToAGV(string agvId)
        {
            if (_agvPositions.ContainsKey(agvId))
            {
                PanTo(_agvPositions[agvId]);
            }
        }

        #endregion
    }
}