import React, { useEffect, useMemo, useRef, useState } from 'react';
import { createPortal } from 'react-dom';
import { EmojiConfettiBurst } from './types';
import { findJumboEmojiElement, getLocalBurstCanvasSize } from './findJumboMount';
import {
  BurstParticle,
  drawBurstParticles,
  spawnEmojiBurst,
  stepBurstParticles,
} from './emojiBurstEngine';

const MAX_DPR = 2;

type EmojiConfettiBurstCanvasProps = {
  burst: EmojiConfettiBurst;
  onComplete: (burstId: string) => void;
};

export function EmojiConfettiBurstCanvas({ burst, onComplete }: EmojiConfettiBurstCanvasProps) {
  const canvasRef = useRef<HTMLCanvasElement>(null);
  const layerRef = useRef<HTMLDivElement>(null);
  const particlesRef = useRef<BurstParticle[]>([]);
  const frameRef = useRef<number | null>(null);
  const lastFrameTimeRef = useRef<number | null>(null);
  const spawnedRef = useRef(false);
  const onCompleteRef = useRef(onComplete);
  onCompleteRef.current = onComplete;

  const canvasSize = getLocalBurstCanvasSize(burst.origin.maskRadius ?? 36);
  const localOrigin = useMemo(
    () => ({
      x: canvasSize / 2,
      y: canvasSize / 2,
      maskRadius: burst.origin.maskRadius ?? 36,
    }),
    [burst.origin.maskRadius, canvasSize]
  );

  const updateLayerPosition = () => {
    const layer = layerRef.current;
    if (!layer) return false;

    const jumbo = findJumboEmojiElement(burst.targetEventId);
    if (!jumbo) return false;

    const rect = jumbo.getBoundingClientRect();
    layer.style.left = `${rect.left + rect.width / 2 - canvasSize / 2}px`;
    layer.style.top = `${rect.top + rect.height / 2 - canvasSize / 2}px`;
    return true;
  };

  useEffect(() => {
    const onScrollOrResize = () => {
      updateLayerPosition();
    };

    window.addEventListener('scroll', onScrollOrResize, true);
    window.addEventListener('resize', onScrollOrResize);

    return () => {
      window.removeEventListener('scroll', onScrollOrResize, true);
      window.removeEventListener('resize', onScrollOrResize);
    };
  }, [burst.targetEventId, canvasSize]);

  useEffect(() => {
    if (!canvasRef.current || spawnedRef.current) return undefined;

    const reducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
    if (reducedMotion) {
      onCompleteRef.current(burst.id);
      return undefined;
    }

    let frameCleanup: (() => void) | undefined;

    const start = (): boolean => {
      if (spawnedRef.current || !canvasRef.current) return false;
      if (!updateLayerPosition()) return false;

      spawnedRef.current = true;
      const canvas = canvasRef.current;
      const context = canvas.getContext('2d');
      if (!context) return false;

      const dpr = Math.min(window.devicePixelRatio || 1, MAX_DPR);
      canvas.width = Math.round(canvasSize * dpr);
      canvas.height = Math.round(canvasSize * dpr);
      canvas.style.width = `${canvasSize}px`;
      canvas.style.height = `${canvasSize}px`;

      spawnEmojiBurst(
        particlesRef.current,
        burst.id,
        localOrigin,
        burst.emojis[0] ?? '🎉',
        performance.now()
      );

      const tick = (now: number) => {
        updateLayerPosition();

        const last = lastFrameTimeRef.current ?? now;
        const dtSeconds = Math.min((now - last) / 1000, 0.05);
        lastFrameTimeRef.current = now;

        stepBurstParticles(particlesRef.current, now, dtSeconds);

        context.setTransform(1, 0, 0, 1, 0, 0);
        context.clearRect(0, 0, canvas.width, canvas.height);

        if (particlesRef.current.length > 0) {
          drawBurstParticles(context, particlesRef.current, dpr, now);
          frameRef.current = requestAnimationFrame(tick);
          return;
        }

        onCompleteRef.current(burst.id);
        frameRef.current = null;
        lastFrameTimeRef.current = null;
      };

      lastFrameTimeRef.current = performance.now();
      frameRef.current = requestAnimationFrame(tick);

      frameCleanup = () => {
        if (frameRef.current !== null) {
          cancelAnimationFrame(frameRef.current);
          frameRef.current = null;
        }
      };

      return true;
    };

    if (start()) {
      return frameCleanup;
    }

    let attempt = 0;
    const retry = window.setInterval(() => {
      attempt += 1;
      if (start() || attempt >= 10) {
        window.clearInterval(retry);
        if (attempt >= 10 && !spawnedRef.current) {
          onCompleteRef.current(burst.id);
        }
      }
    }, 50);

    return () => {
      window.clearInterval(retry);
      frameCleanup?.();
    };
  }, [burst.emojis, burst.id, burst.targetEventId, canvasSize, localOrigin]);

  return createPortal(
    <div
      ref={layerRef}
      aria-hidden
      style={{
        position: 'fixed',
        width: canvasSize,
        height: canvasSize,
        pointerEvents: 'none',
        zIndex: 4,
      }}
    >
      <canvas
        ref={canvasRef}
        style={{
          position: 'absolute',
          inset: 0,
          pointerEvents: 'none',
        }}
      />
    </div>,
    document.body
  );
}
