/**
 * Scene Preset contract.
 *
 * A Scene Preset is a named snapshot of the Visibility Matrix, stored as a
 * flag on a FoundryVTT Scene document. Up to 50 presets per world.
 *
 * Storage key: scene.getFlag('scrying-pool', 'preset')
 * Shape: { _version: 1, presets: { [name: string]: ScenePreset } }
 *
 * @module contracts/scene-preset
 */

/**
 * @typedef {Object} ScenePreset
 * @property {1} _version - Schema version; always 1 for v1.
 * @property {string} name - Unique preset name (non-empty string).
 * @property {Object.<string, string>} matrix - userId→VisibilityState snapshot.
 * @property {number} createdAt - Timestamp (ms) when preset was created.
 * @property {number} updatedAt - Timestamp (ms) when preset was last updated.
 */

export const SCENE_PRESET_VERSION = 1;
export const MAX_PRESETS_PER_WORLD = 50;

/**
 * Creates a new ScenePreset.
 * @param {string} name - Unique preset name.
 * @param {Object.<string, string>} [matrix={}] - userId→VisibilityState snapshot.
 * @param {number} [now] - Timestamp; defaults to Date.now().
 * @returns {ScenePreset}
 */
export function createScenePreset(name, matrix = {}, now = Date.now()) {
  return {
    _version: SCENE_PRESET_VERSION,
    name,
    matrix: { ...matrix },
    createdAt: now,
    updatedAt: now,
  };
}

/**
 * Validates a ScenePreset DTO. Throws TypeError on any violation.
 * @param {unknown} data - Value to validate.
 * @returns {ScenePreset} The validated preset.
 * @throws {TypeError} If data fails validation.
 */
export function isValidScenePreset(data) {
  if (data === null || typeof data !== "object") {
    throw new TypeError("ScenePreset: must be an object");
  }
  const obj = /** @type {Record<string, unknown>} */ (data);
  const { _version, name, matrix, createdAt, updatedAt, ...rest } = obj;
  if (Object.keys(rest).length > 0) {
    throw new TypeError(`ScenePreset: unknown keys: ${Object.keys(rest).join(", ")}`);
  }
  if (_version !== SCENE_PRESET_VERSION) {
    throw new TypeError(`ScenePreset: _version must be ${SCENE_PRESET_VERSION}, got ${_version}`);
  }
  if (typeof name !== "string" || name.length === 0) {
    throw new TypeError("ScenePreset: name must be a non-empty string");
  }
  if (matrix === null || typeof matrix !== "object" || Array.isArray(matrix)) {
    throw new TypeError("ScenePreset: matrix must be a plain object");
  }
  const matrixObj = /** @type {Record<string, unknown>} */ (matrix);
  for (const [userId, state] of Object.entries(matrixObj)) {
    if (typeof userId !== "string" || userId.length === 0) {
      throw new TypeError(`ScenePreset: userId key must be non-empty string, got "${userId}"`);
    }
    if (typeof state !== "string" || state.length === 0) {
      throw new TypeError(`ScenePreset: state for "${userId}" must be a non-empty string`);
    }
  }
  if (typeof createdAt !== "number" || !Number.isFinite(createdAt) || createdAt < 0) {
    throw new TypeError("ScenePreset: createdAt must be a finite non-negative integer");
  }
  if (typeof updatedAt !== "number" || !Number.isFinite(updatedAt) || updatedAt < 0) {
    throw new TypeError("ScenePreset: updatedAt must be a finite non-negative integer");
  }
  return /** @type {ScenePreset} */ (data);
}
