import type { ChartPoint } from '../components/Charts/HashrateChart';
import type { ContributionBar } from './fleetAnalytics';

export type ChartSeriesKind = 'hashrate' | 'accept' | 'cpu' | 'mem' | 'gpu';

export type ChartDisplayMode = 'live' | 'sample' | 'blend';

const MIN_LIVE_POINTS = 12;

/** Fleet snapshot shown when no live miners — deck still feels “about to print”. */
export const SAMPLE_FLEET_PREVIEW = {
  hashrate: 128_400,
  acceptRate: 96.8,
  avgCpu: 52,
  avgMem: 61,
  onlinePct: 88,
  onlineCount: 7,
  agentCount: 8,
  xmrPerDay: 0.0384,
  xmrPrice: 168.42,
} as const;

export const SAMPLE_CONTRIBUTION_BARS: ContributionBar[] = [
  { id: 's1', name: 'Vault-01', hashrate: 42_800, pct: 33.4 },
  { id: 's2', name: 'Forge-Rig', hashrate: 31_200, pct: 24.3 },
  { id: 's3', name: 'Lan-Node-7', hashrate: 28_100, pct: 21.9 },
  { id: 's4', name: 'Basement-XMR', hashrate: 26_300, pct: 20.4 },
];

export const SAMPLE_ACTIVITY = [
  { id: 'sa1', label: 'OK', ok: true, time: '12:04:11' },
  { id: 'sa2', label: 'OK', ok: true, time: '12:03:58' },
  { id: 'sa3', label: 'OK', ok: true, time: '12:03:41' },
  { id: 'sa4', label: 'OK', ok: true, time: '12:03:22' },
  { id: 'sa5', label: 'OK', ok: true, time: '12:02:59' },
  { id: 'sa6', label: 'BAD', ok: false, time: '12:02:44' },
  { id: 'sa7', label: 'OK', ok: true, time: '12:02:31' },
];

function formatTime(offsetMin: number): string {
  const d = new Date(Date.now() - offsetMin * 60_000);
  return d.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit', second: '2-digit' });
}

function noise(i: number, amp: number): number {
  return Math.sin(i * 0.7) * amp + Math.cos(i * 0.31) * (amp * 0.6);
}

/** Deterministic rich-looking telemetry for chart QA and empty-deck preview. */
export function generateSampleSeries(kind: ChartSeriesKind, points = 48): ChartPoint[] {
  const out: ChartPoint[] = [];
  for (let i = points - 1; i >= 0; i--) {
    const t = formatTime(i * 2);
    const p = (points - 1 - i) / Math.max(1, points - 1);
    let value: number;
    switch (kind) {
      case 'hashrate':
        value = 38_000 + p * 92_000 + noise(i, 4_200);
        break;
      case 'gpu':
        value = 12_000_000 + p * 38_000_000 + noise(i, 1_800_000);
        break;
      case 'accept':
        value = 93.5 + p * 3.2 + noise(i, 0.35);
        value = Math.min(99.5, Math.max(91, value));
        break;
      case 'cpu':
        value = 38 + p * 22 + noise(i, 4);
        value = Math.min(88, Math.max(28, value));
        break;
      case 'mem':
      default:
        value = 44 + p * 18 + noise(i, 3);
        value = Math.min(78, Math.max(36, value));
        break;
    }
    out.push({ time: t, value: Math.round(value * 10) / 10 });
  }
  return out;
}

export function validateChartSeries(data: ChartPoint[]): { ok: boolean; errors: string[] } {
  const errors: string[] = [];
  if (!Array.isArray(data) || data.length === 0) {
    errors.push('series is empty');
    return { ok: false, errors };
  }
  data.forEach((pt, i) => {
    if (!pt.time || typeof pt.time !== 'string') errors.push(`point ${i}: missing time`);
    if (typeof pt.value !== 'number' || !Number.isFinite(pt.value)) errors.push(`point ${i}: invalid value`);
  });
  return { ok: errors.length === 0, errors };
}

function hasMeaningfulLive(live: ChartPoint[], kind: ChartSeriesKind): boolean {
  if (live.length < MIN_LIVE_POINTS) return false;
  const vals = live.map((p) => p.value);
  const max = Math.max(...vals);
  const min = Math.min(...vals);
  if (kind === 'hashrate' || kind === 'gpu') return max > 0 && max !== min;
  return max - min > 0.05;
}

/** Prefer live telemetry; pad with sample so graphs never look broken or empty. */
export function resolveChartSeries(
  live: ChartPoint[],
  kind: ChartSeriesKind,
  options?: { tailValue?: number; minPoints?: number }
): { data: ChartPoint[]; mode: ChartDisplayMode } {
  const minPoints = options?.minPoints ?? MIN_LIVE_POINTS;
  const validation = validateChartSeries(live);
  const liveOk = validation.ok && live.length >= minPoints && hasMeaningfulLive(live, kind);

  if (liveOk) {
    return { data: live.slice(-60), mode: 'live' };
  }

  const sample = generateSampleSeries(kind, 48);
  if (live.length === 0) {
    if (options?.tailValue != null && Number.isFinite(options.tailValue)) {
      const last = sample[sample.length - 1];
      sample[sample.length - 1] = { ...last, value: options.tailValue };
    }
    return { data: sample, mode: 'sample' };
  }

  const merged = [...sample.slice(0, Math.max(0, 48 - live.length)), ...live.slice(-24)];
  validateChartSeries(merged);
  return { data: merged, mode: 'blend' };
}

export function chartSeriesDelta(data: ChartPoint[]): number | null {
  if (data.length < 2) return null;
  const a = data[0].value;
  const b = data[data.length - 1].value;
  if (a === 0) return b > 0 ? 100 : 0;
  return ((b - a) / Math.abs(a)) * 100;
}

export function chartSeriesPeak(data: ChartPoint[]): number {
  if (data.length === 0) return 0;
  return Math.max(...data.map((p) => p.value));
}
