Source: plugin-wikidata/src/entity.js

import { logger } from '@citation-js/core'
import * as response from './response.js'
import { parseProp, getLabel } from './prop.js'
import props from './props.json'
import ignoredProps from './ignoredProps.json'

/**
 * @access private
 * @memberof module:@citation-js/plugin-wikidata.parsers.entity
 * @param {String} prop_ - Chain of nested Wikidata properties
 * @param {Object} entity
 * @param {Set} unknown
 * @return {Object} statement
 */
function resolveProp (prop_, entity, unkown) {
  function resolve ([prop, ...parts], { claims }) {
    if (!parts.length) {
      return claims[prop]
    } else if (claims[prop] && claims[prop].length) {
      return resolve(parts, claims[prop][0].value)
    }
  }

  const parts = prop_.split('.')
  unkown.delete(parts[0])

  return resolve(parts, entity)
}

/**
 * @access private
 * @memberof module:@citation-js/plugin-wikidata.parsers.entity
 * @param {Object|String} statement
 * @param {Object} entity
 * @param {Set} unknown
 * @return {module:@citation-js/core~CSL}
 */
function prepareValue (statement, entity, unkown) {
  if (typeof statement !== 'object') {
    const value = resolveProp(statement, entity, unkown)
    return value && value[0].value
  }

  const values = [].concat(...statement.props
    .map(prop => resolveProp(prop, entity, unkown))
    .filter(Boolean)
  )

  if (statement.values === 'all') {
    return values[0] && values
  } else {
    return values[0] && values[0].value
  }
}

/**
 * @access private
 * @memberof module:@citation-js/plugin-wikidata.parsers.entity
 * @param {Object} entity - The input data
 * @return {module:@citation-js/core~CSL} The formatted input data
 */
export function parseEntity (entity) {
  const data = {
    id: entity.id,
    custom: { QID: entity.id },
    source: 'Wikidata'
  }

  const unkown = new Set(Object.keys(entity.claims))

  for (const prop in props) {
    const input = prepareValue(props[prop], entity, unkown)
    if (input) {
      const output = parseProp(prop, input, entity)
      if (output) {
        data[prop] = output
      }
    }
  }

  for (const prop of unkown) {
    if (prop in ignoredProps) {
      continue
    }

    logger.unmapped('[plugin-wikidata]', 'property', prop)
  }

  if (!data.title) {
    data.title = getLabel(entity)
  }

  // BEGIN: Hot-fix some types

  if (data['reviewed-title'] || data['reviewed-author']) {
    // not all
    if (data.type.slice(0, 6) !== 'review') {
      data.type = 'review'
    }

    // P921 (main subject) is used for review subjects and keywords
    delete data.keyword
  }

  if (data.recipient) {
    data.type = 'personal_communication'
  }

  if (data['event-title']) {
    data.type = 'paper-conference'
  }

  if (typeof data['part-number'] === 'number') {
    delete data['part-title']
  } else if (typeof data['part-title'] === 'string') {
    delete data['part-number']
  }

  if (data.type !== 'chapter' && data['chapter-number']) {
    delete data['chapter-number']
  }

  // END

  return data
}

/**
 * Asynchronously parse entities from an API response.
 *
 * @access protected
 * @method parseAsync
 * @memberof module:@citation-js/plugin-wikidata.parsers.entity
 * @param {Object} data - The input data
 * @return {Promise<Array<module:@citation-js/core~CSL>>} The formatted input data
 */
export async function parseEntitiesAsync ({ entities }) {
  return (await response.parseAsync(entities)).map(parseEntity)
}

/**
 * Parse entities from an API response.
 *
 * @access protected
 * @method parse
 * @memberof module:@citation-js/plugin-wikidata.parsers.entity
 * @param {Object} data - The input data
 * @return {Array<module:@citation-js/core~CSL>} The formatted input data
 */
export function parseEntities ({ entities }) {
  return response.parse(entities).map(parseEntity)
}

export {
  parseEntities as parse,
  parseEntitiesAsync as parseAsync,
  parseEntities as default
}