const { Subscription } = require('../../app/src/models/Subscription')
const RecurlyWrapper = require('../../app/src/Features/Subscription/RecurlyWrapper')
const SubscriptionUpdater = require('../../app/src/Features/Subscription/SubscriptionUpdater')
const async = require('async')
const minimist = require('minimist')

// make sure all `allMismatchReasons` are displayed in the output
const util = require('util')
util.inspect.defaultOptions.maxArrayLength = null

const ScriptLogger = {
  checkedSubscriptionsCount: 0,
  mismatchSubscriptionsCount: 0,
  allMismatchReasons: {},

  recordMismatch: (subscription, recurlySubscription) => {
    const mismatchReasons = {}
    if (subscription.planCode !== recurlySubscription.plan.plan_code) {
      mismatchReasons.recurlyPlan = recurlySubscription.plan.plan_code
      mismatchReasons.olPlan = subscription.planCode
    }
    if (recurlySubscription.state === 'expired') {
      mismatchReasons.state = 'expired'
    }

    if (!Object.keys(mismatchReasons).length) {
      return
    }

    ScriptLogger.mismatchSubscriptionsCount += 1
    const mismatchReasonsString = JSON.stringify(mismatchReasons)
    if (ScriptLogger.allMismatchReasons[mismatchReasonsString]) {
      ScriptLogger.allMismatchReasons[mismatchReasonsString].push(
        subscription._id
      )
    } else {
      ScriptLogger.allMismatchReasons[mismatchReasonsString] = [
        subscription._id
      ]
    }
  },

  printProgress: () => {
    console.warn(
      `Subscriptions checked: ${ScriptLogger.checkedSubscriptionsCount}. Mismatches: ${ScriptLogger.mismatchSubscriptionsCount}`
    )
  },

  printSummary: () => {
    console.log('All Mismatch Reasons:', ScriptLogger.allMismatchReasons)
    console.log(
      'Mismatch Subscriptions Count',
      ScriptLogger.mismatchSubscriptionsCount
    )
  }
}

const slowCallback = callback => setTimeout(callback, 80)

const handleSyncSubscriptionError = (subscription, error, callback) => {
  console.warn(`Errors with subscription id=${subscription._id}:`, error)
  if (typeof error === 'string' && error.match(/429$/)) {
    return setTimeout(callback, 1000 * 60 * 5)
  }
  if (typeof error === 'string' && error.match(/5\d\d$/)) {
    return setTimeout(() => {
      syncSubscription(subscription, callback)
    }, 1000 * 60)
  }
  slowCallback(callback)
}

const syncSubscription = (subscription, callback) => {
  RecurlyWrapper.getSubscription(
    subscription.recurlySubscription_id,
    (error, recurlySubscription) => {
      if (error) {
        return handleSyncSubscriptionError(subscription, error, callback)
      }

      ScriptLogger.recordMismatch(subscription, recurlySubscription)

      if (!COMMIT) {
        return callback()
      }

      SubscriptionUpdater._updateSubscriptionFromRecurly(
        recurlySubscription,
        subscription,
        {},
        error => {
          if (error) {
            return handleSyncSubscriptionError(subscription, error, callback)
          }
          slowCallback(callback)
        }
      )
    }
  )
}

const syncSubscriptions = (subscriptions, callback) => {
  async.eachLimit(subscriptions, ASYNC_LIMIT, syncSubscription, callback)
}

const loopForSubscriptions = (skip, callback) => {
  Subscription.find({
    recurlySubscription_id: { $exists: true, $ne: '' }
  })
    .sort('_id')
    .skip(skip)
    .limit(FETCH_LIMIT)
    .exec((error, subscriptions) => {
      if (error) {
        return callback(error)
      }

      if (subscriptions.length === 0) {
        console.warn('DONE')
        return callback()
      }

      syncSubscriptions(subscriptions, error => {
        if (error) {
          return callback(error)
        }
        ScriptLogger.checkedSubscriptionsCount += subscriptions.length
        retryCounter = 0
        ScriptLogger.printProgress()
        ScriptLogger.printSummary()
        loopForSubscriptions(
          MONGO_SKIP + ScriptLogger.checkedSubscriptionsCount,
          callback
        )
      })
    })
}

let retryCounter = 0
const run = () =>
  loopForSubscriptions(
    MONGO_SKIP + ScriptLogger.checkedSubscriptionsCount,
    error => {
      if (error) {
        if (retryCounter < 3) {
          console.error(error)
          retryCounter += 1
          console.warn(`RETRYING IN 60 SECONDS. (${retryCounter}/3)`)
          return setTimeout(run, 60000)
        }
        throw error
      }
      process.exit()
    }
  )

let FETCH_LIMIT, ASYNC_LIMIT, COMMIT, MONGO_SKIP
const setup = () => {
  const argv = minimist(process.argv.slice(2))
  FETCH_LIMIT = argv.fetch ? argv.fetch : 100
  ASYNC_LIMIT = argv.async ? argv.async : 10
  MONGO_SKIP = argv.skip ? argv.skip : 0
  COMMIT = argv.commit !== undefined
  if (!COMMIT) {
    console.warn('Doing dry run without --commit')
  }
  if (MONGO_SKIP) {
    console.warn(`Skipping first ${MONGO_SKIP} records`)
  }
}

setup()
run()
