/**
 * Format auto-detection for pasted dataset data.
 *
 * Portable core: no browser APIs, no React — usable in Node and tests.
 * Implements the detection rules from spec §05 (Datasets → Auto-detection):
 *
 *   - Valid JSON parses to `json`, or `topojson` when it is a topology object — high confidence.
 *   - Otherwise multi-line text with a header row is `tsv` (tab-separated) or `csv` (comma) — medium.
 *   - Unrecognized input yields `null` format — low confidence (saving should be blocked upstream).
 */

export type DataFormat = 'json' | 'csv' | 'tsv' | 'topojson';
type DetectionConfidence = 'high' | 'medium' | 'low';

export interface FormatDetection {
  format: DataFormat | null;
  confidence: DetectionConfidence;
}

function isTopology(value: unknown): boolean {
  return (
    typeof value === 'object' && value !== null && (value as { type?: unknown }).type === 'Topology'
  );
}

/** Detect the format of pasted inline data. */
export function detectFormat(raw: string): FormatDetection {
  const text = raw.trim();
  if (text === '') return { format: null, confidence: 'low' };

  // 1. Try JSON first — highest confidence signal.
  try {
    const parsed: unknown = JSON.parse(text);
    return {
      format: isTopology(parsed) ? 'topojson' : 'json',
      confidence: 'high',
    };
  } catch {
    // not JSON — fall through to delimited detection
  }

  // 2. Delimited text: needs at least a header row plus one data row.
  const lines = text.split(/\r?\n/).filter((l) => l.length > 0);
  if (lines.length >= 2) {
    const header = lines[0];
    if (header.includes('\t')) return { format: 'tsv', confidence: 'medium' };
    if (header.includes(',')) return { format: 'csv', confidence: 'medium' };
  }

  // 3. Unrecognized.
  return { format: null, confidence: 'low' };
}

/** Infer a dataset's format from a URL's file extension (spec §05). */
export function detectFormatFromUrl(url: string): DataFormat | null {
  const match = url.toLowerCase().match(/\.(csv|tsv|json|topojson)(?:[?#]|$)/);
  if (!match) return null;
  return match[1] as DataFormat;
}
