"use client";

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

export const money = (value: number) =>
  new Intl.NumberFormat("en-SG", {
    style: "currency",
    currency: "SGD",
    currencyDisplay: "narrowSymbol",
  }).format(value);

export function AnimatedMoney({ value }: { value: number }) {
  const [initialValue] = useState(value);
  const element = useRef<HTMLSpanElement>(null);
  const displayed = useRef(value);

  useEffect(() => {
    const node = element.current;
    if (!node) return;
    const preference = window.matchMedia("(prefers-reduced-motion: reduce)");
    const timing = getComputedStyle(node)
      .getPropertyValue("--motion-duration-value")
      .trim();
    const duration =
      (Number.parseFloat(timing) || 0) * (timing.endsWith("ms") ? 1 : 1000);
    const from = displayed.current;
    let start: number | undefined;
    let frame = 0;
    const finish = () => {
      cancelAnimationFrame(frame);
      displayed.current = value;
      node.textContent = money(value);
    };
    const tick = (now: number) => {
      start ??= now;
      const progress = Math.max(0, Math.min((now - start) / duration, 1));
      displayed.current = from + (value - from) * (1 - (1 - progress) ** 3);
      node.textContent = money(displayed.current);
      if (progress < 1) frame = requestAnimationFrame(tick);
      else finish();
    };
    if (preference.matches || duration <= 0 || from === value) finish();
    else frame = requestAnimationFrame(tick);
    const onPreferenceChange = () => {
      if (preference.matches) finish();
    };
    preference.addEventListener("change", onPreferenceChange);
    return () => {
      cancelAnimationFrame(frame);
      preference.removeEventListener("change", onPreferenceChange);
    };
  }, [value]);

  return (
    <>
      <span className="calculator-accessible-value">{money(value)}</span>
      <span ref={element} aria-hidden="true">
        {money(initialValue)}
      </span>
    </>
  );
}
