const error_ = require('../error')
const { isGuid, isEntityId, isPropertyId, getEntityIdFromGuid } = require('wikibase-sdk')
const { getEntityClaims } = require('../get_entity')
const { findClaimByGuid } = require('./helpers')
const { propertiesDatatypesDontMatch } = require('./move_commons')

module.exports = async (params, config, API) => {
  const { guid, propertyClaimsId, id: targetEntityId, property: targetPropertyId, baserevid } = params

  let originEntityId, originPropertyId

  if (guid) {
    if (!isGuid(guid)) throw error_.new('invalid claim guid', 400, params)
    originEntityId = getEntityIdFromGuid(guid)
  } else if (propertyClaimsId) {
    ([ originEntityId, originPropertyId ] = propertyClaimsId.split('#'))
    if (!(isEntityId(originEntityId) && isPropertyId(originPropertyId))) {
      throw error_.new('invalid property claims id', 400, params)
    }
  } else {
    throw error_.new('missing claim guid or property claims id', 400, params)
  }

  if (!targetEntityId) throw error_.new('missing target entity id', 400, params)
  if (!isEntityId(targetEntityId)) throw error_.new('invalid target entity id', 400, params)

  if (!targetPropertyId) throw error_.new('missing property id', 400, params)
  const propertyDatatype = config.properties[targetPropertyId]

  const claims = await getEntityClaims(originEntityId, config)

  let movedClaims
  if (guid) {
    const claim = findClaimByGuid(claims, guid)
    if (!claim) throw error_.new('claim not found', 400, params)
    originPropertyId = claim.mainsnak.property
    movedClaims = [ claim ]
  } else {
    movedClaims = claims[originPropertyId]
    if (!movedClaims) throw error_.new('no property claims found', 400, params)
  }

  if (originEntityId === targetEntityId && originPropertyId === targetPropertyId) {
    throw error_.new("move operation wouldn't have any effect: same entity, same property", 400, params)
  }

  const { datatype: currentPropertyDatatype } = movedClaims[0].mainsnak

  if (propertyDatatype !== currentPropertyDatatype) {
    propertiesDatatypesDontMatch({
      movedSnaks: movedClaims,
      originPropertyId,
      originDatatype: currentPropertyDatatype,
      targetPropertyId,
      targetDatatype: propertyDatatype,
    })
  }

  const currentEntityData = {
    rawMode: true,
    id: originEntityId,
    claims: movedClaims.map(claim => ({ id: claim.id, remove: true })),
    summary: params.summary || config.summary || generateCurrentEntitySummary(guid, originEntityId, originPropertyId, targetEntityId, targetPropertyId)
  }

  movedClaims.forEach(claim => {
    delete claim.id
    claim.mainsnak.property = targetPropertyId
  })

  if (originEntityId === targetEntityId) {
    currentEntityData.claims.push(...movedClaims)
    currentEntityData.baserevid = baserevid
    const res = await API.entity.edit(currentEntityData, config)
    return [ res ]
  } else {
    if (baserevid) throw error_.new('commands editing multiple entities can not have a baserevid', 400, params)
    const targetEntityData = {
      rawMode: true,
      id: targetEntityId,
      claims: movedClaims,
      summary: params.summary || config.summary || generateTargetEntitySummary(guid, originEntityId, originPropertyId, targetEntityId, targetPropertyId)
    }
    const removeClaimsRes = await API.entity.edit(currentEntityData, config)
    const addClaimsRes = await API.entity.edit(targetEntityData, config)
    return [ removeClaimsRes, addClaimsRes ]
  }
}

const generateCurrentEntitySummary = (guid, originEntityId, originPropertyId, targetEntityId, targetPropertyId) => {
  if (guid) {
    if (originEntityId === targetEntityId) {
      return `moving a ${originPropertyId} claim to ${targetPropertyId}`
    } else {
      return `moving a ${originPropertyId} claim to ${targetEntityId}#${targetPropertyId}`
    }
  } else {
    if (originEntityId === targetEntityId) {
      return `moving ${originPropertyId} claims to ${targetPropertyId}`
    } else {
      return `moving ${originPropertyId} claims to ${targetEntityId}#${targetPropertyId}`
    }
  }
}

const generateTargetEntitySummary = (guid, originEntityId, originPropertyId, targetEntityId, targetPropertyId) => {
  if (guid) {
    return `moving a ${originEntityId}#${originPropertyId} claim from ${targetEntityId}#${targetPropertyId}`
  } else {
    return `moving ${originEntityId}#${originPropertyId} claims from ${targetEntityId}#${targetPropertyId}`
  }
}
