import _ from 'lodash'
import { isMultilink } from './helpers'

/**
 * Compares two datasets
 * @param {*} first
 * @param {*} second
 * @return {Object}
 */
export function compareDatasets(first, second) {
  return {
    nodes: compareNodeArrays(first.nodes, second.nodes),
    links: compareLinkArrays(first.links, second.links),
    tags: compareTagArrays(first.tags, second.tags, first.nodes, second.nodes)
  }
}

function compareNodeArrays(firstRaw, secondRaw) {
  //console.log(`compareNodeArrays> first=${JSON.stringify(first)} second=${JSON.stringify(second)}`)
  if (!checkUuids(firstRaw) || !checkUuids(secondRaw))
    throw Error('Every entity needs to have UUID')

  const first = firstRaw.filter(n => n.type === 'regular')
  const second = secondRaw.filter(n => n.type === 'regular')

  const commonA = _.intersectionWith(first, second, isEqualEntity)
  const commonB = _.intersectionWith(second, first, isEqualEntity)
  const onlyInFirst = _.differenceWith(first, commonA, isEqualEntity)
  const onlyInSecond = _.differenceWith(second, commonA, isEqualEntity)

  const commonAndModified = []
  for (const nodeA of commonA) {
    const nodeB = second.find(n => isEqualEntity(n, nodeA))
    if (!nodeB) throw Error('Should find')
    // console.log(`COmpareNodes: A = ${JSON.stringify(nodeA)}`)
    // console.log(' ')
    // console.log(`COmpareNodes: B = ${JSON.stringify(nodeB)}`)
    // console.log(' ')
    // console.log(`${JSON.stringify(compareNodeAttrs(nodeA, nodeB))}`)
    const differentAttrs = compareNodeAttrs(nodeA, nodeB)
    if (differentAttrs.length > 0) commonAndModified.push(nodeA.uuid)
  }

  // console.log(`  > onlyInFirst=${JSON.stringify(onlyInFirst)} `)
  // console.log(`  > common=${JSON.stringify(common)} `)
  // console.log(`  > commonAndMOdified=${JSON.stringify(commonAndModified)} `)
  return { onlyInFirst, onlyInSecond, commonA, commonB, commonAndModified }
}

function compareLinkArrays(first_, second_) {
  if (!checkUuids(first_) || !checkUuids(second_)) throw Error('Every entity needs to have UUID')

  const first = first_.filter(l => !isMultilink(l)) //.map(l => ({ ...l, highlightUuids: isInMultilink(l) ? [l.uuid, l.inMultilink] : [l.uuid] }))
  const second = second_.filter(l => !isMultilink(l))

  const common = _.intersectionWith(first, second, isEqualEntity)
  const onlyInFirst = _.differenceWith(first, common, isEqualEntity)
  const onlyInSecond = _.differenceWith(second, common, isEqualEntity)

  return { onlyInFirst, onlyInSecond, common, commonAndModified: [] }
}

function isEqualEntity(a, b) {
  return a.uuid === b.uuid
}

function tagGatherUuids(tags, nodes) {
  return tags.map(tag => {
    let groupNodeUuid,
      assignedObjectUuids = [],
      highlightUuids = []
    const groupNode = nodes.find(n => n.uuid === tag.groupNodeId)
    if (groupNode) {
      groupNodeUuid = groupNode.uuid
      highlightUuids.push(groupNode.uuid)
    }
    if (tag.assignedObjectsIds) {
      assignedObjectUuids = nodes
        .filter(n => tag.assignedObjectsIds.includes(n.uuid))
        .map(n => n.uuid)
      highlightUuids = highlightUuids.concat(assignedObjectUuids)
    }
    //console.log(`${JSON.stringify({ ...tag, highlightUuids, groupNodeUuid, assignedObjectUuids })}`)
    return { ...tag, highlightUuids, groupNodeUuid, assignedObjectUuids }
  })
}

function compareTagArrays(firstRaw, secondRaw, firstNodes, secondNodes) {
  const first = tagGatherUuids(firstRaw, firstNodes)
  const second = tagGatherUuids(secondRaw, secondNodes)

  const common = _.intersectionWith(first, second, isEqualTag)
  const onlyInFirst = _.differenceWith(first, common, isEqualTag)
  const onlyInSecond = _.differenceWith(second, common, isEqualTag)

  return { onlyInFirst, onlyInSecond, common, commonAndModified: [] }
}

function isEqualTag(a, b) {
  return JSON.stringify(a.assignedObjectUuids) === JSON.stringify(b.assignedObjectUuids)
}

function checkUuids(arr) {
  for (const n of arr) {
    if (n.uuid === undefined) return false
  }
  return true
}

function compareNodeAttrs(nodeA, nodeB) {
  const getNodeAttrs = node => {
    return Object.keys(node)
      .filter(key => !['id', 'vis', 'credibility', 'uuid'].includes(key))
      .reduce((obj, key) => {
        obj[key] = node[key]
        return obj
      }, {})
  }
  const attrsA = getNodeAttrs(nodeA)
  const attrsB = getNodeAttrs(nodeB)

  const different = []
  for (const prop in attrsA) {
    if (!attrsB.hasOwnProperty(prop) || !attrsB.prop !== attrsA[prop]) different.push(prop)
  }

  return different
}
