/**
 * Read-time migration for Snippet records (docs/architecture/02 §4).
 *
 * The IndexedDB database version governs store *layout*; this governs the shape
 * of an individual *record*. Every snippet read from storage passes through
 * `migrateSnippet`, which fills in missing/old fields and stamps the current
 * version. It must tolerate unknown fields (spread the original, only fill gaps)
 * so a record written by a newer build round-trips without data loss.
 */

import { CURRENT_SNIPPET_VERSION, type Snippet } from '@core/snippet';

/** Coerce a stored spec field (object or string) into the canonical string form. */
function asSpecText(value: unknown, fallback: string): string {
  if (typeof value === 'string') return value;
  if (value == null) return fallback;
  try {
    return JSON.stringify(value, null, 2);
  } catch {
    return fallback;
  }
}

/** Upgrade a raw stored record to the current Snippet shape. */
export function migrateSnippet(raw: unknown): Snippet {
  const r = { ...(raw as Record<string, unknown>) };
  // Records written before versioning existed are treated as v1.
  // (No structural migrations yet; this is the hook for future versions.)

  const spec = asSpecText(r.spec, '{}');
  const draftSpec = asSpecText(r.draftSpec, spec);

  return {
    ...r,
    id: String(r.id),
    version: CURRENT_SNIPPET_VERSION,
    name: typeof r.name === 'string' ? r.name : 'Untitled',
    created: typeof r.created === 'string' ? r.created : new Date(0).toISOString(),
    modified: typeof r.modified === 'string' ? r.modified : new Date(0).toISOString(),
    spec,
    draftSpec,
    comment: typeof r.comment === 'string' ? r.comment : '',
    tags: Array.isArray(r.tags) ? (r.tags as string[]) : [],
    datasetRefs: Array.isArray(r.datasetRefs) ? (r.datasetRefs as string[]) : [],
    meta: typeof r.meta === 'object' && r.meta !== null ? (r.meta as Record<string, unknown>) : {},
  };
}
