import { useEffect, useRef } from "react";
import styles from "./DiceTray.module.css";

interface DiceRoll {
  /** Original dice expression, e.g. "1d20+3", "2d6+1" */
  expression: string;
  /** Individual die values from the server */
  rolls: number[];
  /** Character's HSL color string, e.g. "hsl(210, 70%, 50%)" */
  characterColor?: string;
  /** Unique ID to trigger re-rolls of the same expression */
  id: number;
}

interface DiceTrayProps {
  roll: DiceRoll | null;
  onAnimationComplete?: () => void;
}

interface DiceBoxThree {
  roll: (notation: string) => Promise<unknown[]>;
  clearDice: () => void;
  theme_customColorset: Record<string, string> | null;
  loadTheme: (config: Record<string, string>) => Promise<void>;
  DiceColors: { makeColorSet: (c: Record<string, string>) => Promise<unknown> };
  DiceFactory: { applyColorSet: (c: unknown) => void };
}

export default function DiceTray({ roll, onAnimationComplete }: DiceTrayProps) {
  const diceBoxRef = useRef<DiceBoxThree | null>(null);
  const initRef = useRef(false);
  const containerRef = useRef<HTMLDivElement>(null);
  const clearTimerRef = useRef<ReturnType<typeof setTimeout>>();
  const lastColorRef = useRef<string>("");
  const onCompleteRef = useRef(onAnimationComplete);
  onCompleteRef.current = onAnimationComplete;

  useEffect(() => {
    if (initRef.current) return;
    initRef.current = true;

    import("@3d-dice/dice-box-threejs")
      .then(async ({ default: DiceBox }) => {
        const box = new DiceBox("#dice-tray", {
          assetPath: "/dice/",
          theme_customColorset: {
            foreground: "#FFFFFF",
            background: "#8B2020",
            outline: "#FFFFFF",
            edge: "#4A0E0E",
            texture: "stone",
            material: "metal",
          },
          theme_surface: "default",
          gravity_multiplier: 400,
          light_intensity: 1.0,
          baseScale: 140,
          strength: 2,
          shadows: true,
          onRollComplete: () => {
            onCompleteRef.current?.();
            clearTimerRef.current = setTimeout(() => {
              box.clearDice();
              if (containerRef.current) {
                containerRef.current.classList.remove(styles.active);
              }
            }, 2500);
          },
        });

        await box.initialize();
        diceBoxRef.current = box as DiceBoxThree;
      })
      .catch((err) => {
        console.error("Failed to load dice-box-threejs:", err);
      });

    return () => {
      if (clearTimerRef.current) clearTimeout(clearTimerRef.current);
    };
  }, []);

  useEffect(() => {
    if (!roll || !diceBoxRef.current) return;

    const notation = buildNotation(roll.expression, roll.rolls);
    if (!notation) return;

    if (clearTimerRef.current) clearTimeout(clearTimerRef.current);

    if (containerRef.current) {
      containerRef.current.classList.add(styles.active);
    }

    const box = diceBoxRef.current;
    const bgColor = roll.characterColor
      ? hslToHex(roll.characterColor)
      : "#8B2020";

    // Update dice color if character changed
    if (bgColor !== lastColorRef.current) {
      lastColorRef.current = bgColor;
      const darkBg = darken(bgColor, 0.3);
      const colorset = {
        foreground: "#FFFFFF",
        background: darkBg,
        outline: "#FFFFFF",
        edge: darken(bgColor, 0.5),
        texture: "stone",
        material: "metal",
      };
      box.theme_customColorset = colorset;
      box.DiceColors.makeColorSet(colorset).then((cs) => {
        box.DiceFactory.applyColorSet(cs);
        box.roll(notation);
      });
    } else {
      box.roll(notation);
    }
  }, [roll]);

  return <div id="dice-tray" ref={containerRef} className={styles.tray} />;
}

function buildNotation(expression: string, rolls: number[]): string | null {
  const match = expression.match(/(\d*)d(\d+)/i);
  if (!match) return null;

  const sides = parseInt(match[2]);
  const validSides = [4, 6, 8, 10, 12, 20, 100];
  if (!validSides.includes(sides)) return null;

  if (rolls.length === 0) return null;

  // d100: show percentile die (tens) + d10 (ones) per roll
  // notation format: "3d100+3d10@t1,t2,t3,o1,o2,o3"
  if (sides === 100) {
    const n = rolls.length;
    const tens = rolls.map((v) => Math.floor(v / 10) * 10 || 100);
    const ones = rolls.map((v) => v % 10 || 10);
    const values = [...tens, ...ones].join(",");
    return `${n}d100+${n}d10@${values}`;
  }

  const count = rolls.length;
  const values = rolls.join(",");

  return `${count}d${sides}@${values}`;
}

/** Convert a color string (HSL or hex) to hex */
function hslToHex(color: string): string {
  // Already hex — pass through
  if (color.startsWith("#")) return color;

  const match = color.match(/hsl\((\d+),\s*(\d+)%,\s*(\d+)%\)/);
  if (!match) return "#8B2020";

  const h = parseInt(match[1]) / 360;
  const s = parseInt(match[2]) / 100;
  const l = parseInt(match[3]) / 100;

  const hue2rgb = (p: number, q: number, t: number) => {
    if (t < 0) t += 1;
    if (t > 1) t -= 1;
    if (t < 1 / 6) return p + (q - p) * 6 * t;
    if (t < 1 / 2) return q;
    if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;
    return p;
  };

  const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
  const p = 2 * l - q;
  const r = Math.round(hue2rgb(p, q, h + 1 / 3) * 255);
  const g = Math.round(hue2rgb(p, q, h) * 255);
  const b = Math.round(hue2rgb(p, q, h - 1 / 3) * 255);

  return `#${((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1)}`;
}

/** Darken a hex color by a factor (0 = no change, 1 = black) */
function darken(hex: string, factor: number): string {
  const r = parseInt(hex.slice(1, 3), 16);
  const g = parseInt(hex.slice(3, 5), 16);
  const b = parseInt(hex.slice(5, 7), 16);
  const dr = Math.round(r * (1 - factor));
  const dg = Math.round(g * (1 - factor));
  const db = Math.round(b * (1 - factor));
  return `#${((1 << 24) + (dr << 16) + (dg << 8) + db).toString(16).slice(1)}`;
}
