'use client';

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

const FA_DIGITS = ['۰', '۱', '۲', '۳', '۴', '۵', '۶', '۷', '۸', '۹'] as const;

function toAscii(str: string) {
  return str.replace(/[۰-۹]/g, (d) =>
    String(FA_DIGITS.indexOf(d as (typeof FA_DIGITS)[number])),
  );
}

function toFa(n: number) {
  return n.toString().replace(/\d/g, (d) => FA_DIGITS[Number(d)]);
}

/**
 * Parses a metric string like "18+", "۱۲ms", "99%", "۹۹٪" into a numeric
 * target plus a trailing suffix that survives the count animation.
 */
function parse(value: string) {
  const ascii = toAscii(value);
  const match = ascii.match(/^(\d+(?:\.\d+)?)(.*)$/);
  if (!match) return { target: 0, suffix: value, decimals: 0 };
  const target = parseFloat(match[1]);
  const decimals = match[1].includes('.') ? match[1].split('.')[1].length : 0;
  return { target, suffix: match[2], decimals };
}

const easeOutCubic = (t: number) => 1 - Math.pow(1 - t, 3);

type Props = {
  /** Final string, e.g. "18+", "۱۲ms", "99%" */
  value: string;
  /** Locale controls digit script in the rendered output */
  locale: 'fa' | 'en';
  /** Animation duration in ms */
  duration?: number;
  className?: string;
};

export function Counter({ value, locale, duration = 1600, className }: Props) {
  const { target, suffix, decimals } = parse(value);
  const [display, setDisplay] = useState(0);
  const elRef = useRef<HTMLSpanElement>(null);
  const started = useRef(false);

  useEffect(() => {
    const el = elRef.current;
    if (!el) return;

    const start = () => {
      if (started.current) return;
      started.current = true;
      const t0 = performance.now();
      const tick = (now: number) => {
        const p = Math.min(1, (now - t0) / duration);
        const eased = easeOutCubic(p);
        setDisplay(target * eased);
        if (p < 1) requestAnimationFrame(tick);
        else setDisplay(target);
      };
      requestAnimationFrame(tick);
    };

    if (typeof IntersectionObserver === 'undefined') {
      start();
      return;
    }

    const io = new IntersectionObserver(
      (entries) => {
        for (const e of entries) {
          if (e.isIntersecting) {
            start();
            io.disconnect();
            break;
          }
        }
      },
      { threshold: 0.4 },
    );
    io.observe(el);
    return () => io.disconnect();
  }, [target, duration]);

  const formatted = decimals
    ? display.toFixed(decimals)
    : Math.round(display).toString();
  const rendered = locale === 'fa' ? toFa(Number(formatted)) : formatted;
  const sfx = locale === 'fa' ? suffix : toAscii(suffix);

  return (
    <span ref={elRef} className={className}>
      {rendered}
      {sfx}
    </span>
  );
}
