import { transform, isEqual, isObject, get } from 'lodash'

type Dict = {
  [key: string]: boolean | Dict
}

export function diff(newObject: any = {}, oldObject: any = {}, ignore?: Dict) {
  const currentIgnoreList = ignore
    ? Object.entries(ignore).reduce(
        (accumulator, [key, value]) =>
          typeof value === 'boolean' ? [...accumulator, key] : accumulator,
        [] as string[]
      )
    : []
  const transformResult = transform(
    newObject,
    (result: any, value: any, key: string) => {
      if (!currentIgnoreList.includes(key) && !isEqual(value, oldObject[key])) {
        const child = ignore && ignore[key]
        let childIgnoreList
        if (result instanceof Array) {
          childIgnoreList = ignore
        } else if (typeof child !== 'boolean') {
          childIgnoreList = child
        } else {
          childIgnoreList = undefined
        }
        const newValue =
          isObject(value) && isObject(oldObject[key])
            ? diff(value, oldObject[key], childIgnoreList)
            : value
        if (newValue !== undefined && newValue !== null) result[key] = newValue
      }
    }
  )
  return Object.keys(transformResult).length > 0 ? transformResult : undefined
}

export function deepDiff(
  newObject: any = {},
  oldObject: any = {},
  ignore?: Dict
) {
  return {
    added: diff(newObject, oldObject, ignore),
    removed: diff(oldObject, newObject, ignore)
  }
}

interface IcompareOptions<T> {
  compareKeys: (keyof T)[]
  diffIgnore?: Dict
  keyNames?: {
    inA: string
    inB: string
  }
}

export function compare<T>(
  objectsA: T[],
  objectsB: T[],
  compareOptions: IcompareOptions<T>
) {
  const { keyNames, diffIgnore, compareKeys } = compareOptions
  const { inA, inB } = keyNames || { inA: 'inFile', inB: 'inDb' }
  let copyB = [...objectsB]
  let copyA: T[] = []
  for (let objectA of objectsA) {
    const idxObjectB = copyB.findIndex(objectB =>
      compareKeys.reduce((accumulator, value) => {
        console.log(get)
        return accumulator && get(objectA, value, 1) === get(objectB, value, 2)
      }, true as boolean)
    )
    const objectB = copyB[idxObjectB]
    copyB = [...copyB.slice(0, idxObjectB), ...copyB.slice(idxObjectB + 1)]
    copyA = [
      ...copyA,
      {
        ...objectA,
        changes: deepDiff(objectA, objectB, diffIgnore),
        [inA]: true,
        [inB]: !!objectB
      }
    ]
  }
  for (let objectB of copyB) {
    copyA = [
      ...copyA,
      {
        ...objectB,
        changes: deepDiff(undefined, objectB, diffIgnore),
        [inA]: false,
        [inB]: true
      }
    ]
  }
  return copyA
}
