import type { HistoryDataRow, HistoryExportJob, HistoryMetricDefinition, HistorySeries, HistorySeriesResponse } from '../../api/types';
import { formatZhNumber } from './formatters';

export const HISTORY_EXPORT_POLL_MS = {
  running: 3_000,
  queued: 5_000
} as const;

export function historyExportPollInterval(jobs?: Array<Pick<HistoryExportJob, 'status'>>) {
  if (jobs?.some((job) => job.status === 'running')) return HISTORY_EXPORT_POLL_MS.running;
  if (jobs?.some((job) => job.status === 'queued')) return HISTORY_EXPORT_POLL_MS.queued;
  return false;
}

export function parseHistoryKeywords(value: string) {
  const seen = new Set<string>();
  return value.split(/[,;\n]/).map((item) => item.trim()).filter((item) => {
    const key = item.toLowerCase();
    if (!item || seen.has(key)) return false;
    seen.add(key);
    return true;
  }).slice(0, 5);
}

export function formatHistoryValue(value: unknown, metric?: HistoryMetricDefinition) {
  if (value == null || value === '') return '—';
  const formatted = typeof value === 'number' ? formatZhNumber(value, 6) : String(value);
  return metric?.unit ? `${formatted} ${metric.unit}` : formatted;
}

export type ChartSeries = { key: string; label: string; color: string; path: string; points: number };
const chartColors = ['#1268f3', '#12a46f', '#8b5cf6', '#f59e0b'];

export function buildHistoryChartSeries(rows: HistoryDataRow[], metrics: HistoryMetricDefinition[], width = 800, height = 150): ChartSeries[] {
  const ordered = [...rows].reverse();
  return metrics.slice(0, 4).flatMap((metric, seriesIndex) => {
    const values = ordered.map((row, rowIndex) => ({ rowIndex, value: row.values[metric.key] })).filter((item): item is { rowIndex: number; value: number } => typeof item.value === 'number' && Number.isFinite(item.value));
    if (values.length < 2) return [];
    let min = values[0].value;
    let max = values[0].value;
    for (const item of values) { if (item.value < min) min = item.value; if (item.value > max) max = item.value; }
    const range = max - min || 1;
    const usableWidth = width - 24;
    const usableHeight = height - 24;
    const path = values.map((item, pointIndex) => {
      const x = 12 + (ordered.length <= 1 ? 0 : item.rowIndex / (ordered.length - 1)) * usableWidth;
      const y = 12 + (1 - (item.value - min) / range) * usableHeight;
      return `${pointIndex ? 'L' : 'M'}${x.toFixed(1)},${y.toFixed(1)}`;
    }).join(' ');
    return [{ key: metric.key, label: metric.unit ? `${metric.label} (${metric.unit})` : metric.label, color: chartColors[seriesIndex], path, points: values.length }];
  });
}

export type HistorySeriesLine = { key: string; label: string; color: string; paths: string[]; points: number };
export type HistorySeriesPanel = { key: string; label: string; unit: string; minimum: number; maximum: number; start: string; end: string; lines: HistorySeriesLine[] };

export function buildHistorySeriesPanels(response?: HistorySeriesResponse, width = 800, height = 116): HistorySeriesPanel[] {
  if (!response) return [];
  const byMetric = new Map<string, HistorySeries[]>();
  response.series.forEach((series) => byMetric.set(series.metric, [...(byMetric.get(series.metric) ?? []), series]));
  return [...byMetric.entries()].flatMap(([metric, seriesList], panelIndex) => {
    const values = seriesList.flatMap((series) => series.points.map((point) => point.value).filter((value): value is number => typeof value === 'number' && Number.isFinite(value)));
    if (!values.length) return [];
    let minimum = Math.min(...values); let maximum = Math.max(...values);
    if (minimum === maximum) { const padding = Math.max(Math.abs(minimum) * 0.05, 1); minimum -= padding; maximum += padding; }
    const startMs = new Date(response.dateFrom).getTime(); const endMs = new Date(response.dateTo).getTime();
    const timeRange = Math.max(1, endMs - startMs); const valueRange = maximum - minimum;
    const lines = seriesList.map((series, seriesIndex) => {
      const paths: string[] = []; let current = ''; let previousMs: number | undefined;
      series.points.forEach((point) => {
        if (typeof point.value !== 'number' || !Number.isFinite(point.value)) { if (current) paths.push(current); current = ''; previousMs = undefined; return; }
        const time = new Date(point.time.replace(' ', 'T')).getTime();
        if (!Number.isFinite(time)) return;
        if (previousMs != null && time - previousMs > response.summary.grainSeconds * 1500) { if (current) paths.push(current); current = ''; }
        const x = 54 + Math.max(0, Math.min(1, (time - startMs) / timeRange)) * (width - 68);
        const y = 10 + (1 - (point.value - minimum) / valueRange) * (height - 30);
        current += `${current ? ' L' : 'M'}${x.toFixed(1)},${y.toFixed(1)}`; previousMs = time;
      });
      if (current) paths.push(current);
      return { key: `${series.vin}-${series.protocol}-${metric}`, label: `${series.plate || series.vin} · ${series.protocol}`, color: chartColors[(panelIndex * 2 + seriesIndex) % chartColors.length], paths, points: series.points.length };
    });
    const first = seriesList[0];
    return [{ key: metric, label: first.label, unit: first.unit, minimum, maximum, start: response.dateFrom, end: response.dateTo, lines }];
  });
}

export function formatSeriesGrain(seconds: number) {
  if (seconds < 60) return `${seconds} 秒`;
  if (seconds < 3600) return `${seconds / 60} 分钟`;
  if (seconds < 86400) return `${seconds / 3600} 小时`;
  return `${seconds / 86400} 天`;
}

export function formatExportFileSize(bytes: number) {
  if (!Number.isFinite(bytes) || bytes <= 0) return '—';
  if (bytes < 1024) return `${bytes} B`;
  if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
  return `${(bytes / 1024 / 1024).toFixed(1)} MB`;
}
