"use client";

import { useEffect, useId, useRef, useState } from "react";

type HoleRect = {
  x: number;
  y: number;
  width: number;
  height: number;
};

type StageThemeLayerProps = {
  enabled: boolean;
};

const VIEWBOX_WIDTH = 1280;
const VIEWBOX_HEIGHT = 720;
const HOLE_BLEED_PX = 1;

export function StageThemeLayer({ enabled }: StageThemeLayerProps) {
  const layerRef = useRef<SVGSVGElement | null>(null);
  const [holes, setHoles] = useState<HoleRect[]>([]);
  const reactId = useId().replace(/[^a-zA-Z0-9_-]/g, "");
  const maskId = `stage-theme-hole-${reactId}`;

  useEffect(() => {
    if (!enabled) {
      return;
    }

    const layer = layerRef.current;
    if (!layer) return;

    const stage = layer.closest(".stage");
    if (!stage) return;

    let frameId = 0;

    const updateHoles = () => {
      cancelAnimationFrame(frameId);
      frameId = requestAnimationFrame(() => {
        const layerRect = layer.getBoundingClientRect();
        if (!layerRect.width || !layerRect.height) return;

        const nextHoles = Array.from(stage.querySelectorAll<HTMLElement>(".result-panel-hidden .result-board"))
          .map((board) => {
            const boardRect = board.getBoundingClientRect();
            const left = Math.max(layerRect.left, boardRect.left - HOLE_BLEED_PX);
            const top = Math.max(layerRect.top, boardRect.top - HOLE_BLEED_PX);
            const right = Math.min(layerRect.right, boardRect.right + HOLE_BLEED_PX);
            const bottom = Math.min(layerRect.bottom, boardRect.bottom + HOLE_BLEED_PX);

            const x = ((left - layerRect.left) / layerRect.width) * VIEWBOX_WIDTH;
            const y = ((top - layerRect.top) / layerRect.height) * VIEWBOX_HEIGHT;
            const rectRight = ((right - layerRect.left) / layerRect.width) * VIEWBOX_WIDTH;
            const rectBottom = ((bottom - layerRect.top) / layerRect.height) * VIEWBOX_HEIGHT;
            const snappedX = Math.floor(x);
            const snappedY = Math.floor(y);

            return {
              x: snappedX,
              y: snappedY,
              width: Math.ceil(rectRight) - snappedX,
              height: Math.ceil(rectBottom) - snappedY,
            };
          })
          .filter((rect) => rect.width > 0 && rect.height > 0);

        setHoles(nextHoles);
      });
    };

    updateHoles();

    const resizeObserver = new ResizeObserver(updateHoles);
    resizeObserver.observe(stage);
    resizeObserver.observe(layer);

    const mutationObserver = new MutationObserver(updateHoles);
    mutationObserver.observe(stage, {
      attributes: true,
      attributeFilter: ["class", "style"],
      childList: true,
      subtree: true,
    });

    window.addEventListener("resize", updateHoles);

    return () => {
      cancelAnimationFrame(frameId);
      resizeObserver.disconnect();
      mutationObserver.disconnect();
      window.removeEventListener("resize", updateHoles);
    };
  }, [enabled]);

  if (!enabled) return null;

  return (
    <svg
      ref={layerRef}
      className="stage-theme-layer"
      viewBox={`0 0 ${VIEWBOX_WIDTH} ${VIEWBOX_HEIGHT}`}
      preserveAspectRatio="none"
      aria-hidden="true"
    >
      <defs>
        <mask id={maskId} maskUnits="userSpaceOnUse">
          <rect x="0" y="0" width={VIEWBOX_WIDTH} height={VIEWBOX_HEIGHT} fill="white" />
          {holes.map((hole, index) => (
            <rect
              key={index}
              x={hole.x}
              y={hole.y}
              width={hole.width}
              height={hole.height}
              fill="black"
              shapeRendering="crispEdges"
            />
          ))}
        </mask>
      </defs>
      <foreignObject x="0" y="0" width={VIEWBOX_WIDTH} height={VIEWBOX_HEIGHT} mask={`url(#${maskId})`}>
        <div className="stage-theme-fill" />
      </foreignObject>
    </svg>
  );
}
