/**
 * @typedef {'toml' | 'yaml'} Preset
 *   Known name of a frontmatter style.
 *
 * @typedef Info
 *   Sequence.
 *
 *   Depending on how this structure is used, it reflects a marker or a fence.
 * @property {string} close
 *   Closing.
 * @property {string} open
 *   Opening.
 *
 * @typedef MatterProps
 *   Fields describing a kind of matter.
 * @property {string} type
 *   Node type to tokenize as.
 * @property {boolean | null | undefined} [anywhere=false]
 *   Whether matter can be found anywhere in the document, normally, only matter
 *   at the start of the document is recognized.
 *
 *   > πŸ‘‰ **Note**: using this is a terrible idea.
 *   > It’s called frontmatter, not matter-in-the-middle or so.
 *   > This makes your markdown less portable.
 *
 * @typedef MarkerProps
 *   Marker configuration.
 * @property {Info | string} marker
 *   Character repeated 3 times, used as complete fences.
 *
 *   For example the character `'-'` will result in `'---'` being used as the
 *   fence
 *   Pass `open` and `close` to specify different characters for opening and
 *   closing fences.
 * @property {never} [fence]
 *   If `marker` is set, `fence` must not be set.
 *
 * @typedef FenceProps
 *   Fence configuration.
 * @property {Info | string} fence
 *   Complete fences.
 *
 *   This can be used when fences contain different characters or lengths
 *   other than 3.
 *   Pass `open` and `close` to interface to specify different characters for opening and
 *   closing fences.
 * @property {never} [marker]
 *   If `fence` is set, `marker` must not be set.
 *
 * @typedef {(MatterProps & FenceProps) | (MatterProps & MarkerProps)} Matter
 *   Fields describing a kind of matter.
 *
 *   > πŸ‘‰ **Note**: using `anywhere` is a terrible idea.
 *   > It’s called frontmatter, not matter-in-the-middle or so.
 *   > This makes your markdown less portable.
 *
 *   > πŸ‘‰ **Note**: `marker` and `fence` are mutually exclusive.
 *   > If `marker` is set, `fence` must not be set, and vice versa.
 *
 * @typedef {Matter | Preset | Array<Matter | Preset>} Options
 *   Configuration.
 */

import {fault} from 'fault'
const own = {}.hasOwnProperty
const markers = {
  yaml: '-',
  toml: '+'
}

/**
 * Simplify options by normalizing them to an array of matters.
 *
 * @param {Options | null | undefined} [options='yaml']
 *   Configuration (default: `'yaml'`).
 * @returns {Array<Matter>}
 *   List of matters.
 */
export function toMatters(options) {
  /** @type {Array<Matter>} */
  const result = []
  let index = -1

  /** @type {Array<Matter | Preset>} */
  const presetsOrMatters = Array.isArray(options)
    ? options
    : options
    ? [options]
    : ['yaml']
  while (++index < presetsOrMatters.length) {
    result[index] = matter(presetsOrMatters[index])
  }
  return result
}

/**
 * Simplify an option.
 *
 * @param {Matter | Preset} option
 *   Configuration.
 * @returns {Matter}
 *   Matter.
 */
function matter(option) {
  let result = option
  if (typeof result === 'string') {
    if (!own.call(markers, result)) {
      throw fault('Missing matter definition for `%s`', result)
    }
    result = {
      type: result,
      marker: markers[result]
    }
  } else if (typeof result !== 'object') {
    throw fault('Expected matter to be an object, not `%j`', result)
  }
  if (!own.call(result, 'type')) {
    throw fault('Missing `type` in matter `%j`', result)
  }
  if (!own.call(result, 'fence') && !own.call(result, 'marker')) {
    throw fault('Missing `marker` or `fence` in matter `%j`', result)
  }
  return result
}