// Small shared primitives

function ShortHash({ hash, len = 6 }) {
  if (!hash) return null;
  const s = hash.startsWith('0x') ? hash.slice(2) : hash;
  const disp = `${hash.slice(0, len + 2)}…${s.slice(-len)}`;
  return <span className="mono">{disp}</span>;
}

// Text scramble that settles on the final string
function ScrambleHash({ target, duration = 1600, className = '' }) {
  const [text, setText] = React.useState(target);
  const startRef = React.useRef(null);
  const rafRef = React.useRef(null);
  const chars = '0123456789abcdef';

  React.useEffect(() => {
    startRef.current = performance.now();
    const tick = (t) => {
      const el = (t - startRef.current) / duration;
      if (el >= 1) { setText(target); return; }
      // reveal from left to right
      const revealed = Math.floor(el * target.length);
      let out = target.slice(0, revealed);
      for (let i = revealed; i < target.length; i++) {
        const c = target[i];
        if (c === 'x' || c === '0' && i === 0) out += c;
        else out += chars[Math.floor(Math.random() * chars.length)];
      }
      setText(out);
      rafRef.current = requestAnimationFrame(tick);
    };
    rafRef.current = requestAnimationFrame(tick);
    return () => cancelAnimationFrame(rafRef.current);
  }, [target, duration]);

  return <span className={'scramble-hash ' + className}>{text}</span>;
}

const CHAIN_BADGES = {
  ethereum:  { cls: 'badge-eth',       label: 'Ξ Ethereum' },
  polygon:   { cls: 'badge-polygon',   label: '◇ Polygon' },
  avalanche: { cls: 'badge-avalanche', label: '▲ Avalanche' },
  arbitrum:  { cls: 'badge-arbitrum',  label: '◈ Arbitrum' },
  base:      { cls: 'badge-base',      label: '○ Base' },
};

function ChainBadge({ chain }) {
  const meta = CHAIN_BADGES[chain];
  if (!meta) return <span className="badge">{chain}</span>;
  return <span className={'badge ' + meta.cls}>{meta.label}</span>;
}

function StatusBadge({ status }) {
  const { t } = useI18n();
  if (status === 'sealed')      return <span className="badge badge-verified"><span className="dot-verified" /> {t('common.sealed')}</span>;
  if (status === 'in-progress') return <span className="badge"><span className="dot-pending" /> {t('common.inProgress')}</span>;
  return <span className="badge">{t('common.draft')}</span>;
}

function IconArrowRight() {
  return (
    <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.6">
      <path d="M4 12h16m-6-6l6 6-6 6" />
    </svg>
  );
}

// `navigator.clipboard` only exists in a secure context, and this app is
// routinely reached over plain http / a raw IP / a tunnel URL — the same
// reason merkle.js avoids crypto.subtle. So try the modern API, then fall
// back to the old execCommand trick rather than silently doing nothing.
async function copyToClipboard(text) {
  try {
    if (navigator.clipboard && window.isSecureContext) {
      await navigator.clipboard.writeText(text);
      return true;
    }
  } catch (_) { /* fall through to the legacy path */ }

  try {
    const ta = document.createElement('textarea');
    ta.value = text;
    ta.setAttribute('readonly', '');
    ta.style.position = 'fixed';
    ta.style.top = '-1000px';
    ta.style.opacity = '0';
    document.body.appendChild(ta);
    ta.select();
    ta.setSelectionRange(0, ta.value.length);
    const ok = document.execCommand('copy');
    document.body.removeChild(ta);
    return ok;
  } catch (_) {
    return false;
  }
}

// A button that copies `text` and confirms by swapping its own label for a
// moment — there's no toast/snackbar in this app, and the button is already
// where the user is looking.
function CopyButton({ text, label, className = 'btn btn-secondary btn-sm', style, title }) {
  const { t } = useI18n();
  const [state, setState] = React.useState(null); // null | 'ok' | 'fail'
  const timerRef = React.useRef(null);

  React.useEffect(() => () => clearTimeout(timerRef.current), []);

  const onClick = async () => {
    const ok = await copyToClipboard(text);
    setState(ok ? 'ok' : 'fail');
    clearTimeout(timerRef.current);
    timerRef.current = setTimeout(() => setState(null), 1600);
  };

  return (
    <button className={className} style={style} title={title} onClick={onClick}>
      {state === 'ok' ? t('common.copied') : state === 'fail' ? t('common.copyFailed') : label}
    </button>
  );
}

function fmtDate(iso, opts = {}) {
  const d = new Date(iso);
  const y = d.getFullYear();
  const m = String(d.getMonth() + 1).padStart(2, '0');
  const day = String(d.getDate()).padStart(2, '0');
  const hh = String(d.getHours()).padStart(2, '0');
  const mm = String(d.getMinutes()).padStart(2, '0');
  if (opts.dateOnly) return `${y}.${m}.${day}`;
  return `${y}.${m}.${day} · ${hh}:${mm}`;
}
function fmtTimeUTC(iso) {
  const d = new Date(iso);
  return d.toISOString().replace('T',' ').slice(0,19) + ' UTC';
}

Object.assign(window, { ShortHash, ScrambleHash, ChainBadge, CHAIN_BADGES, StatusBadge, IconArrowRight, fmtDate, fmtTimeUTC, copyToClipboard, CopyButton });
