const metricAnimationFrames = new WeakMap();

let rowAnimationObserver = null;


function changeClass(value) {
  return number(value) > 0 ? "up" : number(value) < 0 ? "down" : "";
}


function streakLabel(streak) {
  const value = Math.max(1, number(streak));
  return value === 1 ? "首板" : `${value}板`;
}

function signed(value) {
  const parsed = number(value);
  return `${parsed > 0 ? "+" : ""}${formatNumber(parsed, 2)}`;
}


function formatMoneyMillion(value) {
  const parsed = number(value);
  const sign = parsed > 0 ? "+" : "";
  if (Math.abs(parsed) >= 100) return `${sign}${formatNumber(parsed / 100, 2)} 亿`;
  return `${sign}${formatNumber(parsed * 100, 0)} 万`;
}

async function apiRequest(url, method = "GET", body = null, requestOptions = {}) {
  return window.XiaobaiAPI.request(url, method, body, requestOptions);
}

function setLoading(loading, text = "正在加载复盘数据", context = "default") {
  elements.loading.hidden = !loading;
  elements.loading.dataset.context = loading ? context : "default";
  setText("loadingTitle", text);
  setText(
    "loadingHint",
    context === "screener"
      ? "正在完成因子筛选、候选排序与历史样本回测，这通常需要一点时间"
      : "请稍候",
  );
}

function setStatus(text) {
  applicationShell.setStatus(text);
}

let toastTimer;
function showToast(message) {
  clearTimeout(toastTimer);
  elements.toast.textContent = message;
  elements.toast.hidden = false;
  toastTimer = setTimeout(() => { elements.toast.hidden = true; }, 3600);
}

function setText(id, value) {
  const element = document.getElementById(id);
  if (element) element.textContent = value;
}

function motionEnabled() {
  return !window.matchMedia("(prefers-reduced-motion: reduce)").matches;
}

function refreshIcons() {
  if (!window.lucide?.createIcons) return;
  window.lucide.createIcons({ attrs: { "aria-hidden": "true" } });
}

function toggleHeaderCommandMenu(force) {
  applicationShell.toggleHeaderCommandMenu(force);
}


function animateMetric(id, rawValue, formatter = (value) => value) {
  const element = document.getElementById(id);
  const target = Number(rawValue);
  if (!element || !Number.isFinite(target)) {
    setText(id, formatter(rawValue));
    return;
  }
  const storedValue = Number(element.dataset.metricValue);
  const previous = Number.isFinite(storedValue) ? storedValue : 0;
  element.dataset.metricValue = String(target);
  const existingFrame = metricAnimationFrames.get(element);
  if (existingFrame) cancelAnimationFrame(existingFrame);
  if (!motionEnabled() || previous === target) {
    element.textContent = formatter(target);
    return;
  }
  element.classList.remove("metric-changed");
  void element.offsetWidth;
  element.classList.add("metric-changed");
  const startedAt = performance.now();
  const duration = 560;
  const update = (now) => {
    const progress = Math.min(1, (now - startedAt) / duration);
    const eased = 1 - (1 - progress) ** 3;
    element.textContent = formatter(previous + (target - previous) * eased);
    if (progress < 1) {
      metricAnimationFrames.set(element, requestAnimationFrame(update));
    } else {
      element.textContent = formatter(target);
      metricAnimationFrames.delete(element);
      setTimeout(() => element.classList.remove("metric-changed"), 80);
    }
  };
  metricAnimationFrames.set(element, requestAnimationFrame(update));
}

function animateRows(container) {
  if (!container) return;
  const rows = [...container.children].filter((item) => item.matches("tr, [data-code]"));
  if (!motionEnabled()) {
    rows.forEach((row) => row.classList.remove("row-pending", "row-enter"));
    return;
  }
  const unseenRows = rows.filter((row) => row.dataset.motionSeen !== "1");
  unseenRows.slice(0, 12).forEach((row, index) => {
    row.dataset.motionSeen = "1";
    row.classList.remove("row-pending", "row-enter");
    row.style.setProperty("--row-delay", `${index * 24}ms`);
    requestAnimationFrame(() => row.classList.add("row-enter"));
    row.addEventListener("animationend", () => row.classList.remove("row-enter"), { once: true });
  });
  if (!("IntersectionObserver" in window)) {
    unseenRows.slice(12).forEach((row) => { row.dataset.motionSeen = "1"; });
    return;
  }
  if (!rowAnimationObserver) {
    rowAnimationObserver = new IntersectionObserver((entries) => {
      entries.forEach((entry) => {
        if (!entry.isIntersecting) return;
        const row = entry.target;
        rowAnimationObserver.unobserve(row);
        row.dataset.motionSeen = "1";
        row.classList.remove("row-pending");
        row.style.setProperty("--row-delay", "0ms");
        requestAnimationFrame(() => row.classList.add("row-enter"));
        row.addEventListener("animationend", () => row.classList.remove("row-enter"), { once: true });
      });
    }, { threshold: 0.08, rootMargin: "0px 0px 40px 0px" });
  }
  unseenRows.slice(12).forEach((row) => {
    row.classList.add("row-pending");
    rowAnimationObserver.observe(row);
  });
}

function waitForMotion(duration) {
  return new Promise((resolve) => setTimeout(resolve, motionEnabled() ? duration : 0));
}
