"use client";

import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import { ArrowDownRight, ArrowUpRight, Check, ChevronDown, Copy, Download, GitFork, Languages, Maximize2, Minus, Plus, RefreshCw, X } from "lucide-react";
import { Button } from "@/components/ui/button";
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
import { copy, isLanguage, languagePath, languages, type Language } from "@/lib/i18n";
import { experience } from "@/lib/experience-copy";
import { CLONE_COMMAND, REPOSITORY_NAME } from "@/lib/site-config";
import { type LiquidityNode, type LiquidityPayload, type TokenCategory } from "@/lib/liquidity";
import { diagramSize, layoutNetwork, nodeRadius, palette, sameClass, tickerLabel } from "@/lib/network-layout";
import { isSnapshotSet, mergeSnapshots, type SnapshotSet } from "@/lib/liquidity-set";
import { FRESH_MS } from "@/lib/feed-config";
import { saveSnapshot, storedSnapshot, snapshotStorageKey } from "@/lib/liquidity-client";
import { CHAINS, DEFAULT_CHAIN, getChain, type ChainId } from "@/lib/chains";
import { chainCopy, feedCopy } from "@/lib/chain-copy";
import sourceManifest from "@/lib/source-manifest.json";

type Metric = "volume24h" | "liquidity";
const localeFor = (language: Language) => languages.find(l => l.code === language)!.htmlLang;
function compact(n: number, money = false) {
  return new Intl.NumberFormat("en", { notation: "compact", maximumFractionDigits: n >= 1e9 ? 2 : 1, ...(money ? { style: "currency", currency: "USD" } : {}) }).format(n);
}
function ageLabel(timestamp: string, now: number | null, language: Language) {
  if (now === null) return new Intl.DateTimeFormat(localeFor(language), { month: "short", day: "numeric", timeZone: "UTC" }).format(new Date(timestamp));
  const seconds = Math.max(0, (now - Date.parse(timestamp)) / 1000);
  const [unit, amount] = seconds >= 86400 ? ["day", seconds / 86400] : seconds >= 3600 ? ["hour", seconds / 3600] : seconds >= 60 ? ["minute", seconds / 60] : ["second", seconds];
  return new Intl.RelativeTimeFormat(localeFor(language), { numeric: "auto", style: "short" }).format(-Math.floor(amount as number), unit as Intl.RelativeTimeFormatUnit);
}
const contract = (node: LiquidityNode) => node.id.replace(/^[^_]+_/, "");
const shortAddress = (node: LiquidityNode) => `${contract(node).slice(0, 6)}…${contract(node).slice(-4)}`;

async function copyText(value: string): Promise<boolean> {
  try {
    if (navigator.clipboard && window.isSecureContext) { await navigator.clipboard.writeText(value); return true; }
    const field = document.createElement("textarea");
    field.value = value; field.style.cssText = "position:fixed;opacity:0;left:0;top:0";
    document.body.appendChild(field); field.select();
    const success = document.execCommand("copy"); field.remove(); return success;
  } catch { return false; }
}

function NetworkDiagram({ data, metric, language, mobile, selected, onSelect }: {
  data: LiquidityPayload; metric: Metric; language: Language; mobile: boolean;
  selected: string | null; onSelect: (id: string | null) => void;
}) {
  const t = copy[language], x = experience[language];
  const { width, height } = diagramSize(data.nodes.length, mobile);
  const [hover, setHover] = useState<string | null>(null);
  const [view, setView] = useState({ zoom: 1, x: 0, y: 0 });
  const drag = useRef<{ x: number; y: number; panX: number; panY: number; moved: boolean } | null>(null);
  const positions = useMemo(() => layoutNetwork(data.nodes, data.edges, mobile), [data.nodes, data.edges, mobile]);
  const nodes = useMemo(() => new Map(data.nodes.map(n => [n.id, n])), [data.nodes]);
  const maxNode = Math.max(...data.nodes.map(n => n[metric]), 1);
  const maxEdge = Math.max(...data.edges.map(e => e[metric]), 1);
  const focus = hover ?? selected;
  const connected = new Set([focus, ...data.edges.filter(e => e.source === focus || e.target === focus).flatMap(e => [e.source, e.target])]);
  const zoomBy = (delta: number) => setView(v => ({ x: 0, y: 0, zoom: Math.max(1, Math.min(3, v.zoom + delta)) }));
  const minX = width * (1 - 1 / view.zoom) / 2 + view.x;
  const minY = height * (1 - 1 / view.zoom) / 2 + view.y;
  const selectedNode = selected ? nodes.get(selected) : null;
  return <div className={`diagram ${mobile ? "diagram-mobile" : "diagram-desktop"}`}>
    <svg style={mobile ? { aspectRatio: `${width}/${height}` } : undefined} viewBox={`${minX} ${minY} ${width / view.zoom} ${height / view.zoom}`}
      className={view.zoom > 1 ? "is-zoomed" : ""} role="group" aria-label={x.network}
      onPointerDown={event => {
        if (view.zoom <= 1) return;
        drag.current = { x: event.clientX, y: event.clientY, panX: view.x, panY: view.y, moved: false };
      }}
      onPointerMove={event => {
        const start = drag.current;
        if (!start) return;
        const dx = event.clientX - start.x, dy = event.clientY - start.y;
        if (Math.abs(dx) + Math.abs(dy) > 5) { start.moved = true; event.currentTarget.setPointerCapture(event.pointerId); }
        const ratio = width / event.currentTarget.getBoundingClientRect().width / view.zoom;
        const bx = width * (1 - 1 / view.zoom) / 2, by = height * (1 - 1 / view.zoom) / 2;
        setView(v => ({ ...v, x: Math.max(-bx, Math.min(bx, start.panX - dx * ratio)), y: Math.max(-by, Math.min(by, start.panY - dy * ratio)) }));
      }}
      onPointerUp={() => { if (drag.current) setTimeout(() => { drag.current = null; }, 0); }}
      onPointerCancel={() => { drag.current = null; }}
      onPointerLeave={() => setHover(null)}>
      <title>{t.title}</title>
      <desc>{x.method}</desc>
      <g className="edges" aria-hidden="true">
        {[...data.edges].sort((a, b) => a[metric] - b[metric]).map(e => {
          const a = positions.get(e.source)!, b = positions.get(e.target)!;
          const related = !focus || e.source === focus || e.target === focus;
          const same = sameClass(nodes.get(e.source)!, nodes.get(e.target)!);
          const stroke = same ? "#12a684" : "#d74c9b";
          const weight = .65 + Math.sqrt(e[metric] / maxEdge) * (mobile ? 8 : 13);
          return <g key={e.id} className={related ? "edge" : "edge muted-edge"}>
            <line x1={a.x} y1={a.y} x2={b.x} y2={b.y} stroke={stroke} strokeWidth={weight} opacity={same ? .75 : .20 + Math.sqrt(e[metric] / maxEdge) * .45} />
            {focus && related && <line x1={a.x} y1={a.y} x2={b.x} y2={b.y} stroke={same ? "#0b9c78" : palette[nodes.get(focus)!.category]} strokeWidth={Math.max(1.4, weight * .45)} strokeDasharray="3 12" className="route-flow" />}
          </g>;
        })}
      </g>
      <g className="nodes">{data.nodes.map(node => {
        const p = positions.get(node.id)!, r = nodeRadius(node, maxNode, mobile, metric);
        const active = node.id === focus, related = !focus || connected.has(node.id);
        const label = tickerLabel(node, data.nodes);
        const inside = r > (mobile ? 19 : 24) && label.length <= 5;
        return <g key={node.id} transform={`translate(${p.x} ${p.y})`} role="button" tabIndex={0}
          aria-label={`${node.symbol}, ${node.name}, ${shortAddress(node)}, ${compact(node[metric], true)}`}
          aria-pressed={selected === node.id} className={`node ${related ? "" : "muted-node"}`}
          onMouseEnter={() => setHover(node.id)} onMouseLeave={() => setHover(null)}
          onFocus={() => setHover(node.id)} onBlur={() => setHover(null)}
          onClick={() => { if (!drag.current?.moved) onSelect(selected === node.id ? null : node.id); }}
          onKeyDown={event => {
            if (event.key === "Enter" || event.key === " ") { event.preventDefault(); onSelect(selected === node.id ? null : node.id); }
            if (event.key === "Escape") onSelect(null);
          }}>
          <title>{`${node.symbol} · ${node.name} · ${shortAddress(node)}`}</title>
          <circle className="touch-target" r={Math.max(r + 6, mobile ? 17 : 14)} fill="transparent" />
          <circle className="node-ring" r={r + 5} fill="none" stroke={palette[node.category]} strokeWidth={active ? 2 : 0} opacity=".35" />
          <circle className="node-body" r={r} fill={palette[node.category]} stroke="white" strokeWidth="2.5" />
          <text textAnchor="middle" dy={inside ? ".35em" : -(r + 7)} className={`ticker ${inside ? "ticker-inside" : ""}`}>
            {label.length > 14 ? `${label.slice(0, 12)}…` : label}
          </text>
        </g>;
      })}</g>
    </svg>
    <div className="graph-controls" aria-label={x.network}>
      <Button variant="ghost" size="icon" aria-label={x.zoomOut} disabled={view.zoom === 1} onClick={() => zoomBy(-.5)}><Minus /></Button>
      <Button variant="ghost" size="icon" aria-label={x.fit} onClick={() => setView({ zoom: 1, x: 0, y: 0 })}><Maximize2 /></Button>
      <Button variant="ghost" size="icon" aria-label={x.zoomIn} disabled={view.zoom === 3} onClick={() => zoomBy(.5)}><Plus /></Button>
    </div>
    <div className="graph-caption" aria-live="polite">
      {selectedNode ? <><span className="asset-dot" style={{ background: palette[selectedNode.category] }} /><b>{selectedNode.symbol}</b><span>{compact(selectedNode[metric], true)}</span><a href="#asset-details" aria-label={x.details}><ArrowDownRight /></a><button onClick={() => onSelect(null)} aria-label={x.clear}><X /></button></> : <><span className="caption-cross">+</span>{x.hint}</>}
    </div>
  </div>;
}

export function LiquidityNetwork({ initialSnapshots, initialChain = DEFAULT_CHAIN.id, initialLanguage }: { initialSnapshots: SnapshotSet; initialChain?: ChainId; initialLanguage: Language }) {
  const t = copy[initialLanguage], x = experience[initialLanguage];
  const c = { ...chainCopy[initialLanguage], ...feedCopy[initialLanguage] };
  const [chainId, setChainId] = useState<ChainId>(initialChain);
  const chain = getChain(chainId)!;
  const [snapshots, setSnapshots] = useState(initialSnapshots);
  const data = snapshots[chainId];
  const activeChain = useRef(initialChain);
  const [metric, setMetric] = useState<Metric>("volume24h");
  const [selected, setSelected] = useState<string | null>(null);
  const [loading, setLoading] = useState(false);
  const [failed, setFailed] = useState(false);
  const [now, setNow] = useState<number | null>(null);
  const [copyStatus, setCopyStatus] = useState("");
  const [refreshResult, setRefreshResult] = useState<"checking" | "updated" | "unchanged" | "limited" | "collecting" | "unavailable" | null>(null);
  const pollDelay = useRef(20_000);
  const latestObservation = useRef(Object.fromEntries(CHAINS.map(({ id }) => [id, Date.parse(initialSnapshots[id].updatedAt)])) as Record<ChainId, number>);
  const manualRefresh = useRef(false);
  const inFlight = useRef<AbortController | null>(null);
  const mounted = useRef(false);

  const refresh = useCallback(async (manual = false) => {
    if (inFlight.current) return;
    manualRefresh.current = manual;
    if (manual) setRefreshResult("checking");
    const controller = new AbortController(); inFlight.current = controller;
    setLoading(true);
    try {
      const response = await fetch("/api/liquidity", { signal: AbortSignal.any([controller.signal, AbortSignal.timeout(8000)]), cache: "no-store" });
      if (!response.ok) throw new Error("Unavailable feed");
      const body = await response.json();
      if (!body || typeof body !== "object" || !("chains" in body) || !isSnapshotSet(body.chains)) throw new Error("Incomplete chain data");
      if (!mounted.current || controller.signal.aborted) return;
      const incoming = body.chains;
      const accepted = incoming[activeChain.current];
      const nextAttempt = Date.parse(accepted.feed?.nextAttemptAt ?? "");
      pollDelay.current = Number.isFinite(nextAttempt) ? Math.max(5000, Math.min(60_000, nextAttempt - Date.now() + 1000)) : 20_000;
      const newer = Date.parse(accepted.updatedAt) > latestObservation.current[activeChain.current];
      if (manualRefresh.current) setRefreshResult(newer ? "updated" : "unchanged");
      else if (newer) setRefreshResult(current => current ? "updated" : null);
      if (accepted.feed?.state === "rate-limited") setRefreshResult("limited");
      else if (accepted.feed?.state === "collecting") setRefreshResult("collecting");
      else if (accepted.feed?.state === "unavailable") setRefreshResult("unavailable");
      else if (!manualRefresh.current) setRefreshResult(current => current === "limited" || current === "collecting" || current === "unavailable" ? null : current);
      for (const { id } of CHAINS) {
        latestObservation.current[id] = Math.max(latestObservation.current[id], Date.parse(incoming[id].updatedAt));
        saveSnapshot(incoming[id], id);
      }
      setSnapshots(current => mergeSnapshots(current, incoming));
      setSelected(current => current && accepted.nodes.some(n => n.id === current) ? current : null);
      setFailed(false);
    } catch {
      if (mounted.current && !controller.signal.aborted) { setFailed(true); setRefreshResult("unavailable"); }
    } finally {
      if (inFlight.current === controller) inFlight.current = null;
      if (mounted.current) setLoading(false);
    }
  }, []);

  useEffect(() => {
    mounted.current = true;
    document.documentElement.lang = localeFor(initialLanguage);
    let timer: ReturnType<typeof setTimeout>;
    const poll = async () => {
      if (!mounted.current) return;
      if (document.visibilityState === "visible") await refresh();
      if (mounted.current) timer = setTimeout(poll, pollDelay.current + Math.random() * 1500);
    };
    const restore = () => {
      const saved: Partial<SnapshotSet> = {};
      for (const { id } of CHAINS) {
        const value = storedSnapshot(id);
        if (value) { saved[id] = value; latestObservation.current[id] = Math.max(latestObservation.current[id], Date.parse(value.updatedAt)); }
      }
      setSnapshots(current => mergeSnapshots(current, saved));
    };
    const start = setTimeout(() => { setNow(Date.now()); restore(); void poll(); }, 0);
    const clock = setInterval(() => setNow(Date.now()), 1000);
    const resume = () => {
      clearTimeout(timer);
      if (document.visibilityState === "visible") void poll();
      else inFlight.current?.abort();
    };
    const sync = (event: StorageEvent) => { if (CHAINS.some(({ id }) => event.key === snapshotStorageKey(id))) restore(); };
    document.addEventListener("visibilitychange", resume);
    window.addEventListener("storage", sync);
    return () => { mounted.current = false; clearTimeout(start); clearTimeout(timer); clearInterval(clock); inFlight.current?.abort(); document.removeEventListener("visibilitychange", resume); window.removeEventListener("storage", sync); };
  }, [refresh, initialLanguage]);

  const age = ageLabel(data.updatedAt, now, initialLanguage);
  const stale = data.stale || failed || (now !== null && now - Date.parse(data.updatedAt) > FRESH_MS);
  const detailNode = data.nodes.find(n => n.id === selected) ?? data.nodes.find(n => n.symbol === (chainId === "robinhood" ? "USDG" : "USDC")) ?? data.nodes[0];
  const nodeMap = useMemo(() => new Map(data.nodes.map(n => [n.id, n])), [data.nodes]);
  const routes = data.edges.filter(e => e.source === detailNode.id || e.target === detailNode.id).sort((a, b) => b[metric] - a[metric]).slice(0, 6);
  const categories: Array<[TokenCategory, string]> = [["eth", "ETH"], ["stable", t.stablecoin], ["btc", t.btcWrapper], ["equity", t.tokenizedEquity], ["other", t.otherToken]];
  const venueMetric = data.venues.reduce((sum, v) => sum + v[metric], 0);
  const venues = [...data.venues].sort((a, b) => b[metric] - a[metric]);

  return <main className="shell" lang={localeFor(initialLanguage)}>
    <header className="topbar">
      <div className="identity"><div className="brand-mark" aria-hidden="true"><span /><span /><span /></div><div><p>AMM <span>/</span> {t.liquidity.toUpperCase()}</p><h1>{x.name}<span className="title-dot">.</span></h1></div></div>
      <div className="top-actions">
        <div className="feed-state" title={`${t.updated}: ${new Date(data.updatedAt).toUTCString()} · ${failed ? x.failed : x.polling}`}>
          <span className={`live-badge ${stale ? "is-stale" : ""}`}><i />{stale ? t.snapshot : t.live}</span>
          <time dateTime={data.updatedAt}>{age}</time>
        </div>
        <Button className="refresh-button" variant="ghost" size="icon" aria-label={t.refresh} disabled={loading} onClick={() => void refresh(true)}><RefreshCw className={loading ? "spin" : ""} /></Button>
        <Select value={initialLanguage} onValueChange={value => { if (isLanguage(value)) location.assign(`${languagePath(value)}?chain=${chainId}`); }}>
          <SelectTrigger className="language-trigger" aria-label={t.language}><Languages /><SelectValue>{languages.find(l => l.code === initialLanguage)!.label}</SelectValue></SelectTrigger>
          <SelectContent align="end">{languages.map(l => <SelectItem key={l.code} value={l.code}>{l.label}</SelectItem>)}</SelectContent>
        </Select>
      </div>
    </header>
    <div className="chain-row">
      <Select value={chainId} onValueChange={value => { if (getChain(value)) { const id = value as ChainId; activeChain.current = id; setChainId(id); setSelected(null); setRefreshResult(null); const url = new URL(location.href); url.searchParams.set("chain", id); history.replaceState(null, "", url); } }}>
        <SelectTrigger className="chain-trigger" aria-label={c.chain}><span className={`chain-mark chain-${chainId}`} aria-hidden="true">{chainId === "ethereum" ? "Ξ" : chainId === "base" ? "B" : "R"}</span><SelectValue>{chain.name}</SelectValue></SelectTrigger>
        <SelectContent>{CHAINS.map(item => <SelectItem key={item.id} value={item.id}>{item.name}</SelectItem>)}<p className="chain-availability">{c.available}</p></SelectContent>
      </Select>
      <a href="#coverage" className="coverage-label">{c.universe}</a>
      <span className="refresh-result" role="status" aria-live="polite">{loading ? c.checking : refreshResult ? c[refreshResult] : ""}</span>
    </div>

    <section className="metrics" aria-label={x.sampled}>
      {[{ label: t.volume24h, value: compact(data.totals.volume24h, true), note: x.sampled }, { label: t.trackedLiquidity, value: compact(data.totals.liquidity, true), note: t.acrossPools }, { label: t.swaps24h, value: compact(data.totals.transactions24h), note: t.buysSells }, { label: t.ammPools, value: data.totals.pools, note: `${data.nodes.length} ${t.assets} · ${data.venues.length} ${t.venues}` }].map((stat) => <div className="metric" key={stat.label}><span>{stat.label}</span><strong>{stat.value}</strong><small>{stat.note}</small></div>)}
    </section>

    <section className="fork-bar" aria-label={t.forkTitle}>
      <div className="fork-label"><GitFork /><span>{x.fork}</span><span className="license-tag">MIT</span></div>
      <code tabIndex={0}>{CLONE_COMMAND}</code>
      <button aria-label={copyStatus === t.copied ? t.copied : t.copy} onClick={async () => { const success = await copyText(CLONE_COMMAND); setCopyStatus(success ? t.copied : x.copyFailed); setTimeout(() => setCopyStatus(""), 3500); }}>{copyStatus === t.copied ? <Check /> : <Copy />}<span>{copyStatus || t.copy}</span></button>
      <span className="sr-only" role="status">{copyStatus}</span>
    </section>

    <div className="workspace">
      <section className="network-card" aria-labelledby="network-heading">
        <div className="network-head"><div><span className="section-kicker">01 / NETWORK</span><h2 id="network-heading">{x.network}</h2></div>
          <RadioGroup value={metric} onValueChange={v => setMetric(v as Metric)} className="metric-switch" aria-label={x.network}>
            <label><RadioGroupItem value="volume24h" className="sr-only" /><span>{t.volume}</span></label><label><RadioGroupItem value="liquidity" className="sr-only" /><span>{t.liquidity}</span></label>
          </RadioGroup>
        </div>
        <div className="legend">{categories.map(([category, label]) => <span key={category}><i style={{ background: palette[category] }} />{label}</span>)}</div>
        <NetworkDiagram key={`${chainId}-desktop`} data={data} metric={metric} language={initialLanguage} mobile={false} selected={selected} onSelect={setSelected} />
        <NetworkDiagram key={`${chainId}-mobile`} data={data} metric={metric} language={initialLanguage} mobile selected={selected} onSelect={setSelected} />
        <div className="network-key"><span><i className="edge-swatch same" />{x.sameClass}</span><span><i className="edge-swatch" />{x.crossClass}</span><small>{x.classNote}</small></div>
      </section>

      <aside className="focus-card" id="asset-details" aria-label={x.details}>
        <div className="focus-header"><span className="section-kicker">02 / {selected ? t.selectedAsset : t.featuredHub}</span>{selected && <button aria-label={x.clear} onClick={() => setSelected(null)}><X /></button>}</div>
        <div className="focus-title"><div className="focus-token" style={{ color: palette[detailNode.category], background: `${palette[detailNode.category]}12` }}>{detailNode.symbol.slice(0, 3)}</div><div><h2>{detailNode.symbol}</h2><p>{detailNode.name}</p></div></div>
        <a className="contract-link" href={`${chain.explorer}/tokens/${contract(detailNode)}`} target="_blank" rel="noopener noreferrer" title={contract(detailNode)}>{shortAddress(detailNode)}<ArrowUpRight /><span className="sr-only">{x.inspect}</span></a>
        {(contract(detailNode) === `0x${"0".repeat(40)}` || data.nodes.filter(n => n.symbol === detailNode.symbol).length > 1) && <p className="identity-note">{contract(detailNode) === `0x${"0".repeat(40)}` ? c.native : c.duplicate}</p>}
        <div className="focus-grid"><div><span>{t.volume24h}</span><strong>{compact(detailNode.volume24h, true)}</strong></div><div><span>{t.liquidity}</span><strong>{compact(detailNode.liquidity, true)}</strong></div><div><span>{t.swaps}</span><strong>{compact(detailNode.transactions24h)}</strong></div><div><span>{t.connections}</span><strong>{detailNode.degree}</strong></div></div>
        <div className="routes-head"><h3>{t.strongestRoutes}</h3><span>{metric === "volume24h" ? t.volume : t.liquidity}</span></div>
        <div className="routes">{routes.map(e => {
          const other = nodeMap.get(e.source === detailNode.id ? e.target : e.source)!;
          const share = e[metric] / Math.max(1, detailNode[metric]);
          return <button key={e.id} onClick={() => setSelected(other.id)} aria-label={`${other.symbol} · ${other.name} · ${shortAddress(other)}`}>
            <span className="route-main"><span><i style={{ background: palette[other.category] }} />{detailNode.symbol}<b>/</b>{other.symbol}</span><strong>{compact(e[metric], true)}</strong></span>
            <span className="route-bar"><i style={{ width: `${share * 100}%`, background: palette[other.category] }} /></span>
            <small>{e.venues.join(" + ")} · {e.poolCount} {e.poolCount === 1 ? t.pool : t.pools}</small>
          </button>;
        })}</div>
      </aside>
    </div>

    <section className="venues-card" aria-labelledby="venues-heading"><div><span className="section-kicker">03 / AMMs</span><h2 id="venues-heading">{t.trackedAmms}</h2><p>{metric === "volume24h" ? t.volume : t.liquidity}</p></div><div className="venues">{venues.map(v => <div key={v.id}><span>{v.name}</span><strong>{new Intl.NumberFormat(localeFor(initialLanguage), { style: "percent", maximumFractionDigits: 1 }).format(v[metric] / Math.max(1, venueMetric))}</strong><i style={{ width: `${Math.max(.3, v[metric] / Math.max(1, venueMetric) * 100)}%` }} /><small>{v.poolCount} {t.pools}</small></div>)}</div></section>

    <details className="methodology" id="coverage"><summary><span>{x.methodology}</span><span>{data.nodes.length} {t.assets} · {data.edges.length} {x.routes}<ChevronDown /></span></summary><p>{chain.name} · {c.universe}. {x.method}</p><p>{t.updated}: <time dateTime={data.updatedAt}>{new Date(data.updatedAt).toUTCString()}</time>. {failed ? x.failed : x.polling}.</p><a href={`/api/liquidity?chain=${chainId}`}>JSON · {c.coverage} <ArrowUpRight /></a></details>
    <footer><div><p>{t.dataCredit}</p><span>{x.noAffiliation}</span></div><div className="footer-links"><a className="source-revision" href="/source.json" title={sourceManifest.publishedAt}>{c.source} {sourceManifest.head.slice(0, 8)}</a><a href={`/${REPOSITORY_NAME}.tar.gz`} download><Download />{x.archive}</a><a href={`/${REPOSITORY_NAME}.bundle`} download><GitFork />{x.bundle}</a><a href="https://x.com/haydenzadams/status/2095564227989700913" target="_blank" rel="noopener noreferrer">{t.haydensGraph}<ArrowUpRight /></a></div><nav className="locale-links" aria-label={t.language}>{languages.map(l => <a key={l.code} href={`${languagePath(l.code)}?chain=${chainId}`} hrefLang={l.htmlLang} aria-current={initialLanguage === l.code ? "page" : undefined}>{l.label}</a>)}</nav></footer>
  </main>;
}
