-jraphical = require 'jraphical'
-
-JAccount = require '../account'
-JUser = require '../user'
-JVM = require '../vm'
+jraphical   = require 'jraphical'
 KodingError = require '../../error'
-{argv}   = require 'optimist'
-KONFIG = require('koding-config-manager').load("main.#{argv.c}")
-
+
+JAccount    = require '../account'
+JUser       = require '../user'
+
+{argv}      = require 'optimist'
+KONFIG      = require('koding-config-manager').load("main.#{argv.c}")
 
 module.exports = class JReferral extends jraphical.Message
 


  @share()

  schema =
    # for now we only have disk space
    type              :
      type            : String
    unit              :
      type            : String
    amount            :
      type            : Number
    sourceCampaign    :
      type            : String
      default         : "register"
    createdAt         :
      type            : Date
      default         : -> new Date

  @set
    sharedMethods     :
      static          :
        addExtraReferral:
          (signature Object, Function)
        redeem:
          (signature Object, Function)
        changeBaseVMForUsers:[
          (signature Function)
          (signature Object, Function)
        ]
        resetVMDefaults:
          (signature Function)
        fetchRedeemableReferrals:
          (signature Object, Function)
        fetchReferredAccounts:
          (signature Object, Object, Function)
        fetchEarnedSpace:
          (signature Function)
    sharedEvents      :
      static          : []
      instance        : []
    schema            : schema
    relationships     :
      redeemedOn      :
        targetType    : 'JVM'
        as            : 'redeemedOn'


  @getReferralEarningLimits = (type) ->
    switch type
      when "disk" then return 17408
      else return console.error "Unknown referral query limit"

  # Simply find the unused referrals
  # me-[:referrer]->JReferral<-[:referred]-JAccount
  @fetchRedeemableReferrals = secure (client, query, callback)->
    # check for type of request
    allowedTypes = ["disk"]
    return callback new KodingError "Request is not valid" unless query.type in allowedTypes

    @fetchOwnReferralsIds client, { type: query.type }, {}, (err, allReferalIds) =>
      return callback err if err

      @fetchUsedReferrals allReferalIds, (err, usedReferrals) =>
        return callback err if err

        # aggregate the used referrals
        usedRefIds = []
        usedRefIds.push "#{usedReferral._id}" for usedReferral in usedReferrals when id not in usedRefIds

        # find the diff of unused referrals
        unusedRefIds = []
        unusedRefIds.push id for id in allReferalIds when id not in usedRefIds

        return callback null, [] if unusedRefIds.length < 1

        refSelector = { _id: $in: unusedRefIds }
        JReferral.some refSelector, {}, (err, unusedReferrals)=>
          return callback err if err

          # find the total used referred disk spacea
          totalUsedReferalAmount = 0
          totalUsedReferalAmount += ref.amount for ref in usedReferrals

          limit = @getReferralEarningLimits(query.type)
          # find the usable amount
          usableReferralSize = limit - totalUsedReferalAmount
          # if user consumed their quota return an error
          # this is a special case for used up quota
          if usableReferralSize <= 0
            return callback new KodingError "You have used your quota for referral system"

          # gather usable referrals
          usableReferrals = []
          for unusedRef in unusedReferrals
            usableReferralSize -= unusedRef.amount
            # when we go under the limit, do not continue
            break if usableReferralSize < 0
            usableReferrals.push unusedRef

          callback null, usableReferrals


  @fetchUsedReferrals = (allReferalIds, callback)->

    usedReferralsSelector = {
      sourceId    : { $in: allReferalIds },
      sourceName  : 'JReferral',
      targetName  : 'JVM',
      as          : 'redeemedOn'
    }

    Relationship.some usedReferralsSelector, {}, (err, usedReferralsRels)=>
      return callback err if err

      usedRefIds = usedReferralsRels.map (rel) -> rel.sourceId

      JReferral.some { _id: $in: usedRefIds }, {}, callback

  @fetchOwnReferralsIds = (client, query, options, callback)->
    account = client.connection.delegate

    # find user's relationships as referrer to the referral system
    selector = {
      sourceId    : account.getId(),
      sourceName  : 'JAccount',
      targetName  : 'JReferral',
      as          : 'referrer'
    }

    Relationship.count selector, (err, count) =>
      return callback err if err
      options.targetOptions = { selector: query }
      @fetchOwnReferralsIdsInBatch count, 100, selector, options, (err, relationships)->
        allReferalIds = relationships.map (rel) -> "#{rel.targetId}"
        callback null, allReferalIds


  @fetchOwnReferralsIdsInBatch = (totalCount, batchCount, selector, options, callback)->
    teasers = []
    collectRels = race (i, step, fin)->
      options.skip  = batchCount * (step - 1)
      options.limit = batchCount
      Relationship.some selector, options, (err, relationships)=>
        if err
          callback err
          fin()
        else if not relationships
          fin()
        else
          teasers.push rel for rel in relationships
          fin()
    , ->
      callback null, teasers
    totalStep = Math.floor(totalCount/batchCount)
    totalStep = if totalStep <= 0 then 1 else totalStep
    while totalStep
      collectRels totalStep
      totalStep--

  @fetchEarnedSpace$ = secure (client, callback)->
    @fetchEarnedSpace client, callback

  @spaceForReferring = 500

  @fetchEarnedSpace = (client, callback)->
    @fetchReferredAccounts client, {}, {}, (err, accounts)=>
      return callback err  if err
      callback null, accounts.length * @spaceForReferring

  @fetchReferredAccounts$ = secure (client, query, options, callback)->
    @fetchReferredAccounts client, query, options, callback

    username = client.connection.delegate.profile.nickname
    JAccount.some { referrerUsername : username }, options, callback


  @addExtraReferral: secure (client, options, callback)->
    {delegate} = client.connection
