import { memo, useMemo } from 'react';
import { Text, Rect, Group, Line } from 'react-konva';
import type { Point, Annotation, ElectricalItem, FurnitureItem } from '@house-plan-maker/shared';
import { rotatedAnchorOffsetToCenter } from '@house-plan-maker/shared';

interface AnnotationLayerProps {
  readonly annotations: readonly Annotation[];
  readonly electricalItems: readonly ElectricalItem[];
  readonly furnitureItems: readonly FurnitureItem[];
  readonly zoom: number;
  readonly panOffset: Point;
  readonly selectedIds: ReadonlySet<string>;
  readonly visible?: boolean;
  readonly onDragEnd?: (id: string, x: number, y: number) => void;
  readonly onDoubleClick?: (id: string) => void;
  readonly onSelect?: (id: string) => void;
}

const DEFAULT_FONT_SIZE = 14;
const DEFAULT_COLOR = '#333333';
const LINK_COLOR = '#2563eb';
const SELECTED_COLOR = '#4c6ef5';
const SELECTION_PADDING = 4;

// Match plain http(s) URLs only — anything else stays a regular annotation.
// Anchored to start/end so a label like "see http://x" isn't treated as a
// link (we want the whole text to be the URL).
const URL_PATTERN = /^https?:\/\/\S+$/i;

function isUrlAnnotation(text: string): boolean {
  return URL_PATTERN.test(text.trim());
}

function toScreen(point: Point, zoom: number, panOffset: Point): { x: number; y: number } {
  return {
    x: point.x * zoom + panOffset.x,
    y: point.y * zoom + panOffset.y,
  };
}

export const AnnotationLayer = memo(function AnnotationLayer({
  annotations,
  electricalItems,
  furnitureItems,
  zoom,
  panOffset,
  selectedIds,
  visible = true,
  onDragEnd,
  onDoubleClick,
  onSelect,
}: AnnotationLayerProps) {
  // Build item position lookup for attached annotations
  const itemPositions = useMemo(() => {
    const map = new Map<string, { x: number; y: number }>();
    for (const item of electricalItems) {
      map.set(item.id, { x: item.x, y: item.y });
    }
    for (const item of furnitureItems) {
      const offset = rotatedAnchorOffsetToCenter(
        item.positionAnchor,
        item.width,
        item.depth,
        item.rotation,
      );
      map.set(item.id, { x: item.x + offset.dx, y: item.y + offset.dy });
    }
    return map;
  }, [electricalItems, furnitureItems]);

  // Annotations attached to electrical items belong to the wall-projection
  // context — they clutter the top-down 2D view (and exported images) with
  // labels that were authored for per-wall projection layout. Hide them
  // here; they still render in WallProjectionView.
  const electricalIdSet = useMemo(
    () => new Set(electricalItems.map((e) => e.id)),
    [electricalItems],
  );

  const renderedAnnotations = useMemo(() => {
    if (!visible) return [];
    return annotations.filter(
      (a) => !a.attachedToId || !electricalIdSet.has(a.attachedToId),
    );
  }, [annotations, visible, electricalIdSet]);

  return (
    <Group visible={visible}>
      {renderedAnnotations.map((annotation) => {
        // Resolve position: if attached, offset from parent item
        let worldX = annotation.x;
        let worldY = annotation.y;
        let parentScreen: { x: number; y: number } | null = null;

        if (annotation.attachedToId) {
          const parentPos = itemPositions.get(annotation.attachedToId);
          if (parentPos) {
            worldX = parentPos.x + annotation.x;
            worldY = parentPos.y + annotation.y;
            parentScreen = toScreen(parentPos, zoom, panOffset);
          }
        }

        const screen = toScreen({ x: worldX, y: worldY }, zoom, panOffset);
        const isSelected = selectedIds.has(annotation.id);
        const fontSize = annotation.fontSize ?? DEFAULT_FONT_SIZE;
        const isLink = isUrlAnnotation(annotation.text);
        // Link annotations get a distinctive blue tint when not selected so
        // users can spot them; selection still wins to keep the affordance
        // consistent with non-link annotations.
        const color = isSelected
          ? SELECTED_COLOR
          : isLink
            ? (annotation.color ?? LINK_COLOR)
            : (annotation.color ?? DEFAULT_COLOR);

        return (
          <Group key={annotation.id}>
            {/* Leader line from item to annotation */}
            {parentScreen && (
              <Line
                points={[parentScreen.x, parentScreen.y, screen.x, screen.y]}
                stroke={annotation.color ?? '#94a3b8'}
                strokeWidth={1}
                dash={[3, 3]}
                listening={false}
              />
            )}
            <Group
              x={screen.x}
              y={screen.y}
              draggable
              onDragEnd={(e) => {
                const node = e.target;
                let newX = (node.x() - panOffset.x) / zoom;
                let newY = (node.y() - panOffset.y) / zoom;
                // For attached annotations, store as offset from parent
                if (annotation.attachedToId) {
                  const parentPos = itemPositions.get(annotation.attachedToId);
                  if (parentPos) {
                    newX -= parentPos.x;
                    newY -= parentPos.y;
                  }
                }
                onDragEnd?.(annotation.id, newX, newY);
              }}
              onClick={(e) => {
                // Ctrl/Cmd-click on a URL annotation opens it in a new tab.
                // We swallow the event so it doesn't also trigger selection
                // or upstream stage handlers (which would deselect the link
                // immediately on focus loss). Plain clicks fall through to
                // the regular select handler.
                if (isLink && (e.evt.ctrlKey || e.evt.metaKey)) {
                  e.cancelBubble = true;
                  window.open(annotation.text.trim(), '_blank', 'noopener,noreferrer');
                  return;
                }
                onSelect?.(annotation.id);
              }}
              onDblClick={() => onDoubleClick?.(annotation.id)}
            >
              {/* Background */}
              <Rect
                x={-2}
                y={-1}
                width={annotation.text.length * fontSize * 0.6 + 4}
                height={fontSize + 2}
                fill="rgba(255,255,255,0.85)"
                cornerRadius={2}
                listening={false}
              />
              {isSelected && (
                <Rect
                  x={-SELECTION_PADDING}
                  y={-SELECTION_PADDING}
                  width={annotation.text.length * fontSize * 0.6 + SELECTION_PADDING * 2}
                  height={fontSize + SELECTION_PADDING * 2}
                  stroke={SELECTED_COLOR}
                  strokeWidth={1}
                  dash={[4, 2]}
                  fill="rgba(76, 110, 245, 0.05)"
                />
              )}
              <Text
                text={annotation.text}
                fontSize={fontSize}
                fill={color}
                fontFamily="sans-serif"
              />
            </Group>
          </Group>
        );
      })}
    </Group>
  );
});
