import { useEffect, useRef, useState } from 'react';
import type { Damage, Part } from '@arac-hasar/types';
import { cn } from '../utils/cn';

export type OverlayMode = 'damages' | 'parts' | 'both' | 'none';

interface Props {
  imageUrl: string;
  damages?: Damage[];
  parts?: Part[];
  mode?: OverlayMode;
  highlightedDamageId?: number | null;
  highlightedPart?: string | null;
  /** Click handler for a damage overlay (hit-tested in container coords). */
  onDamageClick?: (id: number) => void;
  /** Click handler for a part overlay. */
  onPartClick?: (name: string) => void;
  /** Accessible alt text for the underlying image. */
  alt?: string;
  className?: string;
}

const SEVERITY_COLORS: Record<string, string> = {
  hafif: '#f59e0b',
  orta: '#f97316',
  agir: '#ef4444',
};

const PART_COLOR = '#3b82f6';

export function ImageWithOverlay({
  imageUrl,
  damages = [],
  parts = [],
  mode = 'both',
  highlightedDamageId = null,
  highlightedPart = null,
  onDamageClick,
  onPartClick,
  alt = 'İnceleme görüntüsü',
  className,
}: Props) {
  const canvasRef = useRef<HTMLCanvasElement | null>(null);
  const containerRef = useRef<HTMLDivElement | null>(null);
  const [imgDim, setImgDim] = useState<{ w: number; h: number } | null>(null);
  const [renderSize, setRenderSize] = useState<{ w: number; h: number } | null>(null);

  // Resolve natural image dimensions (also fires via <img onLoad>).
  useEffect(() => {
    if (typeof window === 'undefined') return;
    const img = new window.Image();
    img.crossOrigin = 'anonymous';
    img.onload = () => setImgDim({ w: img.naturalWidth, h: img.naturalHeight });
    img.src = imageUrl;
  }, [imageUrl]);

  // Track rendered container size so the canvas re-paints on layout changes.
  useEffect(() => {
    const container = containerRef.current;
    if (!container || typeof ResizeObserver === 'undefined') return;
    const ro = new ResizeObserver((entries) => {
      const entry = entries[0];
      if (!entry) return;
      const cr = entry.contentRect;
      setRenderSize({ w: cr.width, h: cr.height });
    });
    ro.observe(container);
    return () => ro.disconnect();
  }, []);

  useEffect(() => {
    const canvas = canvasRef.current;
    const container = containerRef.current;
    if (!canvas || !container || !imgDim) return;

    const size = renderSize ?? {
      w: container.getBoundingClientRect().width,
      h: container.getBoundingClientRect().height,
    };
    if (size.w <= 0 || size.h <= 0) return;
    const dpr = (typeof window !== 'undefined' && window.devicePixelRatio) || 1;
    canvas.width = Math.round(size.w * dpr);
    canvas.height = Math.round(size.h * dpr);
    canvas.style.width = `${size.w}px`;
    canvas.style.height = `${size.h}px`;

    const ctx = canvas.getContext('2d');
    if (!ctx) return;
    ctx.setTransform(1, 0, 0, 1, 0, 0);
    ctx.scale(dpr, dpr);
    ctx.clearRect(0, 0, size.w, size.h);

    const sx = size.w;
    const sy = size.h;

    if (mode === 'parts' || mode === 'both') {
      parts.forEach((p) => {
        const isHi = highlightedPart === p.name;
        ctx.strokeStyle = PART_COLOR;
        ctx.lineWidth = isHi ? 3 : 1.5;
        ctx.fillStyle = isHi ? 'rgba(59, 130, 246, 0.18)' : 'rgba(59, 130, 246, 0.05)';
        drawPolygon(ctx, p.polygon_normalized, sx, sy, true);
      });
    }

    if (mode === 'damages' || mode === 'both') {
      damages.forEach((d) => {
        const color = SEVERITY_COLORS[d.severity.level] || '#999';
        const isHi = highlightedDamageId === d.id;
        ctx.strokeStyle = color;
        ctx.lineWidth = isHi ? 4 : 2;
        ctx.fillStyle = isHi ? `${color}55` : `${color}22`;
        if (d.polygon_normalized?.length) {
          drawPolygon(ctx, d.polygon_normalized, sx, sy, true);
        } else {
          drawBbox(ctx, d.bbox, sx, sy, imgDim);
        }
      });
    }
  }, [imgDim, renderSize, damages, parts, mode, highlightedDamageId, highlightedPart]);

  // Hit-test click → first damage/part whose polygon (or bbox) contains the point.
  const handleClick = (e: React.MouseEvent<HTMLDivElement>) => {
    if (!onDamageClick && !onPartClick) return;
    const container = containerRef.current;
    if (!container || !imgDim) return;
    const rect = container.getBoundingClientRect();
    if (rect.width === 0 || rect.height === 0) return;
    const nx = (e.clientX - rect.left) / rect.width;
    const ny = (e.clientY - rect.top) / rect.height;
    if (nx < 0 || nx > 1 || ny < 0 || ny > 1) return;

    if (onDamageClick && (mode === 'damages' || mode === 'both')) {
      for (const d of damages) {
        if (d.polygon_normalized?.length) {
          if (pointInPolygon(nx, ny, d.polygon_normalized)) {
            onDamageClick(d.id);
            return;
          }
        } else if (d.bbox && d.bbox.length >= 4) {
          const [x1, y1, x2, y2] = d.bbox;
          const px = nx * imgDim.w;
          const py = ny * imgDim.h;
          if (px >= x1! && px <= x2! && py >= y1! && py <= y2!) {
            onDamageClick(d.id);
            return;
          }
        }
      }
    }
    if (onPartClick && (mode === 'parts' || mode === 'both')) {
      for (const p of parts) {
        if (p.polygon_normalized?.length && pointInPolygon(nx, ny, p.polygon_normalized)) {
          onPartClick(p.name);
          return;
        }
      }
    }
  };

  const interactive = !!(onDamageClick || onPartClick);

  // Reserve layout space ASAP to prevent CLS. Until we know the image's
  // intrinsic aspect ratio, fall back to 4:3 (typical vehicle photo).
  // packages/ui is framework-agnostic so we keep a plain <img> — consumer
  // apps wrap routes in next/image where they own URLs directly.
  const aspectRatio = imgDim ? `${imgDim.w} / ${imgDim.h}` : '4 / 3';

  return (
    <div
      ref={containerRef}
      onClick={interactive ? handleClick : undefined}
      className={cn(
        'relative overflow-hidden rounded-lg bg-slate-100',
        interactive && 'cursor-pointer',
        className,
      )}
      style={{ aspectRatio }}
    >
      {/* Guard: backend STORAGE_OPTIONAL'da `local://skipped/...` URL'i
          dönebilir; browser bunu fetch edemez — placeholder göster. */}
      {imageUrl && !imageUrl.startsWith('local://') ? (
        <img
          src={imageUrl}
          alt={alt}
          draggable={false}
          loading="lazy"
          decoding="async"
          className="block h-full w-full select-none object-contain"
          onLoad={(e) => {
            const t = e.currentTarget;
            setImgDim({ w: t.naturalWidth, h: t.naturalHeight });
          }}
        />
      ) : (
        <div className="flex h-full w-full items-center justify-center bg-slate-100 text-sm text-slate-500">
          Görsel kaydedilmedi (storage opsiyonel mod)
        </div>
      )}
      <canvas
        ref={canvasRef}
        className="pointer-events-none absolute inset-0 h-full w-full"
        aria-hidden
      />
    </div>
  );
}

function pointInPolygon(x: number, y: number, poly: number[][]): boolean {
  let inside = false;
  for (let i = 0, j = poly.length - 1; i < poly.length; j = i++) {
    const a = poly[i];
    const b = poly[j];
    if (!a || !b || a.length < 2 || b.length < 2) continue;
    const xi = a[0]!;
    const yi = a[1]!;
    const xj = b[0]!;
    const yj = b[1]!;
    const intersect =
      yi > y !== yj > y && x < ((xj - xi) * (y - yi)) / (yj - yi + 1e-12) + xi;
    if (intersect) inside = !inside;
  }
  return inside;
}

function drawPolygon(
  ctx: CanvasRenderingContext2D,
  poly: number[][],
  sx: number,
  sy: number,
  fill = false,
) {
  if (!poly || poly.length === 0) return;
  ctx.beginPath();
  poly.forEach((pt, i) => {
    if (!pt || pt.length < 2) return;
    const x = pt[0]!;
    const y = pt[1]!;
    const px = x * sx;
    const py = y * sy;
    if (i === 0) ctx.moveTo(px, py);
    else ctx.lineTo(px, py);
  });
  ctx.closePath();
  if (fill) ctx.fill();
  ctx.stroke();
}

function drawBbox(
  ctx: CanvasRenderingContext2D,
  bbox: number[],
  sx: number,
  sy: number,
  imgDim: { w: number; h: number },
) {
  if (bbox.length < 4) return;
  const x1 = bbox[0]!;
  const y1 = bbox[1]!;
  const x2 = bbox[2]!;
  const y2 = bbox[3]!;
  const rx = (x1 / imgDim.w) * sx;
  const ry = (y1 / imgDim.h) * sy;
  const rw = ((x2 - x1) / imgDim.w) * sx;
  const rh = ((y2 - y1) / imgDim.h) * sy;
  ctx.beginPath();
  ctx.rect(rx, ry, rw, rh);
  ctx.fill();
  ctx.stroke();
}
