/**
 * Export envelope — the single JSON object Astrolabe downloads to back up or
 * transfer a whole workspace (spec §08 → Export, Export envelope shape).
 *
 * Portable core: no browser APIs, no React. Builds the in-memory envelope, the
 * download filename, and the success-toast message as plain data. The actual file
 * download (Blob/anchor/DOM) is browser code that lives in the app layer; this
 * module only shapes what that code serializes and reports.
 *
 * The envelope `version` is the **file-format** version, distinct from the
 * per-record `version` fields each snippet/dataset carries (their read-time
 * migration target — see snippet.ts / dataset.ts).
 */

import type { CustomTheme } from './custom-theme';
import type { Dataset } from './dataset';
import { serializeFontAsset, type FontAsset, type SerializedFontAsset } from './font-asset';
import type { Snippet } from './snippet';

/**
 * Current export file-format version (spec §08 → "currently `\"1.0\"`"). Bump when
 * the envelope shape changes in a way importers must branch on; this is NOT the
 * per-record schema version.
 */
export const EXPORT_ENVELOPE_VERSION = '1.0';

/**
 * The downloaded file's top-level shape (spec §08 → Export envelope shape): format
 * metadata plus the complete-record arrays. Each record keeps its own `version`
 * field unchanged. `themes` and `fonts` are additive (always written, optional on
 * read) so older envelopes and importers remain compatible without a format bump.
 */
export interface ExportEnvelope {
  /** Export format version (currently `"1.0"`). */
  version: string;
  /** ISO 8601 timestamp of the export. */
  exportedAt: string;
  /** Fixed exporter tag. */
  exportedBy: 'Astrolabe';
  /** All snippets, as complete records (each including its record `version`). */
  snippets: Snippet[];
  /** All datasets, as complete records (each including its record `version`). */
  datasets: Dataset[];
  /** All custom chart themes, as complete records (each including its record `version`). */
  themes: CustomTheme[];
  /**
   * All uploaded font faces, base64-encoded so a referenced face survives the
   * round-trip (without this the family name imports but renders as fallback —
   * spec §08, scope doc §4). The whole library travels, like datasets/themes: a
   * workspace export is a backup, not a minimal bundle of what's referenced.
   */
  fonts: SerializedFontAsset[];
}

/**
 * Build the export envelope (spec §08 → Export). Stamps the format version, the
 * export timestamp (`now.toISOString()`), and the fixed exporter tag, and copies
 * the records into fresh arrays so the envelope does not alias the caller's arrays
 * (the records themselves are referenced as-is — they are the complete records to
 * serialize, each keeping its own `version`).
 */
export function buildExportEnvelope(
  snippets: ReadonlyArray<Snippet>,
  datasets: ReadonlyArray<Dataset>,
  themes: ReadonlyArray<CustomTheme>,
  fonts: ReadonlyArray<FontAsset>,
  opts: { now: Date },
): ExportEnvelope {
  return {
    version: EXPORT_ENVELOPE_VERSION,
    exportedAt: opts.now.toISOString(),
    exportedBy: 'Astrolabe',
    snippets: [...snippets],
    datasets: [...datasets],
    themes: [...themes],
    // Encode each face's bytes (ArrayBuffer → base64) so the array is JSON-safe.
    fonts: fonts.map(serializeFontAsset),
  };
}

/** Two-digit zero-pad for the filename date (mirrors snippet.ts → generateSnippetName). */
function pad(n: number): string {
  return String(n).padStart(2, '0');
}

/**
 * Download filename for an export (spec §08 → Filename):
 * `astrolabe-project-YYYY-MM-DD.json`, where the date is today's date (export day).
 * Uses local date parts, like `generateSnippetName`.
 */
export function exportFilename(now: Date): string {
  const date = `${now.getFullYear()}-${pad(now.getMonth() + 1)}-${pad(now.getDate())}`;
  return `astrolabe-project-${date}.json`;
}

/** Pluralize a count's noun: "1 snippet" / "4 snippets". */
function countClause(count: number, noun: string): string {
  return `${count} ${noun}${count === 1 ? '' : 's'}`;
}

/**
 * Success-toast message reporting transfer counts (spec §08 → Feedback) for both
 * directions, e.g. "Exported 4 snippets, 2 datasets and 1 theme" / "Imported 1
 * snippet". The dataset, theme, and font clauses are omitted entirely when their
 * counts are zero; singular/plural wording adapts. One builder for export and
 * import so a new record kind or wording change lands in both messages at once.
 */
export function transferSummaryMessage(
  verb: 'Exported' | 'Imported',
  snippetCount: number,
  datasetCount: number,
  themeCount: number,
  fontCount: number,
): string {
  const clauses = [countClause(snippetCount, 'snippet')];
  if (datasetCount > 0) clauses.push(countClause(datasetCount, 'dataset'));
  if (themeCount > 0) clauses.push(countClause(themeCount, 'theme'));
  if (fontCount > 0) clauses.push(countClause(fontCount, 'font'));
  const last = clauses.pop()!;
  return clauses.length === 0 ? `${verb} ${last}` : `${verb} ${clauses.join(', ')} and ${last}`;
}
