export function formatBytes(bytes: number): string {
  if (bytes === 0) return '0 B';
  const k = 1024;
  const sizes = ['B', 'KB', 'MB', 'GB', 'TB'];
  const i = Math.floor(Math.log(bytes) / Math.log(k));
  return `${parseFloat((bytes / Math.pow(k, i)).toFixed(1))} ${sizes[i]}`;
}

// N1: Estimate RAM usage from disk size + quantization level
// Apple Silicon uses unified memory — GPU and CPU share the same pool
export function estimateRam(diskSize: number, quant?: string): number {
  const q = (quant || '').toUpperCase();
  let multiplier = 1.2; // default
  if (q.includes('Q4')) multiplier = 1.2;
  else if (q.includes('Q5')) multiplier = 1.25;
  else if (q.includes('Q8')) multiplier = 1.1;
  else if (q.includes('F16') || q.includes('F32')) multiplier = 1.05;
  return Math.round(diskSize * multiplier);
}

// N2: Check if model fits in available memory
// free = raw free pages, cached = inactive+purgeable+speculative (no overlap)
// macOS reclaims ~90% of cached on demand for large allocations (model mmaps)
export type FitStatus = 'fits' | 'tight' | 'no';
export function checkMemoryFit(
  estimatedRam: number,
  freeMemory: number,
  cachedMemory: number
): FitStatus {
  const available = freeMemory + cachedMemory * 0.9;
  const ratio = estimatedRam / available;
  if (ratio < 0.7) return 'fits';
  if (ratio <= 1.0) return 'tight';
  return 'no';
}

// N6/N7: Detect model capabilities from name and family
export interface ModelBadge {
  label: string;
  color: string;
  tooltip: string;
}

export function getModelBadges(name: string, family?: string): ModelBadge[] {
  const badges: ModelBadge[] = [];
  const n = name.toLowerCase();
  const f = (family || '').toLowerCase();

  // N6: DeepSeek R1 reasoning models emit <think> traces
  if (n.includes('deepseek-r1') || n.includes('deepseek-r1-distill') || f.includes('deepseek')) {
    badges.push({
      label: '<think>',
      color: 'var(--warning)',
      tooltip: 'Emits <think> reasoning traces — strip before JSON.parse',
    });
  }

  // N7: Vision/multimodal models
  if (/llava|bakllava|moondream|qwen.*vl|minicpm-v|llama.*vision/i.test(n)) {
    badges.push({
      label: '👁 Vision',
      color: 'var(--accent-secondary)',
      tooltip: 'Vision model — supports image input',
    });
  }

  return badges;
}

// v4: Approximate tokenizer for context window usage indicator
export function estimateTokens(text: string): number {
  const trimmed = text.trim();
  if (!trimmed) return 0;
  return Math.ceil(trimmed.split(/\s+/).length * 1.3);
}

// v4: Best-effort model context window lookup from model name
export function getModelContextWindow(modelName: string): number {
  const n = modelName.toLowerCase();
  if (n.includes('128k')) return 128_000;
  if (n.includes('64k')) return 64_000;
  if (n.includes('32k')) return 32_000;
  if (n.includes('16k')) return 16_000;
  if (n.includes('8k')) return 8_000;
  return 4_096;
}

export function formatUptime(seconds: number): string {
  const d = Math.floor(seconds / 86400);
  const h = Math.floor((seconds % 86400) / 3600);
  const m = Math.floor((seconds % 3600) / 60);
  if (d > 0) return `${d}d ${h}h ${m}m`;
  if (h > 0) return `${h}h ${m}m`;
  return `${m}m`;
}
