import { useCallback, useEffect, useRef, useState } from "react";
import OpenSeadragon from "openseadragon";

export interface Annotation {
  id: string;
  type: "rectangle" | "polygon" | "ellipse" | "brush";
  points: Array<{ x: number; y: number }>;
  color: string;
  label: string;
  completed: boolean;
}

interface AnnotationCanvasProps {
  viewer: OpenSeadragon.Viewer | null;
  tool: "rectangle" | "polygon" | "ellipse" | "brush" | "select" | "none";
  onAnnotationComplete: (annotation: Annotation) => void;
  activeLabel: string;
  onAnnotationSelected?: (annotationId: string | null) => void;
  annotations: Annotation[];
  selectedAnnotationId?: string | null;
  showAnnotations: boolean;
}

export function AnnotationCanvas({
  viewer,
  tool,
  onAnnotationComplete,
  activeLabel,
  onAnnotationSelected,
  annotations,
  selectedAnnotationId,
  showAnnotations,
}: AnnotationCanvasProps) {
  const canvasRef = useRef<HTMLCanvasElement>(null);
  const [isDrawing, setIsDrawing] = useState(false);
  const [currentPoints, setCurrentPoints] = useState<Array<{ x: number; y: number }>>([]);
  const [hoveredAnnotationId, setHoveredAnnotationId] = useState<string | null>(null);
  const [drawingTool, setDrawingTool] = useState<"rectangle" | "polygon" | "ellipse" | "brush" | null>(null);
  const [isPanning, setIsPanning] = useState(false);
  const [lastPanPoint, setLastPanPoint] = useState<{ x: number; y: number } | null>(null);

  const labelColor = (label: string) => {
    switch (label) {
      case "Tumor":
        return "#EF4444";
      case "Benign":
        return "#FACC15";
      case "Stroma":
        return "#EC4899";
      case "Necrosis":
        return "#22C55E";
      case "DCIS":
        return "#3B82F6";
      case "Invasive":
        return "#8B5CF6";
      default:
        return "#9CA3AF";
    }
  };

  const toImagePoint = (point: { x: number; y: number }) => {
    if (!viewer) return point;
    const viewportPoint = viewer.viewport.pointFromPixel(
      new OpenSeadragon.Point(point.x, point.y)
    );
    const imagePoint = viewer.viewport.viewportToImageCoordinates(viewportPoint);
    return { x: imagePoint.x, y: imagePoint.y };
  };

  const toScreenPoint = (point: { x: number; y: number }) => {
    if (!viewer) return point;
    const viewportPoint = viewer.viewport.imageToViewportCoordinates(
      new OpenSeadragon.Point(point.x, point.y)
    );
    const pixelPoint = viewer.viewport.pixelFromPoint(viewportPoint, true);
    return { x: pixelPoint.x, y: pixelPoint.y };
  };

  // Setup canvas
  useEffect(() => {
    const canvas = canvasRef.current;
    if (!canvas || !viewer) return;

    const updateCanvasSize = () => {
      canvas.width = viewer.container.clientWidth;
      canvas.height = viewer.container.clientHeight;
      redrawAnnotations();
    };

    updateCanvasSize();
    
    // Update canvas size when viewer resizes
    const resizeObserver = new ResizeObserver(updateCanvasSize);
    resizeObserver.observe(viewer.container);

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

  // Helper function to check if point is inside annotation
  const isPointInAnnotation = (point: { x: number; y: number }, annotation: Annotation): boolean => {
    const tolerance = 10; // pixels
    const screenPoints = annotation.points.map(toScreenPoint);
    
    if (annotation.type === "rectangle" && screenPoints.length === 2) {
      const [p1, p2] = screenPoints;
      const minX = Math.min(p1.x, p2.x);
      const maxX = Math.max(p1.x, p2.x);
      const minY = Math.min(p1.y, p2.y);
      const maxY = Math.max(p1.y, p2.y);
      
      return point.x >= minX - tolerance && point.x <= maxX + tolerance &&
             point.y >= minY - tolerance && point.y <= maxY + tolerance;
    } else if (annotation.type === "ellipse" && screenPoints.length === 2) {
      const [p1, p2] = screenPoints;
      const cx = (p1.x + p2.x) / 2;
      const cy = (p1.y + p2.y) / 2;
      const rx = Math.max(1, Math.abs(p2.x - p1.x) / 2);
      const ry = Math.max(1, Math.abs(p2.y - p1.y) / 2);
      const dx = (point.x - cx) / rx;
      const dy = (point.y - cy) / ry;
      const dist = Math.abs(dx * dx + dy * dy - 1);
      const tol = tolerance / Math.max(rx, ry);
      return dist <= tol;
    } else if (annotation.type === "polygon" && screenPoints.length > 2) {
      // Check if point is near any line segment of the polygon
      const points = screenPoints;
      for (let i = 0; i < points.length; i++) {
        const p1 = points[i];
        const p2 = points[(i + 1) % points.length];
        
        const dist = distanceToLineSegment(point, p1, p2);
        if (dist < tolerance) return true;
      }
      // Also check if near any vertex
      return screenPoints.some(p => 
        Math.hypot(p.x - point.x, p.y - point.y) < tolerance
      );
    } else if (annotation.type === "brush" && screenPoints.length > 1) {
      for (let i = 0; i < screenPoints.length - 1; i++) {
        const dist = distanceToLineSegment(point, screenPoints[i], screenPoints[i + 1]);
        if (dist < tolerance) return true;
      }
    }
    return false;
  };

  // Helper function to calculate distance from point to line segment
  const distanceToLineSegment = (
    point: { x: number; y: number },
    p1: { x: number; y: number },
    p2: { x: number; y: number }
  ): number => {
    const dx = p2.x - p1.x;
    const dy = p2.y - p1.y;
    const t = Math.max(0, Math.min(1, ((point.x - p1.x) * dx + (point.y - p1.y) * dy) / (dx * dx + dy * dy)));
    const closestX = p1.x + t * dx;
    const closestY = p1.y + t * dy;
    return Math.hypot(point.x - closestX, point.y - closestY);
  };

  // Handle mouse events for drawing
  useEffect(() => {
    const canvas = canvasRef.current;
    if (!canvas || !viewer) return;

    const handleMouseMove = (e: MouseEvent) => {
      const rect = canvas.getBoundingClientRect();
      const x = e.clientX - rect.left;
      const y = e.clientY - rect.top;
      const point = { x, y };

      // Update hover state based on tool
      if (tool === "select") {
        const hoveredAnnotation = annotations.find(ann => isPointInAnnotation(point, ann));
        setHoveredAnnotationId(hoveredAnnotation?.id || null);
        
        // Change cursor when hovering over annotation
        if (hoveredAnnotation) {
          canvas.style.cursor = "pointer";
        } else {
          canvas.style.cursor = "default";
        }
      } else {
        setHoveredAnnotationId(null);
      }

      // Handle select tool panning
      if (tool === "select" && isPanning && lastPanPoint && viewer) {
        const dx = x - lastPanPoint.x;
        const dy = y - lastPanPoint.y;
        const delta = viewer.viewport.deltaPointsFromPixels(
          new OpenSeadragon.Point(-dx, -dy),
          true
        );
        viewer.viewport.panBy(delta);
        viewer.viewport.applyConstraints();
        setLastPanPoint({ x, y });
        return;
      }

      // Handle drawing preview for rectangle/ellipse tool
      if (isDrawing && (tool === "rectangle" || tool === "ellipse") && currentPoints.length > 0) {
        const imagePoint = toImagePoint(point);
        setCurrentPoints([currentPoints[0], imagePoint]);
      }

      if (isDrawing && tool === "brush" && currentPoints.length > 0) {
        const imagePoint = toImagePoint(point);
        const lastPoint = currentPoints[currentPoints.length - 1];
        if (Math.hypot(imagePoint.x - lastPoint.x, imagePoint.y - lastPoint.y) > 0.5) {
          setCurrentPoints([...currentPoints, imagePoint]);
        }
      }
    };

    const handleMouseDown = (e: MouseEvent) => {
      const rect = canvas.getBoundingClientRect();
      const x = e.clientX - rect.left;
      const y = e.clientY - rect.top;
      const point = { x, y };

      // Allow selection with Ctrl+Click on any annotation (works with any tool)
      if (e.ctrlKey || e.metaKey) {
        const selectedAnnotation = annotations.find(ann => isPointInAnnotation(point, ann));
        if (onAnnotationSelected) {
          onAnnotationSelected(selectedAnnotation?.id || null);
        }
        return;
      }

      // Handle select tool - click to select annotation, drag to pan
      if (tool === "select") {
        const selectedAnnotation = annotations.find(ann => isPointInAnnotation(point, ann));
        if (selectedAnnotation) {
          if (onAnnotationSelected) {
            onAnnotationSelected(selectedAnnotation.id);
          }
          return;
        }

        if (viewer) {
          setIsPanning(true);
          setLastPanPoint({ x, y });
        }
        return;
      }

      // Handle drawing tools
      if (tool === "rectangle") {
        setIsDrawing(true);
        setDrawingTool("rectangle");
        setCurrentPoints([toImagePoint(point)]);
      } else if (tool === "ellipse") {
        setIsDrawing(true);
        setDrawingTool("ellipse");
        setCurrentPoints([toImagePoint(point)]);
      } else if (tool === "polygon") {
        // For polygon, add point on click
        const imagePoint = toImagePoint(point);
        const newPoints = [...currentPoints, imagePoint];
        setCurrentPoints(newPoints);
        if (currentPoints.length === 0) {
          setIsDrawing(true);
          setDrawingTool("polygon");
        }
      } else if (tool === "brush") {
        setIsDrawing(true);
        setDrawingTool("brush");
        setCurrentPoints([toImagePoint(point)]);
      }
    };

    const handleMouseUp = (e: MouseEvent) => {
      if (tool === "select" && isPanning) {
        setIsPanning(false);
        setLastPanPoint(null);
        return;
      }

      if (!isDrawing || (tool !== "rectangle" && tool !== "ellipse" && tool !== "brush")) return;

      const rect = canvas.getBoundingClientRect();
      const x = e.clientX - rect.left;
      const y = e.clientY - rect.top;

      const imagePoint = toImagePoint({ x, y });
      if (tool === "brush") {
        const newPoints = [...currentPoints, imagePoint];
        setCurrentPoints(newPoints);
        completeAnnotation(undefined, newPoints);
        return;
      }

      setCurrentPoints([currentPoints[0], imagePoint]);
      completeAnnotation();
    };

    const handleDblClick = () => {
      if (tool === "polygon" && isDrawing && currentPoints.length >= 3) {
        completeAnnotation();
      }
    };

    const handleWheel = (e: WheelEvent) => {
      if (!viewer) return;
      e.preventDefault();

      const rect = canvas.getBoundingClientRect();
      const x = e.clientX - rect.left;
      const y = e.clientY - rect.top;
      const zoomPoint = viewer.viewport.pointFromPixel(
        new OpenSeadragon.Point(x, y)
      );

      const zoomFactor = e.deltaY < 0 ? 1.1 : 0.9;
      viewer.viewport.zoomBy(zoomFactor, zoomPoint);
      viewer.viewport.applyConstraints();
    };

    const handleKeyDown = (e: KeyboardEvent) => {
      if (e.key === "Escape" && isDrawing) {
        setIsDrawing(false);
        setCurrentPoints([]);
      }
    };

    canvas.addEventListener("mousemove", handleMouseMove);
    canvas.addEventListener("mousedown", handleMouseDown);
    canvas.addEventListener("mouseup", handleMouseUp);
    canvas.addEventListener("dblclick", handleDblClick);
    canvas.addEventListener("wheel", handleWheel, { passive: false });
    document.addEventListener("keydown", handleKeyDown);

    return () => {
      canvas.removeEventListener("mousemove", handleMouseMove);
      canvas.removeEventListener("mousedown", handleMouseDown);
      canvas.removeEventListener("mouseup", handleMouseUp);
      canvas.removeEventListener("dblclick", handleDblClick);
      canvas.removeEventListener("wheel", handleWheel);
      document.removeEventListener("keydown", handleKeyDown);
    };
  }, [isDrawing, currentPoints, tool, viewer, annotations, selectedAnnotationId, onAnnotationSelected, hoveredAnnotationId, isPanning, lastPanPoint]);

  const completeAnnotation = (
    forcedTool?: "rectangle" | "polygon" | "ellipse" | "brush",
    forcedPoints?: Array<{ x: number; y: number }>
  ) => {
    const points = forcedPoints ?? currentPoints;
    if (points.length < 2) {
      setCurrentPoints([]);
      setIsDrawing(false);
      setDrawingTool(null);
      return;
    }

    const finalTool = forcedTool ?? drawingTool ?? (tool as "rectangle" | "polygon" | "ellipse" | "brush");
    const annotation: Annotation = {
      id: `annotation-${Date.now()}`,
      type: finalTool,
      points,
      color: labelColor(activeLabel),
      label: activeLabel,
      completed: true,
    };

    onAnnotationComplete(annotation);
    setCurrentPoints([]);
    setIsDrawing(false);
    setDrawingTool(null);
    redrawAnnotations();
  };

  const redrawAnnotations = useCallback(() => {
    const canvas = canvasRef.current;
    if (!canvas) return;

    const ctx = canvas.getContext("2d");
    if (!ctx) return;

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

    if (!showAnnotations) return;

    // Draw completed annotations
    annotations.forEach((annotation) => {
      const isSelected = selectedAnnotationId === annotation.id;
      const isHovered = hoveredAnnotationId === annotation.id;
      const screenPoints = annotation.points.map(toScreenPoint);
      
      if (annotation.type === "rectangle" && screenPoints.length === 2) {
        const [p1, p2] = screenPoints;
        const x = Math.min(p1.x, p2.x);
        const y = Math.min(p1.y, p2.y);
        const width = Math.abs(p2.x - p1.x);
        const height = Math.abs(p2.y - p1.y);

        // Draw only outline, no fill
        if (isSelected) {
          ctx.strokeStyle = "#FCD34D";
          ctx.lineWidth = 4;
        } else if (isHovered) {
          ctx.strokeStyle = "#60A5FA"; // Light blue for hover
          ctx.lineWidth = 3;
        } else {
          ctx.strokeStyle = annotation.color;
          ctx.lineWidth = 2;
        }
        ctx.strokeRect(x, y, width, height);
        
        // Add hover indicator
        if (isHovered) {
          // Add hover indicator
          ctx.fillStyle = "#60A5FA";
          ctx.fillRect(x, y - 22, 65, 18);
          ctx.fillStyle = "#FFF";
          ctx.font = "11px Arial";
          ctx.textAlign = "left";
          ctx.textBaseline = "top";
          ctx.fillText("Click to select", x + 4, y - 18);
        }
      } else if (annotation.type === "ellipse" && screenPoints.length === 2) {
        const [p1, p2] = screenPoints;
        const cx = (p1.x + p2.x) / 2;
        const cy = (p1.y + p2.y) / 2;
        const rx = Math.abs(p2.x - p1.x) / 2;
        const ry = Math.abs(p2.y - p1.y) / 2;

        if (isSelected) {
          ctx.strokeStyle = "#FCD34D";
          ctx.lineWidth = 4;
        } else if (isHovered) {
          ctx.strokeStyle = "#60A5FA";
          ctx.lineWidth = 3;
        } else {
          ctx.strokeStyle = annotation.color;
          ctx.lineWidth = 2;
        }

        ctx.beginPath();
        ctx.ellipse(cx, cy, Math.max(1, rx), Math.max(1, ry), 0, 0, Math.PI * 2);
        ctx.stroke();

        if (isHovered) {
          ctx.fillStyle = "#60A5FA";
          ctx.fillRect(cx - 35, cy - ry - 26, 70, 18);
          ctx.fillStyle = "#FFF";
          ctx.font = "11px Arial";
          ctx.textAlign = "center";
          ctx.textBaseline = "top";
          ctx.fillText("Click to select", cx, cy - ry - 22);
        }
      } else if (annotation.type === "polygon" && screenPoints.length > 1) {
        // Determine colors based on state
        let strokeColor, pointColor;
        let lineWidth = 2;
        
        if (isSelected) {
          strokeColor = "#FCD34D";
          pointColor = "#FCD34D";
          lineWidth = 4;
        } else if (isHovered) {
          strokeColor = "#60A5FA";
          pointColor = "#60A5FA";
          lineWidth = 3;
        } else {
          strokeColor = annotation.color;
          pointColor = annotation.color;
        }
        
        ctx.strokeStyle = strokeColor;
        ctx.lineWidth = lineWidth;

        // Draw main polygon
        ctx.strokeStyle = strokeColor;
        ctx.beginPath();
        ctx.moveTo(screenPoints[0].x, screenPoints[0].y);
        screenPoints.slice(1).forEach((p) => {
          ctx.lineTo(p.x, p.y);
        });
        ctx.closePath();
        ctx.stroke();

        // Draw points as circles
        ctx.fillStyle = pointColor;
        screenPoints.forEach((p, index) => {
          ctx.beginPath();
          const radius = isSelected ? 7 : isHovered ? 6 : 5;
          ctx.arc(p.x, p.y, radius, 0, Math.PI * 2);
          ctx.fill();
          
          // Draw point numbers
          ctx.fillStyle = "#FFFFFF";
          ctx.font = "bold 11px Arial";
          ctx.textAlign = "center";
          ctx.textBaseline = "middle";
          ctx.fillText((index + 1).toString(), p.x, p.y);
          ctx.fillStyle = pointColor;
        });
        
        // Add hover indicator
        if (isHovered && screenPoints.length > 0) {
          const firstPoint = screenPoints[0];
          ctx.fillStyle = "#60A5FA";
          ctx.fillRect(firstPoint.x + 10, firstPoint.y - 22, 65, 18);
          ctx.fillStyle = "#FFF";
          ctx.font = "11px Arial";
          ctx.textAlign = "left";
          ctx.textBaseline = "top";
          ctx.fillText("Click to select", firstPoint.x + 14, firstPoint.y - 18);
        }
      } else if (annotation.type === "brush" && screenPoints.length > 1) {
        if (isSelected) {
          ctx.strokeStyle = "#FCD34D";
          ctx.lineWidth = 4;
        } else if (isHovered) {
          ctx.strokeStyle = "#60A5FA";
          ctx.lineWidth = 3;
        } else {
          ctx.strokeStyle = annotation.color;
          ctx.lineWidth = 2;
        }

        ctx.beginPath();
        ctx.moveTo(screenPoints[0].x, screenPoints[0].y);
        screenPoints.slice(1).forEach((p) => {
          ctx.lineTo(p.x, p.y);
        });
        ctx.stroke();

        if (isHovered) {
          const firstPoint = screenPoints[0];
          ctx.fillStyle = "#60A5FA";
          ctx.fillRect(firstPoint.x + 10, firstPoint.y - 22, 65, 18);
          ctx.fillStyle = "#FFF";
          ctx.font = "11px Arial";
          ctx.textAlign = "left";
          ctx.textBaseline = "top";
          ctx.fillText("Click to select", firstPoint.x + 14, firstPoint.y - 18);
        }
      }
    });

    // Draw current drawing (preview)
    if (isDrawing && currentPoints.length > 0) {
      const color = tool === "rectangle" ? "#EF4444" : "#3B82F6";
      const previewPoints = currentPoints.map(toScreenPoint);

      if ((tool === "rectangle" || tool === "ellipse") && previewPoints.length === 2) {
        const [p1, p2] = previewPoints;
        const x = Math.min(p1.x, p2.x);
        const y = Math.min(p1.y, p2.y);
        const width = Math.abs(p2.x - p1.x);
        const height = Math.abs(p2.y - p1.y);

        // Draw only outline with dashed style, no fill
        ctx.strokeStyle = color;
        ctx.lineWidth = 2;
        ctx.setLineDash([5, 5]);
        if (tool === "rectangle") {
          ctx.strokeRect(x, y, width, height);
        } else {
          const cx = x + width / 2;
          const cy = y + height / 2;
          ctx.beginPath();
          ctx.ellipse(cx, cy, Math.max(1, width / 2), Math.max(1, height / 2), 0, 0, Math.PI * 2);
          ctx.stroke();
        }
        ctx.setLineDash([]);
      } else if (tool === "polygon" && previewPoints.length > 1) {
        ctx.strokeStyle = color;
        ctx.lineWidth = 2;
        ctx.setLineDash([5, 5]);

        ctx.beginPath();
        ctx.moveTo(previewPoints[0].x, previewPoints[0].y);
        previewPoints.slice(1).forEach((p) => {
          ctx.lineTo(p.x, p.y);
        });
        
        // Close the path only if we have 3+ points
        if (previewPoints.length >= 3) {
          ctx.closePath();
        }
        
        ctx.stroke();
        ctx.setLineDash([]);

        // Draw points as solid circles with numbers
        ctx.fillStyle = color;
        previewPoints.forEach((p, index) => {
          ctx.beginPath();
          ctx.arc(p.x, p.y, 5, 0, Math.PI * 2);
          ctx.fill();
          
          // Draw point numbers
          ctx.fillStyle = "#FFFFFF";
          ctx.font = "bold 11px Arial";
          ctx.textAlign = "center";
          ctx.textBaseline = "middle";
          ctx.fillText((index + 1).toString(), p.x, p.y);
          ctx.fillStyle = color;
        });
      } else if (tool === "brush" && previewPoints.length > 1) {
        ctx.strokeStyle = "#EF4444";
        ctx.lineWidth = 2;
        ctx.setLineDash([5, 5]);
        ctx.beginPath();
        ctx.moveTo(previewPoints[0].x, previewPoints[0].y);
        previewPoints.slice(1).forEach((p) => {
          ctx.lineTo(p.x, p.y);
        });
        ctx.stroke();
        ctx.setLineDash([]);
      }
    }
  }, [annotations, currentPoints, hoveredAnnotationId, isDrawing, selectedAnnotationId, showAnnotations, tool, viewer]);

  useEffect(() => {
    redrawAnnotations();
  }, [redrawAnnotations]);

  useEffect(() => {
    if (!isDrawing || !drawingTool) return;
    if (tool === drawingTool) return;

    if (drawingTool === "polygon" && currentPoints.length >= 3) {
      completeAnnotation(drawingTool);
      return;
    }

    if ((drawingTool === "rectangle" || drawingTool === "ellipse") && currentPoints.length === 2) {
      completeAnnotation(drawingTool);
      return;
    }

    if (drawingTool === "brush" && currentPoints.length >= 2) {
      completeAnnotation(drawingTool);
      return;
    }

    setIsDrawing(false);
    setCurrentPoints([]);
    setDrawingTool(null);
  }, [tool, drawingTool, currentPoints, isDrawing]);

  useEffect(() => {
    if (!viewer) return;
    const handleViewportChange = () => redrawAnnotations();
    viewer.addHandler("zoom", handleViewportChange);
    viewer.addHandler("pan", handleViewportChange);
    viewer.addHandler("animation", handleViewportChange);
    viewer.addHandler("open", handleViewportChange);

    return () => {
      viewer.removeHandler("zoom", handleViewportChange);
      viewer.removeHandler("pan", handleViewportChange);
      viewer.removeHandler("animation", handleViewportChange);
      viewer.removeHandler("open", handleViewportChange);
    };
  }, [redrawAnnotations, viewer]);

  return (
    <canvas
      ref={canvasRef}
      className={`absolute inset-0 z-40 ${
        tool === "none" ? "pointer-events-none cursor-grab" : ""
      } ${tool === "select" ? "cursor-default" : ""} ${
        tool === "rectangle" || tool === "polygon" || tool === "ellipse" || tool === "brush"
          ? "cursor-crosshair"
          : ""
      }`}
      style={{ width: "100%", height: "100%" }}
    />
  );
}
