import { useEffect, useRef } from 'react';

const createCanvasConfetti = () => {
  const confetti = document.createElement('canvas');
  confetti.id = 'confetti';
  document.body.appendChild(confetti);
  confetti.style.position = 'fixed';
  confetti.style.left = '0';
  confetti.style.top = '0';
  confetti.style.pointerEvents = 'none';
  return confetti;
};

const useConfetti = (ref: React.RefObject<HTMLBaseElement>) => {
  const requestRef = useRef<any>();

  useEffect(() => {
    let confetti = document.getElementById('confetti') as HTMLCanvasElement;
    if (!ref) return;

    const x = ref?.current?.getBoundingClientRect().left;
    const y = ref?.current?.getBoundingClientRect().top;

    if (!confetti) {
      confetti = createCanvasConfetti();
    }

    if (!confetti) return;
    confetti.width = document.body.clientWidth;
    confetti.height = document.body.clientHeight;
    confetti.style.zIndex = '99999';

    // global variables
    const confettiCtx = confetti.getContext('2d');
    if (!confettiCtx) return;

    const confettiElements: any[] = [];

    const container = {
      w: confetti.clientWidth,
      h: confetti.clientHeight,
      x: undefined,
      y: undefined,
    };

    // helper
    const rand = (min: number, max: number) => Math.random() * (max - min) + min;

    const confettiParams = {
      // number of confetti per "explosion"
      number: 200,
      // min and max size for each rectangle
      size: { x: [5, 20], y: [10, 18] },
      // power of explosion
      initSpeed: 15,
      // defines how fast particles go down after blast-off
      gravity: 0.15,
      // how wide is explosion
      drag: 0.04,
      // how slow particles are falling
      terminalVelocity: 6,
      // how fast particles are rotating around themselves
      flipSpeed: 0.017,
    };
    const colors = [
      { front: '#14CC9E', back: '#0E8C6D' },
      { front: '#F2C53D', back: '#73500B' },
      { front: '#E065C2', back: '#591B3F' },
      { front: '#A63275', back: '#591B3F' },
      { front: '#664E8B', back: '#3d2f53' },
      { front: '#4CA6FF', back: '#200336622f48' },
      { front: '#0E8C6D', back: '#045944' },
    ];

    function Conf(this: any) {
      this.randomModifier = rand(-1, 1);
      this.colorPair = colors[Math.floor(rand(0, colors.length))];
      this.dimensions = {
        x: rand(confettiParams.size.x[0], confettiParams.size.x[1]),
        y: rand(confettiParams.size.y[0], confettiParams.size.y[1]),
      };
      this.position = {
        x,
        y,
      };
      this.rotation = rand(0, 2 * Math.PI);
      this.scale = { x: 1, y: 1 };
      this.velocity = {
        x: rand(-confettiParams.initSpeed, confettiParams.initSpeed) * 1,
        y: rand(-confettiParams.initSpeed, confettiParams.initSpeed),
      };
      this.flipSpeed = rand(0.2, 1.5) * confettiParams.flipSpeed;

      if (this.position.y <= container.h) {
        this.velocity.y = -Math.abs(this.velocity.y);
      }

      this.terminalVelocity = rand(1, 1.5) * confettiParams.terminalVelocity;

      this.update = function () {
        this.velocity.x *= 0.98;
        this.position.x += this.velocity.x;

        this.velocity.y += this.randomModifier * confettiParams.drag;
        this.velocity.y += confettiParams.gravity;
        this.velocity.y = Math.min(this.velocity.y, this.terminalVelocity);
        this.position.y += this.velocity.y;

        this.scale.y = Math.cos((this.position.y + this.randomModifier) * this.flipSpeed);
        this.color = this.scale.y > 0 ? this.colorPair.front : this.colorPair.back;
      };
    }

    function updateConfetti() {
      if (!confettiCtx) return;
      confettiCtx.clearRect(0, 0, container.w, container.h);

      confettiElements.forEach((c) => {
        c.update();
        confettiCtx.translate(c.position.x, c.position.y);
        confettiCtx.rotate(c.rotation);
        const width = c.dimensions.x * c.scale.x;
        const height = c.dimensions.y * c.scale.y;
        confettiCtx.fillStyle = c.color;
        confettiCtx.fillRect(-0.5 * width, -0.5 * height, width, height);
        confettiCtx.setTransform(1, 0, 0, 1, 0, 0);
      });

      confettiElements.forEach((c, idx) => {
        if (
          c.position.y > container.h ||
          c.position.x < -0.5 * (container as any).x ||
          c.position.x > 1.5 * (container as any).x
        ) {
          confettiElements.splice(idx, 1);
        }
      });

      requestRef.current = window.requestAnimationFrame(updateConfetti);
    }

    function addConfetti() {
      for (let i = 0; i < confettiParams.number; i++) {
        confettiElements.push(new (Conf as any)());
      }
    }

    setTimeout(() => {
      addConfetti();
      updateConfetti();
    }, 400);

    return () => {
      cancelAnimationFrame(requestRef.current);
      try {
        confetti && document.body.removeChild(confetti);
      } catch (e) {
        console.log(e);
      }
    };
  }, [ref]);
};

export default useConfetti;
