import React, { useEffect, useRef, useMemo } from 'react';
import { sanitizeColor, clampNumber, safeBoolean } from './types';
import { useAnimationLoop, getMobileDpr } from '@/hooks/useAnimationLoop';

interface Props {
  settings: Record<string, unknown>;
}

const COLORS = [
  '#93c5fd',
  '#f9a8d4',
  '#86efac',
  '#fde047',
  '#fca5a5',
  '#d8b4fe',
  '#a5b4fc',
  '#c4b5fd',
];

function hexToRgb(hex: string): [number, number, number] {
  let value = hex.replace('#', '');
  if (value.length === 3)
    value = value
      .split('')
      .map((c) => c + c)
      .join('');
  if (!/^[0-9a-fA-F]{6}$/.test(value)) return [129, 140, 248];
  const v = parseInt(value, 16);
  return [(v >> 16) & 255, (v >> 8) & 255, v & 255];
}

interface CellData {
  rgb: [number, number, number];
  phase: number;
  period: number;
}

const SKEW_X_TAN = Math.tan((-48 * Math.PI) / 180);
const SKEW_Y_TAN = Math.tan((14 * Math.PI) / 180);
const GRID_SCALE = 0.675;

interface BoxesState {
  ctx: CanvasRenderingContext2D;
  w: number;
  h: number;
}

export default React.memo(function BackgroundBoxes({ settings }: Props) {
  const canvasRef = useRef<HTMLCanvasElement>(null);
  const stateRef = useRef<BoxesState | null>(null);
  const rows = clampNumber(settings.rows, 4, 30, 15);
  const cols = clampNumber(settings.cols, 4, 30, 15);
  const boxColor = sanitizeColor(settings.boxColor, '#818cf8');
  const lineColor = sanitizeColor(settings.lineColor, '#334155');
  const multicolor =
    settings.multicolor === undefined
      ? boxColor === '#818cf8'
      : safeBoolean(settings.multicolor, true);

  const cells = useMemo((): CellData[] => {
    return Array.from({ length: rows * cols }, () => ({
      rgb: hexToRgb(multicolor ? COLORS[Math.floor(Math.random() * COLORS.length)] : boxColor),
      phase: Math.random() * 8,
      period: 3 + Math.random() * 4,
    }));
  }, [rows, cols, boxColor, multicolor]);

  useEffect(() => {
    const canvas = canvasRef.current;
    if (!canvas) return;
    const dpr = getMobileDpr();
    const parent = canvas.parentElement;
    if (!parent) return;
    const rect = parent.getBoundingClientRect();
    canvas.width = rect.width * dpr;
    canvas.height = rect.height * dpr;

    const ctx = canvas.getContext('2d');
    if (!ctx) return;

    stateRef.current = { ctx, w: canvas.width, h: canvas.height };

    const onResize = () => {
      const r = parent.getBoundingClientRect();
      canvas.width = r.width * dpr;
      canvas.height = r.height * dpr;
      if (stateRef.current) {
        stateRef.current.w = canvas.width;
        stateRef.current.h = canvas.height;
      }
    };

    window.addEventListener('resize', onResize);
    return () => window.removeEventListener('resize', onResize);
  }, []);

  useAnimationLoop(
    (now) => {
      const state = stateRef.current;
      if (!state) return;

      const { ctx, w, h } = state;
      const t = now * 0.001;

      ctx.clearRect(0, 0, w, h);
      ctx.save();

      const cx = w / 2;
      const cy = h / 2;
      ctx.translate(cx, cy);
      ctx.transform(1, 0, SKEW_X_TAN, 1, 0, 0);
      ctx.transform(1, SKEW_Y_TAN, 0, 1, 0, 0);
      ctx.scale(GRID_SCALE, GRID_SCALE);
      ctx.translate(-cx, -cy);

      const gw = w * 3;
      const gh = h * 3;
      const ox = -w;
      const oy = -h;
      const cellW = gw / cols;
      const cellH = gh / rows;

      for (let i = 0; i < cells.length; i++) {
        const cell = cells[i];
        const cycleT = ((t + cell.phase) % cell.period) / cell.period;
        const alpha = 0.15 * Math.sin(cycleT * Math.PI);
        if (alpha < 0.005) continue;

        const col = i % cols;
        const row = (i - col) / cols;
        const [r, g, b] = cell.rgb;

        ctx.fillStyle = `rgba(${r},${g},${b},${alpha})`;
        ctx.fillRect(ox + col * cellW, oy + row * cellH, cellW, cellH);
      }

      ctx.globalAlpha = 0.5;
      ctx.strokeStyle = lineColor;
      ctx.lineWidth = 1;
      ctx.beginPath();

      for (let r = 0; r <= rows; r++) {
        const y = oy + r * cellH;
        ctx.moveTo(ox, y);
        ctx.lineTo(ox + gw, y);
      }
      for (let c = 0; c <= cols; c++) {
        const x = ox + c * cellW;
        ctx.moveTo(x, oy);
        ctx.lineTo(x, oy + gh);
      }

      ctx.stroke();
      ctx.restore();
    },
    [cells, rows, cols, lineColor],
  );

  return (
    <canvas
      ref={canvasRef}
      style={{
        position: 'absolute',
        inset: 0,
        width: '100%',
        height: '100%',
      }}
    />
  );
});
