import { resolveColorToken } from '../utils/token-resolver.js';

/** UTC 锚点：首条类目为 04-19，之后按天递增（与 Dashboard 预览 mock 一致） */
const CHART_MOCK_AXIS_DATE_ANCHOR_UTC = Date.UTC(2026, 3, 19);

/**
 * 类目轴 mock 文案：`dayIndex` 从 0 起为锚点当天，依次 +1 天。
 * @param {number} dayIndex
 * @returns {string} `MM-DD`
 */
export function chartMockCategoryDateLabel(dayIndex) {
  const d = new Date(CHART_MOCK_AXIS_DATE_ANCHOR_UTC);
  d.setUTCDate(d.getUTCDate() + dayIndex);
  const mm = String(d.getUTCMonth() + 1).padStart(2, '0');
  const dd = String(d.getUTCDate()).padStart(2, '0');
  return `${mm}-${dd}`;
}

/**
 * Recharts 3：`CartesianGrid` 默认 `xAxisId` / `yAxisId` 均为 `0`。
 * 若 `YAxis` 使用字符串 id（如 `left`），网格必须传同一 `yAxisId`，否则横网格线与刻度轴解绑，表现为横线缺失或错位。
 */
export const RECHARTS_CARTESIAN_AXIS_ID = 0;

/** 图表类目轴默认节点数（全局）：横轴默认 9 个类目 */
export const CHART_DEFAULT_CATEGORY_COUNT = 9;

/** 图表纵轴默认刻度数（全局）：纵轴默认 7 个刻度 */
export const CHART_DEFAULT_Y_AXIS_TICK_COUNT = 7;

export { CHART_SERIES_LINE_STROKE_WIDTH } from './chart-line-primitives';

/**
 * 类目刻度文字外沿距绘图区内边界（相对纵轴一侧、相对绘图区右侧）最小留白，单位 px。
 */
export const CHART_CATEGORY_AXIS_EDGE_INSET_PX = 12;

/**
 * Recharts 类目刻度锚点通常在刻度带宽中心，`padding` 只缩小 scale 的像素区间；
 * 要保证「文字左/右外沿」距绘图区边界 ≥ {@link CHART_CATEGORY_AXIS_EDGE_INSET_PX}，
 * 需在 12px 基础上再加刻度文案半宽估算（12px 字号下典型短类目如 MM-DD）。
 */
export const CHART_CATEGORY_TICK_LABEL_HALF_WIDTH_PX = 20;

const CATEGORY_AXIS_RANGE_INSET =
  CHART_CATEGORY_AXIS_EDGE_INSET_PX + CHART_CATEGORY_TICK_LABEL_HALF_WIDTH_PX;

/**
 * 类目在横轴（垂直柱、折线、面积、组合图等）时，scale 区间相对绘图区左右内缩。
 * 用于满足：首尾刻度文字外沿距绘图区左缘/右缘 ≥ 12px（在常见字号与文案长度下）。
 */
export const X_AXIS_CATEGORY_PADDING = {
  left: CATEGORY_AXIS_RANGE_INSET,
  right: CATEGORY_AXIS_RANGE_INSET,
};

/**
 * 类目在纵轴（横向柱图）时，scale 区间相对绘图区上下内缩（同理）。
 */
export const Y_AXIS_CATEGORY_PADDING = {
  top: CATEGORY_AXIS_RANGE_INSET,
  bottom: CATEGORY_AXIS_RANGE_INSET,
};

/**
 * 数值轴顶部「呼吸区」：在 dataMax 之上按比例扩展，避免折线/柱顶贴齐网格上沿。
 * 与 {@link CHART_VALUE_AXIS_TOP_HEADROOM_MIN} 取较大者，再按步长取整刻度上界。
 */
export const CHART_VALUE_AXIS_TOP_HEADROOM_RATIO = 0.08;

/** 与比例取大，避免跨度极小时顶部留白几乎为 0 */
export const CHART_VALUE_AXIS_TOP_HEADROOM_MIN = 2;

/**
 * @param {number} padded
 */
function pickNiceStepForAxisMax(padded) {
  if (!Number.isFinite(padded) || padded <= 0) return 1;
  if (padded <= 40) return 5;
  if (padded <= 120) return 10;
  if (padded <= 350) return 25;
  if (padded <= 800) return 50;
  return 10 ** Math.floor(Math.log10(padded));
}

/**
 * 在 rawMax 之上增加顶部留白，并上取整到较「整」的刻度上界。
 * @param {number} rawMax
 * @param {number} spanBaseline — 参与计算比例跨度的下限（通常为域下界，如 0 或负向最小值）
 */
export function niceCeilAxisMaxWithTopHeadroom(rawMax, spanBaseline = 0) {
  if (!Number.isFinite(rawMax) || rawMax <= 0) return 1;
  const span = Math.max(rawMax - spanBaseline, 1e-9);
  const padded =
    rawMax + Math.max(span * CHART_VALUE_AXIS_TOP_HEADROOM_RATIO, CHART_VALUE_AXIS_TOP_HEADROOM_MIN);
  const step = pickNiceStepForAxisMax(padded);
  return Math.ceil(padded / step) * step;
}

/**
 * @param {Record<string, unknown>[]} rows
 * @param {string[]} keys
 * @returns {{ minV: number, maxV: number }}
 */
export function getNumericMinMaxAmongKeys(rows, keys) {
  let minV = Infinity;
  let maxV = -Infinity;
  if (!Array.isArray(rows) || !Array.isArray(keys)) return { minV, maxV };
  for (const row of rows) {
    for (const k of keys) {
      const v = Number(row[k]);
      if (!Number.isFinite(v)) continue;
      minV = Math.min(minV, v);
      maxV = Math.max(maxV, v);
    }
  }
  return { minV, maxV };
}

/**
 * 常规数值 Y 轴：下界为 0 或数据中的负向最小值，上界为 dataMax + 顶部留白。
 * @param {Record<string, unknown>[]} rows
 * @param {string[]} keys
 * @returns {[number, number] | undefined}
 */
export function buildNumericYDomainZeroOrDataMin(rows, keys) {
  if (!Array.isArray(rows) || rows.length === 0 || !Array.isArray(keys) || keys.length === 0) {
    return undefined;
  }
  const { minV, maxV } = getNumericMinMaxAmongKeys(rows, keys);
  if (!Number.isFinite(maxV) || maxV === -Infinity) return undefined;
  const lower = Number.isFinite(minV) && minV < 0 ? minV : 0;
  const upper = niceCeilAxisMaxWithTopHeadroom(maxV, lower);
  return [lower, upper];
}

/**
 * 堆叠序列（均为非负叠压）：按行求和后再做顶部留白。
 * @param {Record<string, unknown>[]} rows
 * @param {string[]} keys
 * @returns {[number, number] | undefined}
 */
export function buildStackedPositiveYDomain(rows, keys) {
  if (!Array.isArray(rows) || rows.length === 0 || !Array.isArray(keys) || keys.length === 0) {
    return undefined;
  }
  let maxSum = 0;
  for (const row of rows) {
    let s = 0;
    for (const k of keys) {
      const v = Number(row[k]);
      if (!Number.isFinite(v)) continue;
      s += v > 0 ? v : 0;
    }
    maxSum = Math.max(maxSum, s);
  }
  if (!Number.isFinite(maxSum) || maxSum <= 0) return [0, 1];
  return [0, niceCeilAxisMaxWithTopHeadroom(maxSum, 0)];
}

/**
 * 数值轴：在固定刻度数下用**整数步长**等分，必要时略抬上界，使刻度全为整数且横网格等距。
 * 与 Recharts `tickCount` + `domain` 的 “nice ticks” 不同，此处保证 `(hi - lo) / (tickCount-1)` 为正整数。
 * @param {[number, number]} domainTuple — 原始 `[lo, hi]`，`hi` 为数据上界（含留白）后通常已较整
 * @param {number} [tickCount]
 * @returns {{ domain: [number, number], ticks: number[] } | undefined}
 */
export function snapNumericAxisToIntegerUniformTicks(
  domainTuple,
  tickCount = CHART_DEFAULT_Y_AXIS_TICK_COUNT,
) {
  if (!Array.isArray(domainTuple) || domainTuple.length !== 2) return undefined;
  const loRaw = Number(domainTuple[0]);
  const hiRaw = Number(domainTuple[1]);
  if (!Number.isFinite(loRaw) || !Number.isFinite(hiRaw) || !(hiRaw > loRaw)) return undefined;

  const n = Math.max(2, Math.floor(Number(tickCount)) || CHART_DEFAULT_Y_AXIS_TICK_COUNT);
  const segs = n - 1;

  const loInt = Math.floor(loRaw);
  const spanNeed = hiRaw - loInt;
  let step = Math.ceil(spanNeed / segs - 1e-9);
  if (!(step > 0) || !Number.isFinite(step)) step = 1;
  const hiInt = loInt + segs * step;
  const ticks = Array.from({ length: n }, (_, i) => loInt + i * step);
  return { domain: [loInt, hiInt], ticks };
}

/**
 * Recharts `CartesianGrid` 与数值纵轴刻度对齐。
 * 使用 {@link snapNumericAxisToIntegerUniformTicks} 的显式 `ticks` 时须传 `horizontalValues`，否则网格条数会与刻度不一致。
 * @param {{ ticks?: number[] } | undefined} axisUi
 * @returns {{ syncWithTicks: true, horizontalValues?: number[] }}
 */
export function cartesianGridSyncWithNumericYAxis(axisUi) {
  const ticks = axisUi?.ticks;
  if (Array.isArray(ticks) && ticks.length > 0) {
    return { syncWithTicks: true, horizontalValues: ticks };
  }
  return { syncWithTicks: true };
}

/**
 * Recharts `CartesianGrid` 与数值横轴刻度对齐（用于横向条形图等数值轴在 X 的场景）。
 * @param {{ ticks?: number[] } | undefined} axisUi
 * @returns {{ syncWithTicks: true, verticalValues?: number[] }}
 */
export function cartesianGridSyncWithNumericXAxis(axisUi) {
  const ticks = axisUi?.ticks;
  if (Array.isArray(ticks) && ticks.length > 0) {
    return { syncWithTicks: true, verticalValues: ticks };
  }
  return { syncWithTicks: true };
}

/**
 * 组合图单纵轴：柱 + 折线共用左轴时的上界（柱堆叠时按行求和柱列，再与同行折线取大）。
 * @param {Record<string, unknown>[]} rows
 * @param {string[]} barKeys
 * @param {string[]} lineKeys
 * @param {boolean} barStack
 * @returns {[number, number] | undefined}
 */
export function buildComposedSingleNumberYDomain(rows, barKeys, lineKeys, barStack) {
  const bars = Array.isArray(barKeys) ? barKeys.filter(Boolean) : [];
  const lines = Array.isArray(lineKeys) ? lineKeys.filter(Boolean) : [];
  if (!Array.isArray(rows) || rows.length === 0 || (bars.length === 0 && lines.length === 0)) {
    return undefined;
  }
  let minV = Infinity;
  let rawMax = -Infinity;
  for (const row of rows) {
    let barPeak = -Infinity;
    if (bars.length) {
      if (barStack) {
        let sum = 0;
        for (const k of bars) {
          const v = Number(row[k]);
          if (!Number.isFinite(v)) continue;
          minV = Math.min(minV, v);
          sum += v > 0 ? v : 0;
        }
        barPeak = sum;
      } else {
        for (const k of bars) {
          const v = Number(row[k]);
          if (!Number.isFinite(v)) continue;
          minV = Math.min(minV, v);
          barPeak = Math.max(barPeak, v);
        }
      }
    }
    let linePeak = -Infinity;
    for (const k of lines) {
      const v = Number(row[k]);
      if (!Number.isFinite(v)) continue;
      minV = Math.min(minV, v);
      linePeak = Math.max(linePeak, v);
    }
    const parts = [];
    if (Number.isFinite(barPeak)) parts.push(barPeak);
    if (Number.isFinite(linePeak)) parts.push(linePeak);
    const rowPeak = parts.length ? Math.max(...parts) : -Infinity;
    rawMax = Math.max(rawMax, rowPeak);
  }
  if (!Number.isFinite(rawMax) || rawMax === -Infinity) return undefined;
  const lower = Number.isFinite(minV) && minV < 0 ? minV : 0;
  return [lower, niceCeilAxisMaxWithTopHeadroom(rawMax, lower)];
}

/** 与 Dashboard `TOKEN_DATA_COLORS` 条数一致，供各 Smart* 图表复用 */

export const CHART_DATA_COLOR_SLOTS = 21;
const CHART_DEFAULT_COLOR_INDEX_PRIORITY = [0, 4];

/** 语义 Token：`tokens.js` → `border-chart-axis`（Blue Grey 500） */
export const CHART_CATEGORY_AXISLINE_TOKEN = 'border-chart-axis';

const FALLBACK_CHART_BASELINE = '#98A2B3';

/**
 * @param {{ root?: Element }} [resolveOpts]
 * @returns {string}
 */
export function resolveChartCategoryBaselineStroke(resolveOpts) {
  // 轴基线强制走语义变量，避免 token 解析链异常时回落成黑色
  return `var(--color-${CHART_CATEGORY_AXISLINE_TOKEN}, ${FALLBACK_CHART_BASELINE})`;
}

export function normalizeDataColorToken(token) {
  const m = String(token || '').match(/^data-(\d+)$/);
  if (!m) return 'data-0';
  const i = Number.parseInt(m[1], 10);
  if (!Number.isFinite(i)) return 'data-0';
  const k = ((i % CHART_DATA_COLOR_SLOTS) + CHART_DATA_COLOR_SLOTS) % CHART_DATA_COLOR_SLOTS;
  return `data-${k}`;
}

/**
 * 非饼图默认配色：优先 Data1 / Data5，其余按槽位顺排。
 * @param {number} index
 * @returns {string}
 */
export function getDefaultDataColorToken(index) {
  const i = Number.isFinite(Number(index)) ? Math.max(0, Math.floor(Number(index))) : 0;
  const mapped = i < CHART_DEFAULT_COLOR_INDEX_PRIORITY.length
    ? CHART_DEFAULT_COLOR_INDEX_PRIORITY[i]
    : i;
  return normalizeDataColorToken(`data-${mapped}`);
}

/**
 * @param {'top'|'bottom'|'left'|'right'|undefined} position
 */
export function getLegendLayout(position) {
  switch (position) {
    case 'top':
      return {
        verticalAlign: 'top',
        align: 'center',
        layout: 'horizontal',
        wrapperStyle: { paddingBottom: 20 },
      };
    case 'left':
      return { verticalAlign: 'middle', align: 'left', layout: 'vertical' };
    case 'right':
      return { verticalAlign: 'middle', align: 'right', layout: 'vertical' };
    case 'bottom':
    default:
      return { verticalAlign: 'bottom', align: 'center', layout: 'horizontal' };
  }
}

/** 柱状 / 饼环 / 散点 / 词云（非折线、面积）— Recharts 图例实心圆点尺寸，与词云 `.smart-wordcloud__dot` 对齐 */
export const CHART_FILLED_LEGEND_ICON_TYPE = 'circle';
export const CHART_FILLED_LEGEND_ICON_SIZE = 8;
export const CHART_FILLED_LEGEND_FONT_SIZE = 12;

/**
 * 图例文案与容器字色（`text-secondary` 解析值或等效 CSS）。
 * @param {string} textColor
 */
export function getChartFilledLegendTextStyleProps(textColor) {
  const fs = CHART_FILLED_LEGEND_FONT_SIZE;
  return {
    labelStyle: { color: textColor, fontSize: fs, fontWeight: 400 },
  };
}

/**
 * 实心圆点 + 次级字色；用于 Bar / Pie / Scatter 等纯「填色类」序列图例。
 * 折线 / 面积 / 组合图里的折线序列勿传全局 `iconType`，由各序列 `legendType` 区分。
 * @param {string} textColor
 */
export function getChartFilledLegendRechartsProps(textColor) {
  return {
    ...getChartFilledLegendTextStyleProps(textColor),
    iconType: CHART_FILLED_LEGEND_ICON_TYPE,
    iconSize: CHART_FILLED_LEGEND_ICON_SIZE,
  };
}

const CHART_ACTIVE_DOT_SHADOW =
  'drop-shadow(0px 2px 6px var(--color-fill-active, rgba(83, 96, 143, 0.15)))';

/**
 * 线/面积图 Hover 高亮点：白描边 + 阴影，提升亮背景下可读性。
 * @param {string} fillColor
 */
export function getChartHoverActiveDotProps(fillColor) {
  return {
    r: 6.5,
    fill: fillColor,
    stroke: 'var(--color-page, #FFFFFF)',
    strokeWidth: 3,
    filter: CHART_ACTIVE_DOT_SHADOW,
  };
}
