'use strict'
var LayeredGraph = require('layered-graph')
var pull         = require('pull-stream')
var isFeed       = require('ssb-ref').isFeed
// friends plugin
// methods to analyze the social graph
// maintains a 'follow' and 'flag' graph

exports.name = 'friends'
exports.version = '1.0.0'
exports.manifest = {
  hopStream: 'source',
  onEdge: 'sync',
  isFollowing: 'async',
  isBlocking: 'async',
  hops: 'async',
  help: 'sync',
  // createLayer: 'sync',       // not exposed over RPC as returns a function
  get: 'async',                 // legacy
  createFriendStream: 'source', // legacy
  stream: 'source'              // legacy
}

//mdm.manifest(apidoc)

exports.init = function (sbot, config) {
  var max = config.friends && config.friends.hops || config.replicate && config.replicate.hops || 3
  var layered = LayeredGraph({max: max, start: sbot.id})

  function isFollowing (opts, cb) {
    layered.onReady(function () {
      var g = layered.getGraph()
      cb(null, g[opts.source] && g[opts.source][opts.dest] >= 0)
    })
  }

  function isBlocking (opts, cb) {
    layered.onReady(function () {
      var g = layered.getGraph()
      cb(null, Math.round(g[opts.source] && g[opts.source][opts.dest]) == -1)
    })
  }

  //opinion: do not authorize peers blocked by this node.
  sbot.auth.hook(function (fn, args) {
    var self = this
    isBlocking({source: sbot.id, dest: args[0]}, function (err, blocked) {
      if(blocked)
        args[1](new Error('client is blocked'))
      else fn.apply(self, args)
    })
  })

  if(!sbot.replicate)
    throw new Error('ssb-friends expects a replicate plugin to be available')

  // opinion: replicate with everyone within max hops (max passed to layered above ^)
  pull(
    layered.hopStream({live: true, old: true}),
    pull.drain(function (data) {
      if(data.sync) return
      for(var k in data) {
        sbot.replicate.request(k, data[k] >= 0)
      }
    })
  )

  require('./contacts')(sbot, layered.createLayer, config)

  var legacy = require('./legacy')(layered)

  //opinion: pass the blocks to replicate.block
  setImmediate(function () {
    var block = (sbot.replicate && sbot.replicate.block) || (sbot.ebt && sbot.ebt.block)
    if(block) {
      function handleBlockUnlock(from, to, value) {
        if (value === false) block(from, to, true)
        else                 block(from, to, false)
      }
      pull(
        legacy.stream({live: true}),
        pull.drain(function (contacts) {
          if(!contacts) return

          if (isFeed(contacts.from) && isFeed(contacts.to)) { // live data
            handleBlockUnlock(contacts.from, contacts.to, contacts.value)
          } else { // initial data
            for (var from in contacts) {
              var relations = contacts[from]
              for (var to in relations)
                handleBlockUnlock(from, to, relations[to])
            }
          }
        })
      )
    }
  })

  return {
    hopStream: layered.hopStream,
    onEdge: layered.onEdge,
    isFollowing: isFollowing,
    isBlocking: isBlocking,

    // expose createLayer, so that other plugins may express relationships
    createLayer: layered.createLayer,

    // legacy, debugging
    hops: function (opts, cb) {
      layered.onReady(function () {
        if(isFunction(opts))
          cb = opts, opts = {}
        cb(null, layered.getHops(opts))
      })
    },
    help: function () { return require('./help') },
    // legacy
    get: legacy.get,
    createFriendStream: legacy.createFriendStream,
    stream: legacy.stream,
  }
}

// helpers

function isFunction (f) {
  return 'function' === typeof f
}

