import { useEffect, useRef, useState } from 'react';
import { useTranslation } from 'react-i18next';

import { XIcon } from './icons';

export type AnnotatorTool = 'pen' | 'arrow' | 'rect' | 'circle' | 'text' | 'highlighter';
export type AnnotatorColor = '#ef4444' | '#3b82f6' | '#22c55e' | '#facc15' | '#a855f7' | '#ffffff';
export type AnnotatorWidth = 2 | 4 | 8;

export interface AnnotatorOp {
  tool: AnnotatorTool;
  color: AnnotatorColor;
  width: AnnotatorWidth;
  points?: Array<{ x: number; y: number }>;
  from?: { x: number; y: number };
  to?: { x: number; y: number };
  text?: string;
  at?: { x: number; y: number };
}

interface Props {
  file: File;
  onCancel: () => void;
  onSave: (next: File) => void;
}

export function ImageAnnotator({ file, onCancel, onSave }: Props) {
  const { t } = useTranslation();
  const canvasRef = useRef<HTMLCanvasElement | null>(null);
  const imageRef = useRef<HTMLImageElement | null>(null);
  const [imageLoaded, setImageLoaded] = useState(false);
  const [ops, setOps] = useState<AnnotatorOp[]>([]);
  const [redoStack, setRedoStack] = useState<AnnotatorOp[]>([]);
  // tool/color/width are read by Task 2 (drawing engine) and Task 3 (toolbar).
  // Defaults shown here are intentional so T2's pointer handlers work
  // immediately with sensible behavior before T3's UI is wired.
  const [tool, setTool] = useState<AnnotatorTool>('pen');
  const [color, setColor] = useState<AnnotatorColor>('#ef4444');
  const [width, setWidth] = useState<AnnotatorWidth>(4);

  const draftRef = useRef<AnnotatorOp | null>(null);
  const [draftTick, setDraftTick] = useState(0);

  // Hold a stable ref to onCancel so the image-load effect doesn't depend
  // on its identity. Without this, parents that pass an inline `() => …`
  // re-render the modal on every keystroke / state change, the effect re-
  // runs, the previous URL.createObjectURL gets revoked WHILE the new img
  // is still decoding → img.onerror fires ("file not found") → onCancel →
  // modal flashes open + closes instantly.
  const onCancelRef = useRef(onCancel);
  useEffect(() => { onCancelRef.current = onCancel; }, [onCancel]);

  useEffect(() => {
    // React 18 strict mode in dev double-mounts effects to test idempotency.
    // The first run creates a blob URL, sets img.src, returns a cleanup
    // that revokes — and the cleanup fires BEFORE the (still-in-flight)
    // image fetch completes. The browser then emits ERR_FILE_NOT_FOUND for
    // the revoked URL → img.onerror → modal closes instantly. The
    // `cancelled` flag guards every callback so a torn-down run can't
    // close the modal that the second mount just opened.
    let cancelled = false;
    const url = URL.createObjectURL(file);
    const img = new Image();
    img.onload = () => {
      if (cancelled) return;
      imageRef.current = img;
      setImageLoaded(true);
    };
    img.onerror = () => {
      if (cancelled) return;
      console.error('ImageAnnotator: failed to decode source image');
      onCancelRef.current();
    };
    img.src = url;
    return () => {
      cancelled = true;
      URL.revokeObjectURL(url);
    };
  }, [file]);

  useEffect(() => {
    if (!imageLoaded) return;
    const cv = canvasRef.current;
    const img = imageRef.current;
    if (!cv || !img) return;
    cv.width = img.naturalWidth;
    cv.height = img.naturalHeight;
    const ctx = cv.getContext('2d');
    if (!ctx) return;
    ctx.drawImage(img, 0, 0);
    for (const op of ops) {
      renderOp(ctx, op);
    }
    if (draftRef.current) {
      renderOp(ctx, draftRef.current);
    }
  }, [imageLoaded, ops, draftTick]);

  useEffect(() => {
    const onKey = (e: KeyboardEvent) => {
      if (e.key === 'Escape') onCancel();
      if ((e.ctrlKey || e.metaKey) && e.key === 'z' && !e.shiftKey) {
        e.preventDefault();
        handleUndo();
      } else if ((e.ctrlKey || e.metaKey) && (e.key === 'y' || (e.key === 'z' && e.shiftKey))) {
        e.preventDefault();
        handleRedo();
      }
    };
    window.addEventListener('keydown', onKey);
    return () => window.removeEventListener('keydown', onKey);
  });

  const handleUndo = () => {
    setOps((cur) => {
      if (cur.length === 0) return cur;
      const next = cur.slice(0, -1);
      setRedoStack((r) => [...r, cur[cur.length - 1]!]);
      return next;
    });
  };

  const handleRedo = () => {
    setRedoStack((r) => {
      if (r.length === 0) return r;
      const top = r[r.length - 1]!;
      setOps((cur) => [...cur, top]);
      return r.slice(0, -1);
    });
  };

  const handleReset = () => {
    setOps([]);
    setRedoStack([]);
  };

  const handleSave = () => {
    const cv = canvasRef.current;
    if (!cv) return;
    cv.toBlob((blob) => {
      if (!blob) {
        console.error('ImageAnnotator: toBlob returned null');
        return;
      }
      const baseName = file.name.replace(/\.[^.]+$/, '');
      const next = new File([blob], baseName + '-annotated.png', {
        type: 'image/png',
        lastModified: Date.now(),
      });
      onSave(next);
    }, 'image/png');
  };

  function canvasPoint(e: React.PointerEvent<HTMLCanvasElement>): { x: number; y: number } {
    const cv = canvasRef.current!;
    const rect = cv.getBoundingClientRect();
    const scaleX = cv.width / rect.width;
    const scaleY = cv.height / rect.height;
    return {
      x: (e.clientX - rect.left) * scaleX,
      y: (e.clientY - rect.top) * scaleY,
    };
  }

  const handlePointerDown = (e: React.PointerEvent<HTMLCanvasElement>) => {
    if (!imageLoaded) return;
    const cv = canvasRef.current;
    if (!cv) return;
    cv.setPointerCapture(e.pointerId);
    const p = canvasPoint(e);

    if (tool === 'text') {
      const value = window.prompt(
        t('app:annotator.text_prompt', { defaultValue: 'Text eingeben:' }),
        '',
      );
      if (value !== null && value.trim().length > 0) {
        setOps((cur) => [...cur, { tool: 'text', color, width, text: value, at: p }]);
        setRedoStack([]);
      }
      return;
    }

    if (tool === 'pen' || tool === 'highlighter') {
      draftRef.current = { tool, color, width, points: [p] };
    } else {
      draftRef.current = { tool, color, width, from: p, to: p };
    }
    setDraftTick((n) => n + 1);
  };

  const handlePointerMove = (e: React.PointerEvent<HTMLCanvasElement>) => {
    if (!draftRef.current) return;
    const p = canvasPoint(e);
    const cur = draftRef.current;
    if (cur.tool === 'pen' || cur.tool === 'highlighter') {
      cur.points = [...(cur.points ?? []), p];
    } else {
      cur.to = p;
    }
    setDraftTick((n) => n + 1);
  };

  const handlePointerUp = (e: React.PointerEvent<HTMLCanvasElement>) => {
    const cv = canvasRef.current;
    if (cv && cv.hasPointerCapture(e.pointerId)) cv.releasePointerCapture(e.pointerId);
    const cur = draftRef.current;
    draftRef.current = null;
    if (!cur) return;
    const hasContent =
      (cur.tool === 'pen' || cur.tool === 'highlighter')
        ? (cur.points?.length ?? 0) >= 2
        : !!(cur.from && cur.to && (cur.from.x !== cur.to.x || cur.from.y !== cur.to.y));
    if (hasContent) {
      setOps((p) => [...p, cur]);
      setRedoStack([]);
    }
    setDraftTick((n) => n + 1);
  };

  return (
    <div
      role="dialog"
      aria-modal="true"
      aria-label={t('app:annotator.title', { defaultValue: 'Bild bearbeiten' })}
      className="fixed inset-0 z-[80] flex flex-col bg-ink-950/95"
    >
      <header className="flex shrink-0 items-center justify-between border-b border-line/40 bg-surface-2 px-4 py-2">
        <h2 className="font-display text-sm font-semibold text-fg">
          {t('app:annotator.title', { defaultValue: 'Bild bearbeiten' })}
        </h2>
        <button
          type="button"
          onClick={onCancel}
          aria-label={t('app:annotator.cancel', { defaultValue: 'Abbrechen' })}
          className="flex h-7 w-7 cursor-pointer items-center justify-center rounded-md text-fg-muted hover:bg-surface-3 hover:text-fg"
        >
          <XIcon className="h-3.5 w-3.5" />
        </button>
      </header>

      <div className="flex min-h-0 flex-1 items-center justify-center overflow-hidden p-6">
        {imageLoaded ? (
          <canvas
            ref={canvasRef}
            onPointerDown={handlePointerDown}
            onPointerMove={handlePointerMove}
            onPointerUp={handlePointerUp}
            onPointerLeave={handlePointerUp}
            className="max-h-full max-w-full cursor-crosshair touch-none rounded-lg border border-line/40 bg-black shadow-2xl"
          />
        ) : (
          <p className="text-sm text-fg-muted">
            {t('app:annotator.loading', { defaultValue: 'Bild wird geladen…' })}
          </p>
        )}
      </div>

      <footer className="flex shrink-0 flex-wrap items-center gap-4 border-t border-line/40 bg-surface-2 px-4 py-3">
        <div className="flex items-center gap-1">
          {(['pen', 'highlighter', 'arrow', 'rect', 'circle', 'text'] as AnnotatorTool[]).map((id) => {
            const label = t('app:annotator.tool.' + id, {
              defaultValue:
                id === 'pen' ? 'Stift'
                : id === 'highlighter' ? 'Marker'
                : id === 'arrow' ? 'Pfeil'
                : id === 'rect' ? 'Rechteck'
                : id === 'circle' ? 'Kreis'
                : 'Text',
            });
            const active = tool === id;
            return (
              <button
                key={id}
                type="button"
                onClick={() => setTool(id)}
                aria-pressed={active}
                title={label}
                className={
                  'flex h-8 w-8 cursor-pointer items-center justify-center rounded-md text-xs font-semibold transition ' +
                  (active
                    ? 'bg-accent/20 text-accent ring-2 ring-accent/40'
                    : 'bg-surface-3 text-fg-muted hover:bg-surface hover:text-fg')
                }
              >
                {toolGlyph(id)}
              </button>
            );
          })}
        </div>

        <div className="h-6 w-px bg-line/40" aria-hidden />

        <div className="flex items-center gap-1">
          {(['#ef4444', '#3b82f6', '#22c55e', '#facc15', '#a855f7', '#ffffff'] as AnnotatorColor[]).map((c) => {
            const active = color === c;
            return (
              <button
                key={c}
                type="button"
                onClick={() => setColor(c)}
                aria-pressed={active}
                aria-label={c}
                className={
                  'h-6 w-6 cursor-pointer rounded-full border-2 transition ' +
                  (active ? 'border-fg scale-110' : 'border-line/40 hover:scale-105')
                }
                style={{ backgroundColor: c }}
              />
            );
          })}
        </div>

        <div className="h-6 w-px bg-line/40" aria-hidden />

        <div className="flex items-center gap-1">
          {([2, 4, 8] as AnnotatorWidth[]).map((w) => {
            const active = width === w;
            return (
              <button
                key={w}
                type="button"
                onClick={() => setWidth(w)}
                aria-pressed={active}
                title={w + 'px'}
                className={
                  'flex h-8 w-8 cursor-pointer items-center justify-center rounded-md transition ' +
                  (active
                    ? 'bg-accent/20 ring-2 ring-accent/40'
                    : 'bg-surface-3 hover:bg-surface')
                }
              >
                <div
                  className="rounded-full bg-fg"
                  style={{ width: w + 2 + 'px', height: w + 2 + 'px' }}
                />
              </button>
            );
          })}
        </div>

        <div className="h-6 w-px bg-line/40" aria-hidden />

        <div className="flex items-center gap-1">
          <button
            type="button"
            onClick={handleUndo}
            disabled={ops.length === 0}
            title={t('app:annotator.undo', { defaultValue: 'Rückgängig (Ctrl+Z)' })}
            className="flex h-8 w-8 cursor-pointer items-center justify-center rounded-md bg-surface-3 text-fg-muted transition hover:bg-surface hover:text-fg disabled:cursor-not-allowed disabled:opacity-40"
          >
            ↶
          </button>
          <button
            type="button"
            onClick={handleRedo}
            disabled={redoStack.length === 0}
            title={t('app:annotator.redo', { defaultValue: 'Wiederholen (Ctrl+Y)' })}
            className="flex h-8 w-8 cursor-pointer items-center justify-center rounded-md bg-surface-3 text-fg-muted transition hover:bg-surface hover:text-fg disabled:cursor-not-allowed disabled:opacity-40"
          >
            ↷
          </button>
        </div>

        <div className="ml-auto flex items-center gap-2">
          <button
            type="button"
            onClick={handleReset}
            disabled={ops.length === 0}
            className="cursor-pointer rounded-md border border-line bg-surface-3 px-3 py-1.5 text-xs font-medium text-fg-muted transition hover:bg-surface disabled:cursor-not-allowed disabled:opacity-50"
          >
            {t('app:annotator.reset', { defaultValue: 'Zurücksetzen' })}
          </button>
          <button
            type="button"
            onClick={handleSave}
            disabled={!imageLoaded}
            className="cursor-pointer rounded-md bg-accent px-4 py-1.5 text-xs font-semibold text-accent-fg transition hover:bg-accent/90 disabled:cursor-not-allowed disabled:opacity-50"
          >
            {t('app:annotator.save', { defaultValue: 'Speichern' })}
          </button>
        </div>
      </footer>
    </div>
  );
}

function toolGlyph(t: AnnotatorTool): string {
  switch (t) {
    case 'pen': return '✎';
    case 'highlighter': return '🖍';
    case 'arrow': return '↗';
    case 'rect': return '▭';
    case 'circle': return '◯';
    case 'text': return 'T';
  }
}

function renderOp(ctx: CanvasRenderingContext2D, op: AnnotatorOp): void {
  ctx.save();
  ctx.lineCap = 'round';
  ctx.lineJoin = 'round';
  ctx.strokeStyle = op.color;
  ctx.fillStyle = op.color;
  ctx.lineWidth = op.width;

  switch (op.tool) {
    case 'pen': {
      const pts = op.points;
      if (!pts || pts.length < 1) break;
      ctx.beginPath();
      ctx.moveTo(pts[0]!.x, pts[0]!.y);
      for (let i = 1; i < pts.length; i++) {
        ctx.lineTo(pts[i]!.x, pts[i]!.y);
      }
      ctx.stroke();
      break;
    }
    case 'highlighter': {
      const pts = op.points;
      if (!pts || pts.length < 1) break;
      ctx.globalAlpha = 0.35;
      ctx.lineWidth = op.width * 4;
      ctx.beginPath();
      ctx.moveTo(pts[0]!.x, pts[0]!.y);
      for (let i = 1; i < pts.length; i++) {
        ctx.lineTo(pts[i]!.x, pts[i]!.y);
      }
      ctx.stroke();
      break;
    }
    case 'rect': {
      const { from, to } = op;
      if (!from || !to) break;
      ctx.strokeRect(
        Math.min(from.x, to.x),
        Math.min(from.y, to.y),
        Math.abs(to.x - from.x),
        Math.abs(to.y - from.y),
      );
      break;
    }
    case 'circle': {
      const { from, to } = op;
      if (!from || !to) break;
      const cx = (from.x + to.x) / 2;
      const cy = (from.y + to.y) / 2;
      const rx = Math.abs(to.x - from.x) / 2;
      const ry = Math.abs(to.y - from.y) / 2;
      ctx.beginPath();
      ctx.ellipse(cx, cy, rx, ry, 0, 0, Math.PI * 2);
      ctx.stroke();
      break;
    }
    case 'arrow': {
      const { from, to } = op;
      if (!from || !to) break;
      ctx.beginPath();
      ctx.moveTo(from.x, from.y);
      ctx.lineTo(to.x, to.y);
      ctx.stroke();
      const angle = Math.atan2(to.y - from.y, to.x - from.x);
      const head = Math.max(12, op.width * 3);
      ctx.beginPath();
      ctx.moveTo(to.x, to.y);
      ctx.lineTo(
        to.x - head * Math.cos(angle - Math.PI / 6),
        to.y - head * Math.sin(angle - Math.PI / 6),
      );
      ctx.moveTo(to.x, to.y);
      ctx.lineTo(
        to.x - head * Math.cos(angle + Math.PI / 6),
        to.y - head * Math.sin(angle + Math.PI / 6),
      );
      ctx.stroke();
      break;
    }
    case 'text': {
      const { at, text } = op;
      if (!at || !text) break;
      const fontSize = Math.max(14, op.width * 6);
      ctx.font = '600 ' + fontSize + 'px Inter, system-ui, sans-serif';
      ctx.textBaseline = 'top';
      ctx.fillText(text, at.x, at.y);
      break;
    }
  }

  ctx.restore();
}
