import React, { useRef, useEffect, useState, useCallback } from 'react';
import { SimulationMap, AgvState, ToolType, MapNode, MapEdge, MagnetLine, AgvMotionState, NodeType, FloorMark } from '../types';
import { AGV_WIDTH, AGV_HEIGHT, SENSOR_OFFSET_FRONT, SENSOR_OFFSET_REAR, SENSOR_OFFSET_MARK, MAX_SPEED, TURN_SPEED, MARK_SEARCH_SPEED, SPEED_L, SPEED_M, SPEED_H, MAGNET_WIDTH, MAGNET_COLOR, GRID_SIZE } from '../constants';
import { Trash2, X, Move } from 'lucide-react';

interface SimulationCanvasProps {
  activeTool: ToolType;
  mapData: SimulationMap;
  setMapData: (data: SimulationMap | ((prev: SimulationMap) => SimulationMap)) => void;
  agvState: AgvState;
  setAgvState: (state: AgvState | ((prev: AgvState) => AgvState)) => void;
  onLog: (msg: string) => void;
}

interface HitObject {
  type: 'NODE' | 'EDGE' | 'MAGNET' | 'MARK';
  id: string;
  name: string; // For display
  dist: number;
}

const SimulationCanvas: React.FC<SimulationCanvasProps> = ({ activeTool, mapData, setMapData, agvState, setAgvState, onLog }) => {
  const canvasRef = useRef<HTMLCanvasElement>(null);
  const containerRef = useRef<HTMLDivElement>(null);
  const [dimensions, setDimensions] = useState({ width: 800, height: 600 });
  
  // --- View Transform (Zoom/Pan) ---
  const viewRef = useRef({ x: 0, y: 0, scale: 1 });
  const [isPanning, setIsPanning] = useState(false);
  const isSpacePressedRef = useRef(false);

  // Interaction State
  const [dragStartPos, setDragStartPos] = useState<{x:number, y:number} | null>(null);
  const [startNodeId, setStartNodeId] = useState<string | null>(null);
  
  // Selection & Editing State
  const [selectedItemId, setSelectedItemId] = useState<string | null>(null);
  // dragHandle: 'p1'|'p2'|'control' for magnets, 'mark_p1'|'mark_p2' for marks
  const [dragHandle, setDragHandle] = useState<string | null>(null);

  // Move/Drag State for Select Tool (Whole Object)
  const [dragTarget, setDragTarget] = useState<{
    type: 'AGV' | 'NODE' | 'MAGNET' | 'MARK';
    id?: string;
    startMouse: { x: number, y: number };
    initialObjState: any;
  } | null>(null);
  
  // Curve Drawing State
  const [curvePhase, setCurvePhase] = useState<0 | 1 | 2>(0);
  const [tempMagnet, setTempMagnet] = useState<Partial<MagnetLine> | null>(null);

  // Eraser Context Menu State
  const [contextMenu, setContextMenu] = useState<{ x: number, y: number, items: HitObject[] } | null>(null);

  // Image Cache
  const imageCacheRef = useRef<Record<string, HTMLImageElement>>({});

  // Use ref for mouse position (Screen Coords)
  const mouseScreenPosRef = useRef<{ x: number, y: number }>({ x: 0, y: 0 });
  // Use ref for mouse position (World Coords)
  const mouseWorldPosRef = useRef<{ x: number, y: number }>({ x: 0, y: 0 });

  // Refs for loop access
  const mapDataRef = useRef(mapData);
  const agvStateRef = useRef(agvState);
  
  // Track previous sensor position for intersection checking
  const prevMarkSensorPosRef = useRef<{ x: number, y: number } | null>(null);
  // Track the ID of the magnet currently being followed to prevent jumping
  const lastMagnetIdRef = useRef<string | null>(null);
  // Throttle log
  const logThrottleRef = useRef(0);

  // Sync refs
  useEffect(() => { mapDataRef.current = mapData; }, [mapData]);
  useEffect(() => { agvStateRef.current = agvState; }, [agvState]);

  // Handle Resize
  useEffect(() => {
    const updateSize = () => {
        if (containerRef.current) {
            setDimensions({
                width: containerRef.current.clientWidth,
                height: containerRef.current.clientHeight
            });
        }
    };

    updateSize();
    const observer = new ResizeObserver(updateSize);
    if (containerRef.current) {
        observer.observe(containerRef.current);
    }

    return () => observer.disconnect();
  }, []);

  // Keyboard Event for Spacebar Panning
  useEffect(() => {
    const handleKeyDown = (e: KeyboardEvent) => {
      if (e.code === 'Space') isSpacePressedRef.current = true;
      if (e.key === 'Escape') {
          setSelectedItemId(null);
          setDragHandle(null);
          setDragTarget(null);
          setContextMenu(null);
      }
    };
    const handleKeyUp = (e: KeyboardEvent) => {
      if (e.code === 'Space') isSpacePressedRef.current = false;
    };
    window.addEventListener('keydown', handleKeyDown);
    window.addEventListener('keyup', handleKeyUp);
    return () => {
      window.removeEventListener('keydown', handleKeyDown);
      window.removeEventListener('keyup', handleKeyUp);
    };
  }, []);

  // Reset tool states when tool changes
  useEffect(() => {
    setCurvePhase(0);
    setTempMagnet(null);
    setDragStartPos(null);
    setStartNodeId(null);
    setContextMenu(null);
    setDragTarget(null);
    setDragHandle(null);
    // Note: We might want to keep selection when switching tools, but often resetting is safer
    if (activeTool !== ToolType.SELECT) setSelectedItemId(null);
  }, [activeTool]);

  // Preload Images
  useEffect(() => {
      const cache = imageCacheRef.current;
      mapData.nodes.forEach(node => {
          if (node.type === NodeType.Image && node.imageBase64 && !cache[node.id]) {
              const img = new Image();
              let src = node.imageBase64;
              if (!src.startsWith('data:image')) {
                  src = `data:image/png;base64,${src}`;
              }
              img.src = src;
              cache[node.id] = img;
          }
      });
  }, [mapData]);

  // --- Coordinate Transformation Helpers ---
  const screenToWorld = (screenX: number, screenY: number) => {
    const t = viewRef.current;
    return {
      x: (screenX - t.x) / t.scale,
      y: (screenY - t.y) / t.scale
    };
  };

  const worldToScreen = (worldX: number, worldY: number) => {
    const t = viewRef.current;
    return {
      x: worldX * t.scale + t.x,
      y: worldY * t.scale + t.y
    };
  };

  // --- Physics Helper Functions ---

  const rotatePoint = (px: number, py: number, cx: number, cy: number, angleDeg: number) => {
    const rad = (angleDeg * Math.PI) / 180;
    const cos = Math.cos(rad);
    const sin = Math.sin(rad);
    const dx = px - cx;
    const dy = py - cy;
    return {
      x: cx + (dx * cos - dy * sin),
      y: cy + (dx * sin + dy * cos),
    };
  };

  const getDistance = (p1: {x:number, y:number}, p2: {x:number, y:number}) => {
    return Math.sqrt(Math.pow(p2.x - p1.x, 2) + Math.pow(p2.y - p1.y, 2));
  };

  // Distance from point P to line segment VW
  const distanceToSegment = (p: {x:number, y:number}, v: {x:number, y:number}, w: {x:number, y:number}) => {
    const l2 = Math.pow(getDistance(v, w), 2);
    if (l2 === 0) return getDistance(p, v);
    let t = ((p.x - v.x) * (w.x - v.x) + (p.y - v.y) * (w.y - v.y)) / l2;
    t = Math.max(0, Math.min(1, t));
    const projection = { x: v.x + t * (w.x - v.x), y: v.y + t * (w.y - v.y) };
    return getDistance(p, projection);
  };

  // Get closest point on segment
  const getClosestPointOnSegment = (p: {x:number, y:number}, v: {x:number, y:number}, w: {x:number, y:number}) => {
    const l2 = Math.pow(getDistance(v, w), 2);
    if (l2 === 0) return v;
    let t = ((p.x - v.x) * (w.x - v.x) + (p.y - v.y) * (w.y - v.y)) / l2;
    t = Math.max(0, Math.min(1, t));
    return { x: v.x + t * (w.x - v.x), y: v.y + t * (w.y - v.y) };
  };

  // Quadratic Bezier Point Calculation
  const getBezierPoint = (t: number, p0: {x:number, y:number}, p1: {x:number, y:number}, p2: {x:number, y:number}) => {
    const u = 1 - t;
    const tt = t * t;
    const uu = u * u;
    
    return {
        x: (uu * p0.x) + (2 * u * t * p1.x) + (tt * p2.x),
        y: (uu * p0.y) + (2 * u * t * p1.y) + (tt * p2.y)
    };
  };

  // Calculate derivative (tangent vector) at t for Quadratic Bezier
  const getBezierDerivative = (t: number, p0: {x:number, y:number}, p1: {x:number, y:number}, p2: {x:number, y:number}) => {
      const u = 1 - t;
      // B'(t) = 2(1-t)(P1 - P0) + 2t(P2 - P1)
      const dx = 2 * u * (p1.x - p0.x) + 2 * t * (p2.x - p1.x);
      const dy = 2 * u * (p1.y - p0.y) + 2 * t * (p2.y - p1.y);
      return { x: dx, y: dy };
  };

  // Approximate distance to a Quadratic Bezier Curve by subdividing it
  // Now uses 50 segments for higher resolution to prevent "warping"
  const distanceToBezier = (p: {x:number, y:number}, p0: {x:number, y:number}, p1: {x:number, y:number}, p2: {x:number, y:number}, segments = 50) => {
    let minDist = Number.MAX_VALUE;
    let prevPoint = p0;
    let closestProj = p0;
    let bestT = 0;

    // Scan segments to find approximate closest point
    for (let i = 1; i <= segments; i++) {
        const t = i / segments;
        const currPoint = getBezierPoint(t, p0, p1, p2);
        const dist = distanceToSegment(p, prevPoint, currPoint);
        
        if (dist < minDist) {
            minDist = dist;
            // Get linear projection on this segment to approximate t
            const proj = getClosestPointOnSegment(p, prevPoint, currPoint);
            closestProj = proj;
            
            // Approximate t at projection (Lerp)
            const segLen = getDistance(prevPoint, currPoint);
            const distOnSeg = getDistance(prevPoint, proj);
            const tStep = 1 / segments;
            const tStart = (i - 1) / segments;
            bestT = tStart + (distOnSeg / segLen) * tStep;
        }
        prevPoint = currPoint;
    }

    // Calculate Exact Tangent using Derivative at bestT
    const deriv = getBezierDerivative(bestT, p0, p1, p2);
    const tangentAngle = Math.atan2(deriv.y, deriv.x) * (180 / Math.PI);

    return { dist: minDist, proj: closestProj, angle: tangentAngle };
  };

  const normalizeAngle = (angle: number) => {
      let a = angle % 360;
      if (a > 180) a -= 360;
      if (a <= -180) a += 360;
      return a;
  };

  const doSegmentsIntersect = (p1: {x:number, y:number}, p2: {x:number, y:number}, p3: {x:number, y:number}, p4: {x:number, y:number}) => {
    const ccw = (a:{x:number,y:number}, b:{x:number,y:number}, c:{x:number,y:number}) => (c.y - a.y) * (b.x - a.x) > (b.y - a.y) * (c.x - a.x);
    return (ccw(p1, p2, p3) !== ccw(p1, p2, p4)) && (ccw(p3, p4, p1) !== ccw(p3, p4, p2));
  };

  // Helper to find closest magnet and its angle
  const getClosestMagnetInfo = (x: number, y: number) => {
      const map = mapDataRef.current;
      const p = { x, y };
      let closestMag = null;
      let minDist = 30; // Search radius
      let angle = 0;

      map.magnets.forEach(mag => {
          let dist = 999;
          let magAngle = 0;

          if (mag.type === 'STRAIGHT') {
              dist = distanceToSegment(p, mag.p1, mag.p2);
              magAngle = Math.atan2(mag.p2.y - mag.p1.y, mag.p2.x - mag.p1.x) * (180/Math.PI);
          } else if (mag.type === 'CURVE' && mag.controlPoint) {
              const res = distanceToBezier(p, mag.p1, mag.controlPoint, mag.p2);
              dist = res.dist;
              magAngle = res.angle;
          }

          if (dist < minDist) {
              minDist = dist;
              closestMag = mag;
              angle = magAngle;
          }
      });
      return { magnet: closestMag, angle };
  };

  // --- Hit Test Logic ---
  const getObjectsAt = (x: number, y: number, tolerance = 15): HitObject[] => {
      const hits: HitObject[] = [];
      const map = mapDataRef.current;
      const p = { x, y };
      
      const worldTolerance = tolerance / viewRef.current.scale;
      
      // Type Priority Map: Smaller number = Higher Priority
      const typePriority: Record<string, number> = {
          'NODE': 1,
          'MARK': 2,
          'EDGE': 3,
          'MAGNET': 4
      };

      // 1. Nodes
      map.nodes.forEach(node => {
          if (getDistance(p, node) < worldTolerance) {
              hits.push({ type: 'NODE', id: node.id, name: node.name || node.id, dist: getDistance(p, node) });
          }
      });

      // 2. Marks (Calculate as segment for better hit detection)
      map.marks.forEach(mark => {
          // Visual width is 50px (-25 to 25). 
          const rad = (mark.rotation * Math.PI) / 180;
          const dx = Math.cos(rad) * 25; 
          const dy = Math.sin(rad) * 25;
          const p1 = { x: mark.x - dx, y: mark.y - dy };
          const p2 = { x: mark.x + dx, y: mark.y + dy };
          
          const dist = distanceToSegment(p, p1, p2);

          if (dist < worldTolerance) {
              hits.push({ type: 'MARK', id: mark.id, name: 'Mark', dist });
          }
      });

      // 3. Magnets
      map.magnets.forEach(mag => {
          let dist = 999;
          if (mag.type === 'STRAIGHT') {
              dist = distanceToSegment(p, mag.p1, mag.p2);
          } else if (mag.type === 'CURVE' && mag.controlPoint) {
              dist = distanceToBezier(p, mag.p1, mag.controlPoint, mag.p2).dist;
          }
          if (dist < worldTolerance) {
              hits.push({ type: 'MAGNET', id: mag.id, name: `Magnet (${mag.type})`, dist });
          }
      });

      // 4. Edges
      map.edges.forEach(edge => {
          const start = map.nodes.find(n => n.id === edge.from);
          const end = map.nodes.find(n => n.id === edge.to);
          if (start && end) {
              const dist = distanceToSegment(p, start, end);
              if (dist < worldTolerance / 2) { 
                   hits.push({ type: 'EDGE', id: edge.id, name: `Line ${edge.from}-${edge.to}`, dist });
              }
          }
      });

      // Sort by Priority first, then Distance
      return hits.sort((a,b) => {
          const prioDiff = typePriority[a.type] - typePriority[b.type];
          if (prioDiff !== 0) return prioDiff;
          return a.dist - b.dist;
      });
  };

  const deleteObject = (item: HitObject) => {
      if (item.id === selectedItemId) setSelectedItemId(null); // Clear selection if deleted
      setMapData(prev => {
          const next = { ...prev };
          if (item.type === 'NODE') {
              next.nodes = prev.nodes.filter(n => n.id !== item.id);
              next.edges = prev.edges.filter(e => e.from !== item.id && e.to !== item.id);
          } else if (item.type === 'MARK') {
              next.marks = prev.marks.filter(m => m.id !== item.id);
          } else if (item.type === 'MAGNET') {
              next.magnets = prev.magnets.filter(m => m.id !== item.id);
          } else if (item.type === 'EDGE') {
              next.edges = prev.edges.filter(e => e.id !== item.id);
          }
          return next;
      });
      setContextMenu(null);
  };

  // --- Main Simulation Loop ---
  useEffect(() => {
    let animationFrameId: number;

    const updatePhysics = () => {
      const state = { ...agvStateRef.current };
      const map = mapDataRef.current;
      let hasChanged = false;
      logThrottleRef.current++;

      // Update sensors
      const frontSensor = rotatePoint(
        state.x + SENSOR_OFFSET_FRONT.x, 
        state.y + SENSOR_OFFSET_FRONT.y, 
        state.x, state.y, state.rotation
      );
      const rearSensor = rotatePoint(
        state.x + SENSOR_OFFSET_REAR.x, 
        state.y + SENSOR_OFFSET_REAR.y, 
        state.x, state.y, state.rotation
      );
      const markSensor = rotatePoint(
        state.x + SENSOR_OFFSET_MARK.x, 
        state.y + SENSOR_OFFSET_MARK.y, 
        state.x, state.y, state.rotation
      );

      if (!prevMarkSensorPosRef.current) prevMarkSensorPosRef.current = markSensor;

      let lineFrontDetected = false;
      let lineRearDetected = false;
      
      // FIXED: Very tight threshold to ensure the sensor must physically be ON the line
      // Reduced from 12 to 8 to prevent "grazing" false positives during tight turns
      const DETECTION_THRESHOLD = 8; 
      const SEARCH_RADIUS = 30;       

      // 1. Check Strict Sensor Status (for UI/State)
      map.magnets.forEach(mag => {
          let distFront = 999;
          let distRear = 999;

          if (mag.type === 'STRAIGHT') {
              distFront = distanceToSegment(frontSensor, mag.p1, mag.p2);
              distRear = distanceToSegment(rearSensor, mag.p1, mag.p2);
          } else if (mag.type === 'CURVE' && mag.controlPoint) {
              distFront = distanceToBezier(frontSensor, mag.p1, mag.controlPoint, mag.p2).dist;
              distRear = distanceToBezier(rearSensor, mag.p1, mag.controlPoint, mag.p2).dist;
          }

          if (distFront < DETECTION_THRESHOLD) lineFrontDetected = true;
          if (distRear < DETECTION_THRESHOLD) lineRearDetected = true;
      });

      // Handle Line Out Logic
      const isAutoMove = state.motionState === AgvMotionState.RUNNING || state.motionState === AgvMotionState.MARK_STOPPING;
      
      if (state.motionState !== AgvMotionState.IDLE && !state.error) {
        hasChanged = true;
        let moveSpeed = state.speed;
        
        if (state.motionState === AgvMotionState.RUNNING) {
             moveSpeed = state.runConfig.speedLevel === 'L' ? SPEED_L : state.runConfig.speedLevel === 'M' ? SPEED_M : SPEED_H;
        } else if (state.motionState === AgvMotionState.MARK_STOPPING) {
            moveSpeed = Math.min(state.speed, MARK_SEARCH_SPEED); 
        }

        // --- LINE TRACING ---
        if (isAutoMove) {
            const isFwd = state.runConfig.direction === 'FWD';
            const sensorPos = isFwd ? frontSensor : rearSensor;

            let bestMagnet: { mag: MagnetLine, dist: number, targetAngle: number, proj: {x:number, y:number} } | null = null;
            
            // Collect all candidate lines
            const potentialLines: { mag: MagnetLine, dist: number, proj: {x:number, y:number}, targetAngle: number, angleDiff: number, category: string, isSticky: boolean, isBehind: boolean, isConnected: boolean, isSharpTurn: boolean }[] = [];

            map.magnets.forEach(mag => {
                let dist = 999;
                let proj = { x: 0, y: 0 };
                let tangentAngle = 0;

                if (mag.type === 'STRAIGHT') {
                    dist = distanceToSegment(sensorPos, mag.p1, mag.p2);
                    proj = getClosestPointOnSegment(sensorPos, mag.p1, mag.p2);
                    tangentAngle = Math.atan2(mag.p2.y - mag.p1.y, mag.p2.x - mag.p1.x) * (180/Math.PI);
                } else if (mag.type === 'CURVE' && mag.controlPoint) {
                    const res = distanceToBezier(sensorPos, mag.p1, mag.controlPoint, mag.p2);
                    dist = res.dist;
                    proj = res.proj;
                    tangentAngle = res.angle;
                }

                if (dist < SEARCH_RADIUS) {
                    let heading = state.rotation;
                    if (!isFwd) heading += 180;
                    heading = normalizeAngle(heading);

                    // Align tangent to heading for basic diff calc
                    let alignedTangent = tangentAngle;
                    if (mag.type === 'STRAIGHT') {
                        let diff = normalizeAngle(tangentAngle - heading);
                        if (Math.abs(diff) > 90) alignedTangent = normalizeAngle(tangentAngle + 180);
                    } else {
                        let diff = normalizeAngle(tangentAngle - heading);
                        if (Math.abs(diff) > 90) alignedTangent = normalizeAngle(tangentAngle + 180);
                    }
                    
                    let diff = normalizeAngle(alignedTangent - heading);

                    // --- Node-based Lookahead / Category ---
                    let comparisonAngle = alignedTangent; 
                    const d1 = getDistance(sensorPos, mag.p1);
                    const d2 = getDistance(sensorPos, mag.p2);
                    const farEnd = d1 > d2 ? mag.p1 : mag.p2; // Heuristic for segment end
                    const targetNode = map.nodes.find(n => getDistance(n, farEnd) < 40);

                    if (targetNode) {
                        const nodeAngle = Math.atan2(targetNode.y - sensorPos.y, targetNode.x - sensorPos.x) * (180 / Math.PI);
                        comparisonAngle = normalizeAngle(nodeAngle);
                    }
                    
                    let finalDiff = normalizeAngle(comparisonAngle - heading);
                    let category = 'STRAIGHT';
                    if (finalDiff < -45) category = 'LEFT';
                    else if (finalDiff > 45) category = 'RIGHT';

                    // --- Behind Check ---
                    const dx = proj.x - sensorPos.x;
                    const dy = proj.y - sensorPos.y;
                    const hRad = (heading * Math.PI) / 180;
                    const hx = Math.cos(hRad);
                    const hy = Math.sin(hRad);
                    
                    let isSticky = lastMagnetIdRef.current === mag.id;

                    // Release Sticky if End of Line Reached
                    if (isSticky) {
                         const distToP1 = getDistance(sensorPos, mag.p1);
                         const distToP2 = getDistance(sensorPos, mag.p2);
                         if (distToP1 < 25 || distToP2 < 25) {
                             isSticky = false;
                         }
                    }

                    // Dot product check
                    const behindThreshold = isSticky ? -25 : -5;
                    const isBehind = (dx * hx + dy * hy) < behindThreshold;

                    // --- STRICT CONNECTIVITY CHECK ---
                    const distToEndpoint = Math.min(getDistance(sensorPos, mag.p1), getDistance(sensorPos, mag.p2));
                    const isConnected = distToEndpoint < 30; 

                    // --- PHYSICAL CONSTRAINT (SHARP TURN) ---
                    // If deflection is > 35 degrees (Internal angle < 145), it is too sharp for normal line tracking
                    // This forces the AGV to ignore 90-degree junctions unless there is a curve magnet
                    const isSharpTurn = Math.abs(diff) > 35;

                    potentialLines.push({ mag, dist, proj, targetAngle: alignedTangent, angleDiff: diff, category, isSticky, isBehind, isConnected, isSharpTurn });
                }
            });

            // Priority Sort
            const branchMode = state.runConfig.branch; 

            potentialLines.sort((a, b) => {
                // 1. Behind Check (Absolute disqualifier usually)
                if (a.isBehind !== b.isBehind) return a.isBehind ? 1 : -1;

                // 2. PHYSICAL CONSTRAINT: Disqualify Sharp Turns
                // A sharp turn is physically impossible in motion, so we deprioritize it heavily.
                if (a.isSharpTurn !== b.isSharpTurn) return a.isSharpTurn ? 1 : -1;

                const aValid = a.isSticky || a.isConnected;
                const bValid = b.isSticky || b.isConnected;
                
                // 3. Validity Check: We prefer lines that are physically anchored here
                if (aValid !== bValid) return aValid ? -1 : 1;

                // 4. Branch Matching (Only among valid lines)
                if (aValid && bValid) {
                    const aMatch = a.category === branchMode;
                    const bMatch = b.category === branchMode;
                    if (aMatch !== bMatch) return aMatch ? -1 : 1;
                }

                // 5. Stickiness (Stability bias)
                if (a.isSticky !== b.isSticky) return a.isSticky ? -1 : 1;

                // 6. Connectivity (Prefer line starts over mid-lines)
                if (a.isConnected !== b.isConnected) return a.isConnected ? -1 : 1;

                // 7. Distance
                return a.dist - b.dist;
            });

            // Debug Logging (Throttled ~ 30 frames)
            if (logThrottleRef.current % 30 === 0 && isAutoMove) {
                 const logMsg = `Trk: found=${potentialLines.length} | ` + 
                     potentialLines.map(p => `[${p.mag.id.substring(0,4)} D:${Math.round(p.dist)} S:${p.isSticky ? 'Y' : 'N'} M:${p.category === branchMode ? 'Y':'N'} !${p.isSharpTurn ? 'SHARP' : 'OK'}]`).join(' ');
                 onLog(logMsg);
            }

            if (potentialLines.length > 0) {
                bestMagnet = potentialLines[0];
                lastMagnetIdRef.current = bestMagnet.mag.id;
            }

            // *** LINE OUT CHECK INTEGRATED HERE ***
            // Only error out if NO valid lines are found in search radius
            if (!bestMagnet && isAutoMove) {
                 state.motionState = AgvMotionState.IDLE;
                 state.error = 'LINE_OUT';
                 hasChanged = true;
                 onLog(`STOPPED: LINE_OUT. Sensors: F=${lineFrontDetected}, R=${lineRearDetected}`);
            } else if (bestMagnet) {
                // Adaptive Steering
                const angleError = normalizeAngle(bestMagnet.targetAngle - (isFwd ? state.rotation : state.rotation + 180));
                
                // Sharp turn handling - Reduced aggression to prevent jitter
                const steerFactor = Math.abs(angleError) > 20 ? 0.1 : 0.05;
                state.rotation += angleError * steerFactor;

                const driftX = bestMagnet.proj.x - sensorPos.x;
                const driftY = bestMagnet.proj.y - sensorPos.y;
                
                // Position correction (Damping + Clamping)
                // This prevents "Warping" when switching to lines that are found far away (e.g. 60px)
                const driftFactor = 0.1; // Reduced from 0.3 for smoother path following
                let moveX = driftX * driftFactor;
                let moveY = driftY * driftFactor;

                // CLAMP: Max lateral snap per frame to prevent teleportation
                const MAX_SNAP = 2.0; 
                const moveDist = Math.sqrt(moveX*moveX + moveY*moveY);
                if (moveDist > MAX_SNAP) {
                    const ratio = MAX_SNAP / moveDist;
                    moveX *= ratio;
                    moveY *= ratio;
                }

                state.x += moveX;
                state.y += moveY;
                
                if (bestMagnet.mag.type === 'CURVE') {
                     // Extra rotation correction for curves is now handled better by Derivative angle
                     // But we keep a small correctional factor for drift
                     const newSensorX = sensorPos.x + driftX;
                     const newSensorY = sensorPos.y + driftY;
                     const otherSensor = isFwd ? rearSensor : frontSensor;
                     const dx = newSensorX - otherSensor.x;
                     const dy = newSensorY - otherSensor.y;
                     let newAngle = Math.atan2(dy, dx) * (180/Math.PI);
                     if (!isFwd) newAngle += 180;
                     let diff = normalizeAngle(newAngle - state.rotation);
                     state.rotation += diff * 0.1; // Reduced significantly as derivative is more accurate
                }
                
                // Apply Movement
                const rad = (state.rotation * Math.PI) / 180;
                const dirMult = isFwd ? 1 : -1;
                state.x += Math.cos(rad) * moveSpeed * dirMult;
                state.y += Math.sin(rad) * moveSpeed * dirMult;
            }

        } else {
            // Manual Movements (unchanged)
            const rad = (state.rotation * Math.PI) / 180;
            if (state.motionState === AgvMotionState.FORWARD) {
                 state.x += Math.cos(rad) * moveSpeed;
                 state.y += Math.sin(rad) * moveSpeed;
            } else if (state.motionState === AgvMotionState.BACKWARD) {
                 state.x -= Math.cos(rad) * moveSpeed;
                 state.y -= Math.sin(rad) * moveSpeed;
            } else if (state.motionState === AgvMotionState.TURN_LEFT) state.rotation -= TURN_SPEED;
            else if (state.motionState === AgvMotionState.TURN_RIGHT) state.rotation += TURN_SPEED;
            else if (state.motionState === AgvMotionState.TURN_LEFT_180) {
                 state.rotation -= TURN_SPEED;
                 // 180 Turn Logic (L-Turn = CCW):
                 // Stop when REAR sensor detects a new line (e.g. 90 degree cross), but ignore first ~60 degrees to clear original line.
                 
                 const startRot = state.targetRotation !== null ? state.targetRotation + 180 : state.rotation;
                 const degreesTurned = startRot - state.rotation; // Positive increasing
                 
                 const ignoreSensors = degreesTurned < 60; // Increased from 40 to 60 to prevent early false detection
                 
                 // L-Turn: Check REAR sensor for stop condition
                 const stopCondition = !ignoreSensors && lineRearDetected;
                 const reachedLimit = (state.targetRotation !== null && state.rotation <= state.targetRotation);

                 // Debug Log
                 if (logThrottleRef.current % 10 === 0) {
                     onLog(`Turn180 [L]: Deg=${degreesTurned.toFixed(1)}/180 Ignore=${ignoreSensors ? 'Y':'N'} RearLine=${lineRearDetected ? 'YES':'NO'}`);
                 }

                 if (stopCondition || reachedLimit) {
                     if (reachedLimit && state.targetRotation !== null) state.rotation = state.targetRotation;
                     state.motionState = AgvMotionState.IDLE; state.targetRotation = null;
                     onLog(`Turn180 [L] Complete. Reason: ${stopCondition ? 'Rear Sensor Hit' : 'Angle Reached'}`);
                 }
            } else if (state.motionState === AgvMotionState.TURN_RIGHT_180) {
                 state.rotation += TURN_SPEED;
                 // 180 Turn Logic (R-Turn = CW):
                 // Stop when FRONT sensor detects a new line, but ignore first ~60 degrees.

                 const startRot = state.targetRotation !== null ? state.targetRotation - 180 : state.rotation;
                 const degreesTurned = state.rotation - startRot; // Positive increasing

                 const ignoreSensors = degreesTurned < 60; // Increased from 40 to 60
                 
                 // R-Turn: Check FRONT sensor for stop condition
                 const stopCondition = !ignoreSensors && lineFrontDetected;
                 const reachedLimit = (state.targetRotation !== null && state.rotation >= state.targetRotation);

                 // Debug Log
                 if (logThrottleRef.current % 10 === 0) {
                     onLog(`Turn180 [R]: Deg=${degreesTurned.toFixed(1)}/180 Ignore=${ignoreSensors ? 'Y':'N'} FrontLine=${lineFrontDetected ? 'YES':'NO'}`);
                 }

                 if (stopCondition || reachedLimit) {
                     if (reachedLimit && state.targetRotation !== null) state.rotation = state.targetRotation;
                     state.motionState = AgvMotionState.IDLE; state.targetRotation = null;
                     onLog(`Turn180 [R] Complete. Reason: ${stopCondition ? 'Front Sensor Hit' : 'Angle Reached'}`);
                 }
            }
        }
      }

      state.sensorLineFront = lineFrontDetected;
      state.sensorLineRear = lineRearDetected;

      let rfidFound: string | null = null;
      map.nodes.forEach(node => { if (node.rfidId && getDistance({x: state.x, y: state.y}, node) < 15) rfidFound = node.rfidId; });
      state.detectedRfid = rfidFound;

      let markFound = false;
      const prevSensorPos = prevMarkSensorPosRef.current;
      if (prevSensorPos) {
        map.marks.forEach(mark => {
            const rad = (mark.rotation * Math.PI) / 180;
            const dx = Math.cos(rad) * 25; const dy = Math.sin(rad) * 25;
            const ms = { x: mark.x - dx, y: mark.y - dy }; const me = { x: mark.x + dx, y: mark.y + dy };
            
            // Check 1: Dynamic Crossing (Intersection)
            if (doSegmentsIntersect(prevSensorPos, markSensor, ms, me)) {
                markFound = true;
            }

            // Check 2: Static Proximity (Distance to line segment)
            // This ensures sensor stays ON when sitting on the mark
            if (!markFound) {
                 const distToMarkLine = distanceToSegment(markSensor, ms, me);
                 // If within 10px of the mark tape (tape width is technically line width but visually 3px, sensor range is wider)
                 if (distToMarkLine < 10) { 
                     markFound = true; 
                 }
            }
        });
      }
      prevMarkSensorPosRef.current = { x: markSensor.x, y: markSensor.y };
      state.sensorMark = markFound;
      if (markFound && state.motionState === AgvMotionState.MARK_STOPPING) { state.motionState = AgvMotionState.IDLE; hasChanged = true; }

      if (hasChanged || state.sensorLineFront !== agvStateRef.current.sensorLineFront || state.sensorMark !== agvStateRef.current.sensorMark || state.error !== agvStateRef.current.error || state.rotation !== agvStateRef.current.rotation) {
         agvStateRef.current = state;
         setAgvState(state);
      }
    };

    const draw = () => {
      const canvas = canvasRef.current;
      if (!canvas) return;
      const ctx = canvas.getContext('2d');
      if (!ctx) return;

      // Clear Screen
      ctx.clearRect(0, 0, canvas.width, canvas.height);

      // --- Apply Zoom & Pan ---
      const t = viewRef.current;
      ctx.save();
      ctx.translate(t.x, t.y);
      ctx.scale(t.scale, t.scale);

      // --- Draw Infinite Grid ---
      const visibleLeft = -t.x / t.scale;
      const visibleTop = -t.y / t.scale;
      const visibleRight = (canvas.width - t.x) / t.scale;
      const visibleBottom = (canvas.height - t.y) / t.scale;

      const startX = Math.floor(visibleLeft / GRID_SIZE) * GRID_SIZE;
      const endX = Math.floor(visibleRight / GRID_SIZE) * GRID_SIZE + GRID_SIZE;
      const startY = Math.floor(visibleTop / GRID_SIZE) * GRID_SIZE;
      const endY = Math.floor(visibleBottom / GRID_SIZE) * GRID_SIZE + GRID_SIZE;

      ctx.strokeStyle = '#374151';
      ctx.lineWidth = 1;
      ctx.beginPath();
      // Verticals
      for (let x = startX; x <= endX; x += GRID_SIZE) { 
          ctx.moveTo(x, startY); ctx.lineTo(x, endY); 
      }
      // Horizontals
      for (let y = startY; y <= endY; y += GRID_SIZE) { 
          ctx.moveTo(startX, y); ctx.lineTo(endX, y); 
      }
      ctx.stroke();

      const map = mapDataRef.current;
      
      // 1. Logical Edges (Thin Gray)
      ctx.strokeStyle = '#4B5563'; 
      ctx.lineWidth = 1;
      ctx.setLineDash([2, 2]);
      map.edges.forEach(edge => {
        const start = map.nodes.find(n => n.id === edge.from);
        const end = map.nodes.find(n => n.id === edge.to);
        if (start && end) {
          ctx.beginPath(); ctx.moveTo(start.x, start.y); ctx.lineTo(end.x, end.y); ctx.stroke();
        }
      });
      ctx.setLineDash([]);

      // 2. Magnet Lines (Wide, Transparent Blue)
      ctx.lineWidth = MAGNET_WIDTH;
      ctx.lineCap = 'round';
      map.magnets.forEach(mag => {
          const isSelected = mag.id === selectedItemId;
          ctx.strokeStyle = isSelected ? 'rgba(59, 130, 246, 0.8)' : MAGNET_COLOR; // Brighter if selected
          
          ctx.beginPath();
          ctx.moveTo(mag.p1.x, mag.p1.y);
          if (mag.type === 'STRAIGHT') {
              ctx.lineTo(mag.p2.x, mag.p2.y);
          } else if (mag.type === 'CURVE' && mag.controlPoint) {
              ctx.quadraticCurveTo(mag.controlPoint.x, mag.controlPoint.y, mag.p2.x, mag.p2.y);
          }
          ctx.stroke();

          // Draw Controls for Selected Curve
          if (isSelected && mag.type === 'CURVE' && mag.controlPoint) {
              // Helper Lines
              ctx.strokeStyle = 'rgba(255, 255, 0, 0.5)';
              ctx.lineWidth = 1;
              ctx.setLineDash([3, 3]);
              ctx.beginPath();
              ctx.moveTo(mag.p1.x, mag.p1.y);
              ctx.lineTo(mag.controlPoint.x, mag.controlPoint.y);
              ctx.lineTo(mag.p2.x, mag.p2.y);
              ctx.stroke();
              ctx.setLineDash([]);

              // Control Points
              const drawHandle = (p: {x:number, y:number}, color: string) => {
                  ctx.fillStyle = color;
                  ctx.beginPath(); ctx.arc(p.x, p.y, 5 / t.scale, 0, Math.PI*2); ctx.fill(); // Scale handle size
                  ctx.strokeStyle = '#FFF'; ctx.lineWidth = 1; ctx.stroke();
              };

              drawHandle(mag.p1, '#3B82F6'); // Start (Blue)
              drawHandle(mag.p2, '#3B82F6'); // End (Blue)
              drawHandle(mag.controlPoint, '#EAB308'); // Control (Yellow)
          } else if (isSelected && mag.type === 'STRAIGHT') {
              // Simple handles for straight lines
              const drawHandle = (p: {x:number, y:number}, color: string) => {
                  ctx.fillStyle = color;
                  ctx.beginPath(); ctx.arc(p.x, p.y, 5 / t.scale, 0, Math.PI*2); ctx.fill(); 
                  ctx.strokeStyle = '#FFF'; ctx.lineWidth = 1; ctx.stroke();
              };
              drawHandle(mag.p1, '#3B82F6');
              drawHandle(mag.p2, '#3B82F6');
          }
      });

      // Nodes (Unchanged)
      const images = imageCacheRef.current;
      map.nodes.forEach(node => {
        if (node.type === NodeType.Image && images[node.id]) {
            const img = images[node.id];
            if (img.complete) {
                const w = 100; const h = (img.height / img.width) * w;
                ctx.drawImage(img, node.x - w/2, node.y - h/2, w, h);
            }
            return;
        }
        if (node.type === NodeType.Label && node.labelText) {
            ctx.font = 'bold 20px Arial';
            const tm = ctx.measureText(node.labelText);
            const w = tm.width + 20; const h = 30;
            if (node.backColor !== 'Transparent') { ctx.fillStyle = node.backColor!; ctx.fillRect(node.x - w/2, node.y - h/2, w, h); }
            ctx.fillStyle = node.foreColor || 'White'; ctx.textAlign = 'center'; ctx.textBaseline = 'middle'; ctx.fillText(node.labelText, node.x, node.y);
            return; 
        }
        let color = '#4B5563';
        if (node.type === NodeType.Loader || node.type === NodeType.UnLoader) color = 'green';
        else if (node.type === NodeType.Charging || node.type === NodeType.ChargerStation) color = 'magenta';
        else if (node.type === NodeType.Buffer) color = 'green';
        else if (node.displayColor) color = node.displayColor;
        
        // Highlight Selected Node
        if (node.id === selectedItemId) {
            ctx.strokeStyle = '#FDE047'; ctx.lineWidth = 2;
            ctx.strokeRect(node.x - 10, node.y - 10, 20, 20);
        }

        ctx.fillStyle = color;
        if (node.type !== NodeType.Normal) ctx.fillRect(node.x - 6, node.y - 6, 12, 12);
        else { ctx.beginPath(); ctx.arc(node.x, node.y, 4, 0, Math.PI * 2); ctx.fill(); }
        ctx.textAlign = 'center'; ctx.textBaseline = 'alphabetic'; ctx.font = '10px monospace'; ctx.fillStyle = '#9CA3AF'; 
        const topText = node.rfidId ? `[${node.rfidId}]` : node.id; ctx.fillText(topText, node.x, node.y - 10);
        if (node.name) { ctx.font = 'bold 10px Arial'; ctx.fillStyle = '#FFFFFF'; ctx.fillText(node.name, node.x, node.y + 18); }
      });

      // Marks
      ctx.strokeStyle = '#FCD34D'; ctx.lineWidth = 3;
      map.marks.forEach(mark => {
        if (mark.id === selectedItemId) {
            ctx.shadowColor = '#FDE047'; ctx.shadowBlur = 10;
        } else {
            ctx.shadowBlur = 0;
        }
        ctx.save(); ctx.translate(mark.x, mark.y); ctx.rotate((mark.rotation * Math.PI) / 180);
        ctx.beginPath(); ctx.moveTo(-25, 0); ctx.lineTo(25, 0); ctx.stroke(); ctx.restore();
        ctx.shadowBlur = 0;
        
        // Draw handles if selected
        if (mark.id === selectedItemId) {
            
            // Re-apply transform for simple drawing in local space
            ctx.save(); ctx.translate(mark.x, mark.y); ctx.rotate((mark.rotation * Math.PI) / 180);
            
            // Handles
            const drawHandle = (x: number, y: number) => {
                ctx.fillStyle = '#EAB308';
                ctx.beginPath(); ctx.arc(x, y, 4 / t.scale, 0, Math.PI*2); ctx.fill(); 
                ctx.strokeStyle = '#FFF'; ctx.lineWidth = 1; ctx.stroke();
            };
            drawHandle(-25, 0); // Start
            drawHandle(25, 0);  // End
            
            ctx.restore();
        }
      });

      // Drawing Helpers
      
      // -- Straight Magnet --
      if (activeTool === ToolType.DRAW_MAGNET_STRAIGHT && dragStartPos) {
          ctx.strokeStyle = 'rgba(255, 255, 255, 0.5)';
          ctx.lineWidth = MAGNET_WIDTH;
          ctx.beginPath();
          ctx.moveTo(dragStartPos.x, dragStartPos.y);
          
          // Use snapped mouse pos for preview
          const curr = getSnappedPos(mouseWorldPosRef.current.x, mouseWorldPosRef.current.y);
          ctx.lineTo(curr.x, curr.y);
          ctx.stroke();
      }
      
      // -- Logical Line --
      if (activeTool === ToolType.DRAW_LINE && dragStartPos) {
          ctx.lineWidth = 1; ctx.strokeStyle = '#60A5FA'; ctx.setLineDash([5, 5]);
          ctx.beginPath();
          ctx.moveTo(dragStartPos.x, dragStartPos.y);
          const curr = getSnappedPos(mouseWorldPosRef.current.x, mouseWorldPosRef.current.y);
          ctx.lineTo(curr.x, curr.y);
          ctx.stroke();
          ctx.setLineDash([]);
      }

      // -- Curve Magnet (3-Step Drawing) --
      if (activeTool === ToolType.DRAW_MAGNET_CURVE && tempMagnet) {
          const p1 = tempMagnet.p1!;
          const p2 = tempMagnet.p2 || getSnappedPos(mouseWorldPosRef.current.x, mouseWorldPosRef.current.y);
          
          // Visualize Construction
          ctx.strokeStyle = 'rgba(255, 255, 255, 0.5)';
          ctx.lineWidth = 1;
          ctx.setLineDash([2, 2]);
          ctx.beginPath();
          ctx.moveTo(p1.x, p1.y);
          
          let control = tempMagnet.controlPoint;
          if (curvePhase === 1) {
              // In phase 1, control is midpoint of straight line
              control = { x: (p1.x + p2.x)/2, y: (p1.y + p2.y)/2 };
              ctx.lineTo(p2.x, p2.y);
          } else if (curvePhase === 2) {
              // In phase 2, control follows mouse (no snapping for control point usually feels smoother, but can apply if needed)
              // Let's use raw mouse for control point smooth dragging
              control = mouseWorldPosRef.current;
              ctx.lineTo(control.x, control.y);
              ctx.lineTo(p2.x, p2.y);
          }
          ctx.stroke();
          ctx.setLineDash([]);

          // Control Point Handle
          if (control) {
            ctx.fillStyle = '#60A5FA';
            ctx.beginPath(); ctx.arc(control.x, control.y, 4, 0, Math.PI * 2); ctx.fill();
          }

          // Render Final Curve Preview
          ctx.strokeStyle = 'rgba(255, 255, 255, 0.7)';
          ctx.lineWidth = MAGNET_WIDTH;
          ctx.beginPath();
          ctx.moveTo(p1.x, p1.y);
          if (control) {
             ctx.quadraticCurveTo(control.x, control.y, p2.x, p2.y);
          } else {
             ctx.lineTo(p2.x, p2.y);
          }
          ctx.stroke();
      }

      // AGV Drawing (Same as before)
      const state = agvStateRef.current;
      ctx.save(); ctx.translate(state.x, state.y); ctx.rotate((state.rotation * Math.PI) / 180);
      
      // Draw LiDAR Scanner if active (Directional: Front or Rear based on Motor Direction)
      if (state.lidarEnabled) {
          const isFwd = state.runConfig.direction === 'FWD';
          const sensorPos = isFwd ? SENSOR_OFFSET_FRONT : SENSOR_OFFSET_REAR;
          
          ctx.save();
          // Arc Scanner
          ctx.fillStyle = 'rgba(6, 182, 212, 0.2)'; // Cyan transparent
          ctx.strokeStyle = 'rgba(6, 182, 212, 0.5)';
          ctx.lineWidth = 1;
          ctx.beginPath();
          
          const baseAngle = isFwd ? 0 : Math.PI; // 0 for Front, 180 for Rear
          
          ctx.moveTo(sensorPos.x, sensorPos.y); 
          ctx.arc(sensorPos.x, sensorPos.y, 60, baseAngle - Math.PI/6, baseAngle + Math.PI/6); // +/- 30 degrees
          ctx.closePath();
          ctx.fill();
          ctx.stroke();
          
          // Scanning line animation
          const now = Date.now();
          const scanOffset = Math.sin(now / 200) * (Math.PI/6);
          const currentAngle = baseAngle + scanOffset;
          
          ctx.beginPath();
          ctx.moveTo(sensorPos.x, sensorPos.y);
          ctx.lineTo(sensorPos.x + Math.cos(currentAngle)*60, sensorPos.y + Math.sin(currentAngle)*60);
          ctx.strokeStyle = 'rgba(255, 255, 255, 0.5)';
          ctx.stroke();
          
          ctx.restore();
      }

      ctx.fillStyle = '#1F2937'; ctx.strokeStyle = '#3B82F6'; ctx.lineWidth = 2;
      ctx.fillRect(-AGV_WIDTH / 2, -AGV_HEIGHT / 2, AGV_WIDTH, AGV_HEIGHT); ctx.strokeRect(-AGV_WIDTH / 2, -AGV_HEIGHT / 2, AGV_WIDTH, AGV_HEIGHT);
      if (state.error) { ctx.fillStyle = '#EF4444'; ctx.beginPath(); ctx.arc(0, 0, 15, 0, Math.PI * 2); ctx.fill(); ctx.fillStyle = '#FFF'; ctx.font = 'bold 8px Arial'; ctx.textAlign = 'center'; ctx.textBaseline = 'middle'; ctx.fillText('ERR', 0, 0); }
      ctx.fillStyle = '#000'; ctx.fillRect(-AGV_WIDTH / 2 - 4, -10, 4, 20); ctx.fillRect(AGV_WIDTH / 2, -10, 4, 20);
      ctx.fillStyle = '#10B981'; ctx.fillRect(AGV_HEIGHT / 2 - 10, -AGV_WIDTH / 2 + 5, 10, AGV_WIDTH - 10);
      ctx.fillStyle = '#EF4444'; ctx.fillRect(-AGV_HEIGHT / 2 + 5, -AGV_WIDTH / 2 + 10, 15, 20);
      ctx.fillStyle = '#FFF'; ctx.font = '10px Arial'; ctx.textBaseline = 'alphabetic'; ctx.fillText(`Z:${state.liftHeight}`, -AGV_HEIGHT / 2 + 12, -AGV_WIDTH / 2 + 25);
      
      // Draw Status Info Panel (Rear)
      ctx.save();
      ctx.translate(-12, 0); // Move towards rear center
      
      // Panel Background
      ctx.fillStyle = 'rgba(0, 0, 0, 0.6)';
      ctx.strokeStyle = '#374151';
      ctx.lineWidth = 1;
      ctx.fillRect(-10, -18, 20, 36);
      ctx.strokeRect(-10, -18, 20, 36);

      // Speed (Top)
      const spd = state.runConfig.speedLevel;
      ctx.fillStyle = spd === 'H' ? '#F87171' : spd === 'M' ? '#FACC15' : '#4ADE80'; // Red/Yellow/Green
      ctx.font = 'bold 12px Arial';
      ctx.textAlign = 'center';
      ctx.textBaseline = 'middle';
      ctx.fillText(spd, 0, -8); 

      // Divider
      ctx.strokeStyle = '#4B5563';
      ctx.beginPath(); ctx.moveTo(-8, 0); ctx.lineTo(8, 0); ctx.stroke();

      // Branch (Bottom)
      ctx.fillStyle = '#FFF';
      const br = state.runConfig.branch;
      if (br === 'LEFT') {
          ctx.save(); ctx.translate(0, 10); ctx.rotate(-Math.PI/2); ctx.fillText('➜', 0, 0); ctx.restore();
      } else if (br === 'RIGHT') {
          ctx.save(); ctx.translate(0, 10); ctx.rotate(Math.PI/2); ctx.fillText('➜', 0, 0); ctx.restore();
      } else {
          ctx.fillText('➜', 0, 10);
      }
      ctx.restore();

      // Draw Magnet Key Icon if ON (Shifted forward to avoid status panel)
      if (state.magnetOn) {
          ctx.save();
          ctx.translate(15, -6); // Position shifted forward
          ctx.fillStyle = '#FACC15'; // Yellow/Gold
          ctx.strokeStyle = '#FACC15';
          ctx.lineWidth = 1.5;
          
          // Key Head (Circle)
          ctx.beginPath();
          ctx.arc(0, -3, 3.5, 0, Math.PI*2);
          ctx.stroke();
          // Key Hole
          ctx.beginPath(); ctx.arc(0, -3, 1, 0, Math.PI*2); ctx.fill();

          // Key Shaft
          ctx.beginPath();
          ctx.moveTo(0, 0.5);
          ctx.lineTo(0, 8);
          
          // Key Teeth
          ctx.moveTo(0, 4); ctx.lineTo(3, 4);
          ctx.moveTo(0, 6.5); ctx.lineTo(3, 6.5);
          ctx.stroke();
          ctx.restore();
      }

      // Sensors
      ctx.fillStyle = state.sensorLineFront ? '#F87171' : '#4B5563'; ctx.beginPath(); ctx.arc(SENSOR_OFFSET_FRONT.x, SENSOR_OFFSET_FRONT.y, 3, 0, Math.PI * 2); ctx.fill();
      ctx.fillStyle = state.sensorLineRear ? '#F87171' : '#4B5563'; ctx.beginPath(); ctx.arc(SENSOR_OFFSET_REAR.x, SENSOR_OFFSET_REAR.y, 3, 0, Math.PI * 2); ctx.fill();
      ctx.fillStyle = state.sensorMark ? '#FFFF00' : '#4B5563'; ctx.beginPath(); ctx.arc(SENSOR_OFFSET_MARK.x, SENSOR_OFFSET_MARK.y, state.sensorMark ? 6 : 4, 0, Math.PI * 2); ctx.fill(); if(state.sensorMark) { ctx.strokeStyle='#FFF'; ctx.stroke(); }
      
      if (state.motionState === AgvMotionState.RUNNING) {
          const activeOffset = state.runConfig.direction === 'FWD' ? SENSOR_OFFSET_FRONT : SENSOR_OFFSET_REAR;
          ctx.strokeStyle = '#00FF00'; ctx.lineWidth = 2; ctx.beginPath(); ctx.arc(activeOffset.x, activeOffset.y, 6, 0, Math.PI * 2); ctx.stroke();
      }
      ctx.strokeStyle = '#FFF'; ctx.beginPath(); ctx.moveTo(-10, 0); ctx.lineTo(10, 0); ctx.lineTo(5, -5); ctx.moveTo(10, 0); ctx.lineTo(5, 5); ctx.stroke();
      ctx.restore();

      // Restore Transform
      ctx.restore();

      animationFrameId = requestAnimationFrame(draw);
    };

    const interval = setInterval(updatePhysics, 16);
    animationFrameId = requestAnimationFrame(draw);
    return () => { clearInterval(interval); cancelAnimationFrame(animationFrameId); };
  }, [activeTool, dragStartPos, tempMagnet, curvePhase, selectedItemId, dimensions]); // Re-draw when dimensions change

  // --- Interaction Handlers ---

  const snapToGrid = (val: number) => Math.round(val / (GRID_SIZE / 2)) * (GRID_SIZE / 2); // Snap to half-grid

  // Intelligent Snapping: Prefer Nodes, else Raw Position
  const getSnappedPos = (rawX: number, rawY: number) => {
    const map = mapDataRef.current;
    const SNAP_DIST = 20;

    // 1. Try snapping to Nodes
    let closestNode: MapNode | null = null;
    let minDist = SNAP_DIST;

    map.nodes.forEach(node => {
        const d = Math.sqrt(Math.pow(node.x - rawX, 2) + Math.pow(node.y - rawY, 2));
        if (d < minDist) {
            minDist = d;
            closestNode = node;
        }
    });

    if (closestNode) {
        return { x: closestNode.x, y: closestNode.y };
    }

    // 2. Fallback to Raw (No Grid Snap as per user request)
    return { x: rawX, y: rawY };
  };

  const handleWheel = (e: React.WheelEvent) => {
    e.preventDefault();
    const rect = canvasRef.current?.getBoundingClientRect();
    if (!rect) return;
    
    const screenX = e.clientX - rect.left;
    const screenY = e.clientY - rect.top;
    
    // Zoom Logic
    const zoomIntensity = 0.1;
    const delta = e.deltaY < 0 ? 1 : -1;
    let newScale = viewRef.current.scale + delta * zoomIntensity;
    newScale = Math.min(Math.max(0.2, newScale), 5); // Limit zoom

    // World pos before zoom
    const worldX = (screenX - viewRef.current.x) / viewRef.current.scale;
    const worldY = (screenY - viewRef.current.y) / viewRef.current.scale;

    // Adjust offset so world pos remains at screen pos
    const newX = screenX - worldX * newScale;
    const newY = screenY - worldY * newScale;

    viewRef.current = { x: newX, y: newY, scale: newScale };
  };

  const handleMouseMove = (e: React.MouseEvent) => {
    const rect = canvasRef.current?.getBoundingClientRect();
    if (!rect) return;
    const screenX = e.clientX - rect.left;
    const screenY = e.clientY - rect.top;

    mouseScreenPosRef.current = { x: screenX, y: screenY };
    const worldPos = screenToWorld(screenX, screenY);
    mouseWorldPosRef.current = worldPos;

    // Pan
    if (isPanning) {
        viewRef.current = {
            ...viewRef.current,
            x: viewRef.current.x + e.movementX,
            y: viewRef.current.y + e.movementY
        };
        return;
    }

    // Drag Handle (Editing)
    if (dragHandle && selectedItemId) {
        const snapped = getSnappedPos(worldPos.x, worldPos.y);
        setMapData(prev => {
            const next = { ...prev };
            if (dragHandle === 'p1' || dragHandle === 'p2' || dragHandle === 'control') {
                const magIndex = next.magnets.findIndex(m => m.id === selectedItemId);
                if (magIndex !== -1) {
                    const newMags = [...next.magnets];
                    const mag = { ...newMags[magIndex] };
                    if (dragHandle === 'p1') mag.p1 = snapped;
                    else if (dragHandle === 'p2') mag.p2 = snapped;
                    else if (dragHandle === 'control') mag.controlPoint = worldPos; // No snap for control
                    newMags[magIndex] = mag;
                    next.magnets = newMags;
                }
            } else if (dragHandle === 'mark_p1' || dragHandle === 'mark_p2') {
                 // Rotate Mark
                 const markIndex = next.marks.findIndex(m => m.id === selectedItemId);
                 if (markIndex !== -1) {
                     const newMarks = [...next.marks];
                     const mark = { ...newMarks[markIndex] };
                     const dx = worldPos.x - mark.x;
                     const dy = worldPos.y - mark.y;
                     let angle = Math.atan2(dy, dx) * (180 / Math.PI);
                     if (dragHandle === 'mark_p1') angle += 180;
                     mark.rotation = normalizeAngle(angle);
                     newMarks[markIndex] = mark;
                     next.marks = newMarks;
                 }
            }
            return next;
        });
        return;
    }

    // Drag Object (Moving)
    if (dragTarget && dragTarget.initialObjState) {
        const dx = worldPos.x - dragTarget.startMouse.x;
        const dy = worldPos.y - dragTarget.startMouse.y;
        
        if (dragTarget.type === 'AGV') {
            setAgvState(prev => ({
                ...prev,
                x: dragTarget.initialObjState.x + dx,
                y: dragTarget.initialObjState.y + dy
            }));
        } else if (dragTarget.type === 'NODE' && dragTarget.id) {
             const snapped = getSnappedPos(dragTarget.initialObjState.x + dx, dragTarget.initialObjState.y + dy);
             setMapData(prev => ({
                 ...prev,
                 nodes: prev.nodes.map(n => n.id === dragTarget.id ? { ...n, x: snapped.x, y: snapped.y } : n)
             }));
        } else if (dragTarget.type === 'MARK' && dragTarget.id) {
             setMapData(prev => ({
                 ...prev,
                 marks: prev.marks.map(m => m.id === dragTarget.id ? { ...m, x: dragTarget.initialObjState.x + dx, y: dragTarget.initialObjState.y + dy } : m)
             }));
        } else if (dragTarget.type === 'MAGNET' && dragTarget.id) {
             const m0 = dragTarget.initialObjState as MagnetLine;
             const dxw = worldPos.x - dragTarget.startMouse.x; // World delta
             const dyw = worldPos.y - dragTarget.startMouse.y;
             
             setMapData(prev => ({
                 ...prev,
                 magnets: prev.magnets.map(m => {
                     if (m.id === dragTarget.id) {
                         const newM = { ...m };
                         newM.p1 = { x: m0.p1.x + dxw, y: m0.p1.y + dyw };
                         newM.p2 = { x: m0.p2.x + dxw, y: m0.p2.y + dyw };
                         if (m0.controlPoint) newM.controlPoint = { x: m0.controlPoint.x + dxw, y: m0.controlPoint.y + dyw };
                         return newM;
                     }
                     return m;
                 })
             }));
        }
        return;
    }
  };

  const handleMouseUp = (e: React.MouseEvent) => {
    setIsPanning(false);
    setDragHandle(null);
    setDragTarget(null);

    // Finish DRAW_LINE
    if (activeTool === ToolType.DRAW_LINE && dragStartPos && startNodeId) {
        const { x, y } = mouseWorldPosRef.current;
        const existingNode = mapData.nodes.find(n => getDistance(n, {x, y}) < 20);
        if (existingNode && existingNode.id !== startNodeId) {
            const edgeExists = mapData.edges.some(e => 
                (e.from === startNodeId && e.to === existingNode.id) || 
                (e.from === existingNode.id && e.to === startNodeId)
            );
            if (!edgeExists) {
                const newEdge: MapEdge = {
                    id: crypto.randomUUID(),
                    from: startNodeId,
                    to: existingNode.id
                };
                setMapData(prev => ({ ...prev, edges: [...prev.edges, newEdge] }));
            }
        }
    }

    // Finish DRAW_MAGNET_STRAIGHT
    if (activeTool === ToolType.DRAW_MAGNET_STRAIGHT && dragStartPos) {
        const p1 = dragStartPos;
        const p2 = getSnappedPos(mouseWorldPosRef.current.x, mouseWorldPosRef.current.y);
        if (getDistance(p1, p2) > 5) {
            const newMag: MagnetLine = {
                id: crypto.randomUUID(),
                type: 'STRAIGHT',
                p1,
                p2
            };
            setMapData(prev => ({ ...prev, magnets: [...prev.magnets, newMag] }));
        }
    }
    
    setDragStartPos(null);
    setStartNodeId(null);
  };

  const handleMouseDown = (e: React.MouseEvent) => {
    if (contextMenu) {
        setContextMenu(null);
        return;
    }

    const rect = canvasRef.current?.getBoundingClientRect();
    if (!rect) return;
    const screenX = e.clientX - rect.left;
    const screenY = e.clientY - rect.top;
    
    if (e.button === 1 || (e.button === 0 && isSpacePressedRef.current)) {
        setIsPanning(true);
        return;
    }

    const { x, y } = screenToWorld(screenX, screenY);
    const worldPos = { x, y };

    if (activeTool === ToolType.ERASER) {
        const hits = getObjectsAt(x, y);
        if (hits.length === 1) {
            deleteObject(hits[0]);
        } else if (hits.length > 1) {
            setContextMenu({ x: screenX, y: screenY, items: hits });
        }
        return;
    }

    if (activeTool === ToolType.SELECT) {
        // Check handles
        const worldTolerance = 15 / viewRef.current.scale;
        
        if (selectedItemId) {
            const selectedMag = mapData.magnets.find(m => m.id === selectedItemId);
            if (selectedMag) {
                if (getDistance(worldPos, selectedMag.p1) < worldTolerance) { setDragHandle('p1'); return; }
                if (getDistance(worldPos, selectedMag.p2) < worldTolerance) { setDragHandle('p2'); return; }
                if (selectedMag.type === 'CURVE' && selectedMag.controlPoint && getDistance(worldPos, selectedMag.controlPoint) < worldTolerance) { setDragHandle('control'); return; }
            }
            const selectedMark = mapData.marks.find(m => m.id === selectedItemId);
            if (selectedMark) {
                 const rad = (selectedMark.rotation * Math.PI) / 180;
                 const dx = Math.cos(rad) * 25; const dy = Math.sin(rad) * 25;
                 const p1 = { x: selectedMark.x - dx, y: selectedMark.y - dy };
                 const p2 = { x: selectedMark.x + dx, y: selectedMark.y + dy };
                 if (getDistance(worldPos, p1) < worldTolerance) { setDragHandle('mark_p1'); return; }
                 if (getDistance(worldPos, p2) < worldTolerance) { setDragHandle('mark_p2'); return; }
            }
        }
        
        // AGV
        const agvDist = getDistance(worldPos, agvState);
        if (agvDist < 30) {
            setDragTarget({ type: 'AGV', startMouse: worldPos, initialObjState: { x: agvState.x, y: agvState.y } });
            setSelectedItemId(null);
            return;
        }

        // Map Objects
        const hits = getObjectsAt(x, y);
        const movableHits = hits.filter(h => h.type !== 'EDGE');
        if (movableHits.length > 0) {
            const hit = movableHits[0];
            setSelectedItemId(hit.id);
            let initialObjState = null;
            if (hit.type === 'NODE') {
                 const n = mapData.nodes.find(o => o.id === hit.id);
                 if (n) initialObjState = { x: n.x, y: n.y };
            } else if (hit.type === 'MARK') {
                 const m = mapData.marks.find(o => o.id === hit.id);
                 if (m) initialObjState = { x: m.x, y: m.y };
            } else if (hit.type === 'MAGNET') {
                 const m = mapData.magnets.find(o => o.id === hit.id);
                 if (m) initialObjState = JSON.parse(JSON.stringify(m)); 
            }
            if (initialObjState) {
                setDragTarget({ type: hit.type as any, id: hit.id, startMouse: worldPos, initialObjState });
            }
        } else {
            setSelectedItemId(null);
        }
        return;
    }

    if (activeTool === ToolType.DRAW_MAGNET_CURVE) {
        if (curvePhase === 0) {
            const start = getSnappedPos(x, y);
            setTempMagnet({ id: crypto.randomUUID(), type: 'CURVE', p1: start, p2: start, controlPoint: start });
            setCurvePhase(1);
        } else if (curvePhase === 2) {
             // Finish curve
             if (tempMagnet && tempMagnet.p1 && tempMagnet.p2) {
                const finalMagnet: MagnetLine = {
                    id: tempMagnet.id!,
                    type: 'CURVE',
                    p1: tempMagnet.p1,
                    p2: tempMagnet.p2,
                    controlPoint: mouseWorldPosRef.current 
                };
                setMapData(prev => ({ ...prev, magnets: [...prev.magnets, finalMagnet] }));
                setTempMagnet(null);
                setCurvePhase(0);
            }
        }
        return;
    }

    if (activeTool === ToolType.DRAW_LINE) {
        const hits = getObjectsAt(x, y);
        const nodeHit = hits.find(h => h.type === 'NODE');
        if (nodeHit) {
             const n = mapData.nodes.find(n => n.id === nodeHit.id);
             if (n) {
                 setDragStartPos({ x: n.x, y: n.y });
                 setStartNodeId(n.id);
             }
        } else {
             const snapped = getSnappedPos(x, y);
             const newNode: MapNode = {
                 id: `N${Date.now()}`,
                 x: snapped.x, y: snapped.y,
                 type: 0,
                 name: "",
                 rfidId: "",
                 connectedNodes: []
             };
             setMapData(prev => ({ ...prev, nodes: [...prev.nodes, newNode] }));
        }
        return;
    }
    
    if (activeTool === ToolType.DRAW_MAGNET_STRAIGHT) {
        const snapped = getSnappedPos(x, y);
        setDragStartPos(snapped);
        return;
    }

    if (activeTool === ToolType.ADD_MARK) {
        const snapped = getSnappedPos(x, y);
        const newMark: FloorMark = {
            id: crypto.randomUUID(),
            x: snapped.x,
            y: snapped.y,
            rotation: 0
        };
        setMapData(prev => ({ ...prev, marks: [...prev.marks, newMark] }));
        return;
    }

    if (activeTool === ToolType.ADD_RFID) {
        const hits = getObjectsAt(x, y);
        const nodeHit = hits.find(h => h.type === 'NODE');
        if (nodeHit) {
            const rfid = prompt("Enter RFID ID:", "12345");
            if (rfid) {
                setMapData(prev => ({
                    ...prev,
                    nodes: prev.nodes.map(n => n.id === nodeHit.id ? { ...n, rfidId: rfid } : n)
                }));
            }
        }
    }
  };

  return (
    <div ref={containerRef} className={`absolute inset-0 bg-gray-900 overflow-hidden ${isPanning ? 'cursor-grabbing' : 'cursor-crosshair'}`} tabIndex={0}>
      <canvas
        ref={canvasRef}
        width={dimensions.width}
        height={dimensions.height}
        onWheel={handleWheel}
        onMouseDown={handleMouseDown}
        onMouseMove={handleMouseMove}
        onMouseUp={handleMouseUp}
        onMouseLeave={() => setIsPanning(false)}
        className="block bg-gray-950 outline-none w-full h-full"
      />
      
      {/* HUD Info */}
      <div className="absolute top-2 left-2 pointer-events-none text-xs text-gray-500 bg-gray-900/50 p-1 rounded backdrop-blur-sm">
        Pos: {Math.round(agvState.x)}, {Math.round(agvState.y)} | Rot: {Math.round(agvState.rotation)}°
        <span className="ml-2 text-gray-400">| Zoom: {Math.round(viewRef.current.scale * 100)}%</span>
        {activeTool === ToolType.DRAW_MAGNET_CURVE && curvePhase === 1 && <span className="text-blue-400 ml-2">Drag to set Endpoint</span>}
        {activeTool === ToolType.DRAW_MAGNET_CURVE && curvePhase === 2 && <span className="text-yellow-400 ml-2">Move mouse to bend curve, Click to finish</span>}
        <div className="mt-1 flex items-center gap-1 text-[10px] opacity-70">
            <Move size={10} /> <span>Middle Click / Space+Drag to Pan</span>
            {selectedItemId && <span className="text-yellow-500 font-bold ml-2">Object Selected (Drag points to edit)</span>}
        </div>
      </div>

      {/* Context Menu for Eraser */}
      {contextMenu && (
          <div 
            className="absolute z-10 bg-gray-800 border border-gray-600 rounded shadow-lg p-1 min-w-[150px]"
            style={{ left: contextMenu.x, top: contextMenu.y }}
          >
              <div className="flex items-center justify-between px-2 py-1 border-b border-gray-700 text-xs font-bold text-gray-400">
                  <span>Select object to delete</span>
                  <button onClick={() => setContextMenu(null)} className="hover:text-white"><X size={12}/></button>
              </div>
              <ul className="text-xs">
                  {contextMenu.items.map((item, idx) => (
                      <li 
                        key={`${item.type}-${item.id}-${idx}`}
                        onClick={() => deleteObject(item)}
                        className="px-2 py-1.5 hover:bg-red-900/50 hover:text-red-200 cursor-pointer flex items-center gap-2"
                      >
                          <Trash2 size={12} />
                          <span>{item.name}</span>
                          <span className="text-[10px] text-gray-500 ml-auto">{Math.round(item.dist)}px</span>
                      </li>
                  ))}
              </ul>
          </div>
      )}
    </div>
  );
};

export default SimulationCanvas;