/**
 * Visibility Matrix contract.
 *
 * Canonical shape: { _version: 1, matrix: { [userId: string]: VisibilityState } }
 * where VisibilityState ∈ VISIBILITY_STATES.
 *
 * StateStore is the sole writer of this structure. All other modules treat it as read-only.
 *
 * @module contracts/visibility-matrix
 */

/** @typedef {'active'|'hidden'|'self-muted'|'offline'|'cam-lost'|'reconnecting'|'never-connected'|'ghost'} VisibilityState */

/**
 * @typedef {Object} VisibilityMatrix
 * @property {1} _version - Schema version; always 1 for v1.
 * @property {Object.<string, VisibilityState>} matrix - userId → VisibilityState map.
 * @property {number} [_revision] - Optional revision counter for optimistic concurrency control.
 */

export const VISIBILITY_STATES = Object.freeze([
  "active",
  "hidden",
  "self-muted",
  "offline",
  "cam-lost",
  "reconnecting",
  "never-connected",
  "ghost",
]);

export const VISIBILITY_MATRIX_VERSION = 1;

/**
 * Creates a new VisibilityMatrix with an optional initial matrix.
 * @param {Object.<string, VisibilityState>} [matrix={}] - Initial userId→state entries.
 * @returns {VisibilityMatrix}
 */
export function createVisibilityMatrix(matrix = {}) {
  return { _version: VISIBILITY_MATRIX_VERSION, matrix: { ...matrix } };
}

/**
 * Validates a VisibilityMatrix DTO. Throws TypeError on any violation.
 * @param {unknown} data - Value to validate.
 * @returns {VisibilityMatrix} The validated matrix.
 * @throws {TypeError} If data fails validation.
 */
export function isValidVisibilityMatrix(data) {
  if (data === null || typeof data !== "object") {
    throw new TypeError("VisibilityMatrix: must be an object");
  }
  const obj = /** @type {Record<string, unknown>} */ (data);
  const { _version, matrix, _revision, ...rest } = obj;
  if (Object.keys(rest).length > 0) {
    throw new TypeError(`VisibilityMatrix: unknown keys: ${Object.keys(rest).join(", ")}`);
  }
  if (_version !== VISIBILITY_MATRIX_VERSION) {
    throw new TypeError(`VisibilityMatrix: _version must be ${VISIBILITY_MATRIX_VERSION}, got ${_version}`);
  }
  if (matrix === null || typeof matrix !== "object" || Array.isArray(matrix)) {
    throw new TypeError("VisibilityMatrix: matrix must be a plain object");
  }
  if (_revision !== undefined && (typeof _revision !== "number" || !Number.isFinite(_revision) || _revision < 0)) {
    throw new TypeError("VisibilityMatrix: _revision must be a finite non-negative number");
  }
  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(`VisibilityMatrix: userId must be a non-empty string, got "${userId}"`);
    }
    if (!VISIBILITY_STATES.includes(/** @type {any} */ (state))) {
      throw new TypeError(`VisibilityMatrix: invalid state "${state}" for userId "${userId}"`);
    }
  }
  return /** @type {VisibilityMatrix} */ (data);
}
