init = {
  all: {},
  head: null,
  last: null
}

list = {
  all: {
    1: {
      id: '1',
      prev: null,
      next: '2'
    },
    2: {
      id: '2',
      prev: '1',
      next: '3'
    },
    3: {
      id: '3',
      prev: '2',
      next: null
    }
  },
  head: '1',
  last: '3'
}

get = flip(prop)

insertAfter = (prev, node, list) => {
  const next = prev ? list.all[prev].next : list.head

  return evolve({
    all: pipe(
      assoc(node.id, merge(node, { prev, next })),
      evolve({
        [ prev ]: assoc('next', node.id),
        [ next ]: assoc('prev', node.id)
      })
    ),
    head: prev || always(node.id),
    last: next || always(node.id)
  }, list)
}

remove = (id, list) => {
  const node = list.all[id]

  return evolve({
    all: pipe(
      dissoc(id),
      evolve({
        [ node.prev ]: assoc('next', node.next),
        [ node.next ]: assoc('prev', node.prev)
      })
    ),
    head: list.head === node.id && always(node.next),
    last: list.last === node.id && always(node.prev)
  }, list)
}

flattenLL = node => {
  if (!node) return []
  return []
    .concat(
      evolve({ 
        prev: node.prev && prop('id'), 
        next: node.next && prop('id')
      }, node)
    )
    .concat(flattenLL(node.next))
}

toArray = compose(concat([]), flattenLL, toLL)

toNode = all => {
  const inflate = compose(toNode(all), get(all))
  return when(Boolean, evolve({ prev: inflate, next: inflate }))
}

toLL = list =>
  toNode(list.all)(list.head)

// insertHead = (node, list) =>
//   evolve({
//     all: pipe(
//       assoc(node.id, merge(node, { prev: null, next: list.head })),
//       evolve({
//         [ list.head ]: assoc('prev', node.id)
//       })
//     ),
//     head: always(node.id),
//     last: keys(list.all).length || always(node.id)
//   }, list)

// insertLast = (node, list) =>
//   evolve({
//     all: pipe(
//       assoc(node.id, merge(node, { prev: list.last, next: null })),
//       evolve({
//         [ list.last ]: assoc('next', node.id)
//       })
//     ),
//     head: keys(list.all).length || always(node.id)
//     last: always(node.id)
//   }, list)