import { useCallback, useEffect, useRef, useState } from 'react';

import { useAuth } from '../context/AuthContext';
import { supabase } from '../lib/supabase';
import { PencilIcon, XIcon } from './icons';

interface Stroke {
  id: string;
  userId: string;
  color: string;
  // normalized 0..1 coordinates so any viewer's canvas size renders consistently
  points: Array<[number, number]>;
  bornAt: number;
}

interface Props {
  /** Stable per-share key. Use the share's participantId. */
  shareKey: string;
  /** Render annotations transparently (off when the toolbar is closed). */
  enabled: boolean;
  onToggleEnabled: (next: boolean) => void;
}

const FADE_MS = 8000;
const COLORS = ['#ef4444', '#3b82f6', '#22c55e', '#facc15', '#a855f7', '#000000'] as const;

export function ScreenShareAnnotations({ shareKey, enabled, onToggleEnabled }: Props) {
  const { session } = useAuth();
  const userId = session?.user.id ?? 'anon';
  const [color, setColor] = useState<string>(COLORS[0]);
  const [strokes, setStrokes] = useState<Stroke[]>([]);
  const draftRef = useRef<Stroke | null>(null);
  const containerRef = useRef<HTMLDivElement | null>(null);
  const canvasRef = useRef<HTMLCanvasElement | null>(null);

  const broadcastRef = useRef<((s: Stroke) => void) | null>(null);

  // Subscribe to remote strokes.
  useEffect(() => {
    const channel = supabase.channel('screen-annotation:' + shareKey, {
      config: { broadcast: { self: false } },
    });
    channel.on('broadcast', { event: 'stroke' }, (payload) => {
      const s = payload.payload as Stroke | undefined;
      if (!s || s.userId === userId) return;
      setStrokes((prev) => [...prev, { ...s, bornAt: Date.now() }]);
    });
    channel.subscribe();
    broadcastRef.current = (s: Stroke) => {
      void channel.send({
        type: 'broadcast',
        event: 'stroke',
        payload: s,
      });
    };
    return () => {
      broadcastRef.current = null;
      void supabase.removeChannel(channel);
    };
  }, [shareKey, userId]);

  // Garbage-collect faded strokes after FADE_MS + a small grace window.
  useEffect(() => {
    if (strokes.length === 0) return;
    const id = setInterval(() => {
      const cutoff = Date.now() - FADE_MS - 500;
      setStrokes((prev) => {
        const next = prev.filter((s) => s.bornAt > cutoff);
        return next.length === prev.length ? prev : next;
      });
    }, 1000);
    return () => clearInterval(id);
  }, [strokes.length]);

  // Paint the canvas on every render tick.
  useEffect(() => {
    const cv = canvasRef.current;
    const container = containerRef.current;
    if (!cv || !container) return;
    const rect = container.getBoundingClientRect();
    if (cv.width !== rect.width || cv.height !== rect.height) {
      cv.width = Math.max(1, Math.floor(rect.width));
      cv.height = Math.max(1, Math.floor(rect.height));
    }
    const ctx = cv.getContext('2d');
    if (!ctx) return;
    ctx.clearRect(0, 0, cv.width, cv.height);
    const now = Date.now();
    const drawStroke = (s: Stroke) => {
      const age = now - s.bornAt;
      const alpha = Math.max(0, 1 - age / FADE_MS);
      if (alpha <= 0) return;
      ctx.save();
      ctx.globalAlpha = alpha;
      ctx.strokeStyle = s.color;
      ctx.lineWidth = 3;
      ctx.lineCap = 'round';
      ctx.lineJoin = 'round';
      ctx.beginPath();
      for (let i = 0; i < s.points.length; i++) {
        const [nx, ny] = s.points[i]!;
        const x = nx * cv.width;
        const y = ny * cv.height;
        if (i === 0) ctx.moveTo(x, y);
        else ctx.lineTo(x, y);
      }
      ctx.stroke();
      ctx.restore();
    };
    for (const s of strokes) drawStroke(s);
    if (draftRef.current) drawStroke(draftRef.current);
  });

  // Animation frame loop so faded strokes visually decay between paints.
  useEffect(() => {
    if (strokes.length === 0 && !draftRef.current) return;
    let raf = 0;
    const tick = () => {
      // Nudge state to force a re-paint. Slightly hacky but cheaper than a
      // dedicated refresh state.
      setStrokes((prev) => prev.slice());
      raf = window.requestAnimationFrame(tick);
    };
    raf = window.requestAnimationFrame(tick);
    return () => window.cancelAnimationFrame(raf);
  }, [strokes.length]);

  const normalized = useCallback((e: React.PointerEvent<HTMLDivElement>) => {
    const container = containerRef.current;
    if (!container) return [0, 0] as [number, number];
    const rect = container.getBoundingClientRect();
    const nx = (e.clientX - rect.left) / rect.width;
    const ny = (e.clientY - rect.top) / rect.height;
    return [Math.min(1, Math.max(0, nx)), Math.min(1, Math.max(0, ny))] as [number, number];
  }, []);

  const onPointerDown = (e: React.PointerEvent<HTMLDivElement>) => {
    if (!enabled) return;
    (e.currentTarget as HTMLDivElement).setPointerCapture(e.pointerId);
    draftRef.current = {
      id: Math.random().toString(36).slice(2),
      userId,
      color,
      points: [normalized(e)],
      bornAt: Date.now(),
    };
  };

  const onPointerMove = (e: React.PointerEvent<HTMLDivElement>) => {
    if (!draftRef.current) return;
    draftRef.current.points.push(normalized(e));
    setStrokes((prev) => prev.slice()); // cheap re-render trigger
  };

  const onPointerUp = (e: React.PointerEvent<HTMLDivElement>) => {
    const target = e.currentTarget as HTMLDivElement;
    if (target.hasPointerCapture(e.pointerId)) target.releasePointerCapture(e.pointerId);
    const draft = draftRef.current;
    draftRef.current = null;
    if (!draft || draft.points.length < 2) {
      setStrokes((prev) => prev.slice());
      return;
    }
    setStrokes((prev) => [...prev, draft]);
    broadcastRef.current?.(draft);
  };

  return (
    <>
      <div
        ref={containerRef}
        onPointerDown={onPointerDown}
        onPointerMove={onPointerMove}
        onPointerUp={onPointerUp}
        onPointerLeave={onPointerUp}
        className={
          'absolute inset-0 z-10 ' +
          (enabled ? 'cursor-crosshair touch-none' : 'pointer-events-none')
        }
      >
        <canvas
          ref={canvasRef}
          className="pointer-events-none absolute inset-0 h-full w-full"
        />
      </div>
      <div className="absolute right-3 top-12 z-20 flex flex-col items-end gap-1">
        <button
          type="button"
          onClick={() => onToggleEnabled(!enabled)}
          aria-pressed={enabled}
          title={enabled ? 'Annotation aus' : 'Annotation an'}
          className={
            'flex h-8 w-8 cursor-pointer items-center justify-center rounded-full text-white shadow-lg transition ' +
            (enabled ? 'bg-accent' : 'bg-black/60 hover:bg-black/80')
          }
        >
          {enabled ? <XIcon className="h-3.5 w-3.5" /> : <PencilIcon className="h-3.5 w-3.5" />}
        </button>
        {enabled && (
          <div className="flex items-center gap-1 rounded-full bg-black/60 p-1 shadow-lg">
            {COLORS.map((c) => (
              <button
                key={c}
                type="button"
                onClick={() => setColor(c)}
                aria-label={c}
                aria-pressed={c === color}
                className={
                  'h-5 w-5 cursor-pointer rounded-full border-2 transition ' +
                  (c === color ? 'border-white scale-110' : 'border-white/30 hover:scale-105')
                }
                style={{ backgroundColor: c }}
              />
            ))}
          </div>
        )}
      </div>
    </>
  );
}
