/**
 * native chart 内核：Scale（线性 / 带状）与 Nice Ticks
 *
 * 不依赖 d3-scale / recharts，只用纯函数。
 * Recharts 替换路径上所有 chart 都构建在这一层之上。
 */

/* ============================================================
 * Linear Scale
 * 数值定义域 [d0, d1] → 像素值域 [r0, r1]
 * ============================================================ */
export function linearScale(domain, range) {
  const [d0, d1] = domain;
  const [r0, r1] = range;
  const dSpan = d1 - d0;
  const rSpan = r1 - r0;

  function fn(value) {
    if (dSpan === 0) return r0;
    return r0 + ((value - d0) / dSpan) * rSpan;
  }
  fn.invert = (px) => {
    if (rSpan === 0) return d0;
    return d0 + ((px - r0) / rSpan) * dSpan;
  };
  fn.domain = () => [d0, d1];
  fn.range = () => [r0, r1];
  return fn;
}

/* ============================================================
 * Band Scale
 * 类目定义域 [k0, k1, ...] → 像素值域 [r0, r1]
 * 支持内边距（padding，0~1）与首尾外边距（paddingOuter）。
 * 默认与 Recharts BandScale 行为对齐：每个类目在带宽中心。
 * ============================================================ */
export function bandScale(domain, range, options = {}) {
  const {
    padding = 0,
    paddingInner = padding,
    paddingOuter = padding,
    paddingLeft = 0,
    paddingRight = 0,
  } = options;
  const n = domain.length;
  const [r0, r1] = range;
  const usable = (r1 - r0) - paddingLeft - paddingRight;
  const step = n === 0 ? 0 : usable / (n + 2 * paddingOuter - paddingInner);
  const bandWidth = step * (1 - paddingInner);
  const start = r0 + paddingLeft + step * paddingOuter;

  const indexMap = new Map();
  domain.forEach((k, i) => indexMap.set(k, i));

  function fn(value) {
    const i = indexMap.get(value);
    if (i == null) return null;
    return start + i * step;
  }
  fn.bandwidth = () => bandWidth;
  fn.step = () => step;
  fn.domain = () => domain.slice();
  fn.range = () => [r0, r1];
  /** 类目锚点：每个类目带宽中心像素 */
  fn.center = (value) => {
    const x = fn(value);
    return x == null ? null : x + bandWidth / 2;
  };
  /** 像素 → 最近类目（用于 hover 探测） */
  fn.invert = (px) => {
    if (n === 0 || step === 0) return null;
    const idx = Math.round((px - start - bandWidth / 2) / step);
    return domain[Math.max(0, Math.min(n - 1, idx))];
  };
  return fn;
}

/* ============================================================
 * Nice Ticks（用于数值轴自动刻度）
 * ============================================================ */
const NICE_STEPS = [1, 2, 2.5, 5, 10];

function niceStep(rough) {
  if (!Number.isFinite(rough) || rough <= 0) return 1;
  const exp = Math.floor(Math.log10(rough));
  const mag = Math.pow(10, exp);
  const norm = rough / mag;
  for (const s of NICE_STEPS) {
    if (norm <= s) return s * mag;
  }
  return 10 * mag;
}

/**
 * 给定 [min, max] 与目标 tick 个数，返回扩展后的 nice 区间与 tick 数组。
 * @returns {{ niceMin: number, niceMax: number, ticks: number[], step: number }}
 */
export function niceTicks(min, max, targetCount = 5) {
  if (!Number.isFinite(min) || !Number.isFinite(max)) {
    return { niceMin: 0, niceMax: 1, ticks: [0, 1], step: 1 };
  }
  if (min === max) {
    const pad = Math.abs(min) || 1;
    min -= pad;
    max += pad;
  }
  const span = max - min;
  const rough = span / Math.max(1, targetCount - 1);
  const step = niceStep(rough);
  const niceMin = Math.floor(min / step) * step;
  const niceMax = Math.ceil(max / step) * step;
  const ticks = [];
  for (let v = niceMin; v <= niceMax + step / 2; v += step) {
    ticks.push(Number(v.toFixed(10)));
  }
  return { niceMin, niceMax, ticks, step };
}

/* ============================================================
 * 数据极值
 * ============================================================ */
export function extentBy(rows, accessor) {
  let lo = Infinity;
  let hi = -Infinity;
  for (const r of rows) {
    const v = accessor(r);
    if (v == null || !Number.isFinite(v)) continue;
    if (v < lo) lo = v;
    if (v > hi) hi = v;
  }
  if (!Number.isFinite(lo)) return [0, 0];
  return [lo, hi];
}
