/* eslint-disable
    camelcase,
    node/handle-callback-err,
    max-len,
    no-return-assign,
    no-unused-vars,
*/
// TODO: This file was created by bulk-decaffeinate.
// Fix any style issues and re-enable lint.
/*
 * decaffeinate suggestions:
 * DS102: Remove unnecessary code created because of implicit returns
 * DS103: Rewrite code to no longer use __guard__
 * DS207: Consider shorter variations of null checks
 * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
 */
let SubscriptionController
const AuthenticationController = require('../Authentication/AuthenticationController')
const SubscriptionHandler = require('./SubscriptionHandler')
const PlansLocator = require('./PlansLocator')
const SubscriptionViewModelBuilder = require('./SubscriptionViewModelBuilder')
const LimitationsManager = require('./LimitationsManager')
const RecurlyWrapper = require('./RecurlyWrapper')
const Settings = require('settings-sharelatex')
const logger = require('logger-sharelatex')
const GeoIpLookup = require('../../infrastructure/GeoIpLookup')
const UserGetter = require('../User/UserGetter')
const FeaturesUpdater = require('./FeaturesUpdater')
const planFeatures = require('./planFeatures')
const GroupPlansData = require('./GroupPlansData')
const V1SubscriptionManager = require('./V1SubscriptionManager')
const Errors = require('../Errors/Errors')
const HttpErrorHandler = require('../Errors/HttpErrorHandler')
const SubscriptionErrors = require('./Errors')
const OError = require('@overleaf/o-error')

module.exports = SubscriptionController = {
  plansPage(req, res, next) {
    const plans = SubscriptionViewModelBuilder.buildPlansList()
    let currentUser = null

    return GeoIpLookup.getCurrencyCode(
      (req.query != null ? req.query.ip : undefined) || req.ip,
      function (err, recomendedCurrency) {
        if (err != null) {
          return next(err)
        }
        const render = () =>
          res.render('subscriptions/plans', {
            title: 'plans_and_pricing',
            plans,
            gaExperiments: Settings.gaExperiments.plansPage,
            gaOptimize: true,
            recomendedCurrency,
            planFeatures,
            groupPlans: GroupPlansData
          })
        const user_id = AuthenticationController.getLoggedInUserId(req)
        if (user_id != null) {
          return UserGetter.getUser(
            user_id,
            { signUpDate: 1 },
            function (err, user) {
              if (err != null) {
                return next(err)
              }
              currentUser = user
              return render()
            }
          )
        } else {
          return render()
        }
      }
    )
  },

  // get to show the recurly.js page
  paymentPage(req, res, next) {
    const user = AuthenticationController.getSessionUser(req)
    const plan = PlansLocator.findLocalPlanInSettings(req.query.planCode)
    if (!plan) {
      return HttpErrorHandler.unprocessableEntity(req, res, 'Plan not found')
    }
    return LimitationsManager.userHasV1OrV2Subscription(
      user,
      function (err, hasSubscription) {
        if (err != null) {
          return next(err)
        }
        if (hasSubscription) {
          return res.redirect('/user/subscription?hasSubscription=true')
        } else {
          // LimitationsManager.userHasV2Subscription only checks Mongo. Double check with
          // Recurly as well at this point (we don't do this most places for speed).
          return SubscriptionHandler.validateNoSubscriptionInRecurly(
            user._id,
            function (error, valid) {
              if (error != null) {
                return next(error)
              }
              if (!valid) {
                res.redirect('/user/subscription?hasSubscription=true')
              } else {
                let currency =
                  req.query.currency != null
                    ? req.query.currency.toUpperCase()
                    : undefined
                return GeoIpLookup.getCurrencyCode(
                  (req.query != null ? req.query.ip : undefined) || req.ip,
                  function (err, recomendedCurrency, countryCode) {
                    if (err != null) {
                      return next(err)
                    }
                    if (recomendedCurrency != null && currency == null) {
                      currency = recomendedCurrency
                    }
                    return res.render('subscriptions/new', {
                      title: 'subscribe',
                      currency,
                      countryCode,
                      plan,
                      showStudentPlan: req.query.ssp === 'true',
                      recurlyConfig: JSON.stringify({
                        currency,
                        subdomain: Settings.apis.recurly.subdomain
                      }),
                      showCouponField: !!req.query.scf,
                      showVatField: !!req.query.svf,
                      gaOptimize: true
                    })
                  }
                )
              }
            }
          )
        }
      }
    )
  },

  userSubscriptionPage(req, res, next) {
    const user = AuthenticationController.getSessionUser(req)
    return SubscriptionViewModelBuilder.buildUsersSubscriptionViewModel(
      user,
      function (error, results) {
        if (error != null) {
          return next(error)
        }
        const {
          personalSubscription,
          memberGroupSubscriptions,
          managedGroupSubscriptions,
          confirmedMemberAffiliations,
          managedInstitutions,
          managedPublishers,
          v1SubscriptionStatus
        } = results
        return LimitationsManager.userHasV1OrV2Subscription(
          user,
          function (err, hasSubscription) {
            if (error != null) {
              return next(error)
            }
            const fromPlansPage = req.query.hasSubscription
            const plans = SubscriptionViewModelBuilder.buildPlansList()
            const data = {
              title: 'your_subscription',
              plans,
              user,
              hasSubscription,
              fromPlansPage,
              personalSubscription,
              memberGroupSubscriptions,
              managedGroupSubscriptions,
              confirmedMemberAffiliations,
              managedInstitutions,
              managedPublishers,
              v1SubscriptionStatus
            }
            return res.render('subscriptions/dashboard', data)
          }
        )
      }
    )
  },

  createSubscription(req, res, next) {
    const user = AuthenticationController.getSessionUser(req)
    const recurlyTokenIds = {
      billing: req.body.recurly_token_id,
      threeDSecureActionResult:
        req.body.recurly_three_d_secure_action_result_token_id
    }
    const { subscriptionDetails } = req.body

    return LimitationsManager.userHasV1OrV2Subscription(
      user,
      function (err, hasSubscription) {
        if (err != null) {
          return next(err)
        }
        if (hasSubscription) {
          logger.warn({ user_id: user._id }, 'user already has subscription')
          return res.sendStatus(409) // conflict
        }
        return SubscriptionHandler.createSubscription(
          user,
          subscriptionDetails,
          recurlyTokenIds,
          function (err) {
            if (!err) {
              return res.sendStatus(201)
            }

            if (
              err instanceof SubscriptionErrors.RecurlyTransactionError ||
              err instanceof Errors.InvalidError
            ) {
              logger.error({ err }, 'recurly transaction error, potential 422')
              return HttpErrorHandler.unprocessableEntity(
                req,
                res,
                err.message,
                OError.getFullInfo(err).public
              )
            }

            logger.warn(
              { err, user_id: user._id },
              'something went wrong creating subscription'
            )
            next(err)
          }
        )
      }
    )
  },

  successful_subscription(req, res, next) {
    const user = AuthenticationController.getSessionUser(req)
    return SubscriptionViewModelBuilder.buildUsersSubscriptionViewModel(
      user,
      function (error, { personalSubscription }) {
        if (error != null) {
          return next(error)
        }
        if (personalSubscription == null) {
          return res.redirect('/user/subscription/plans')
        }
        return res.render('subscriptions/successful_subscription', {
          title: 'thank_you',
          personalSubscription
        })
      }
    )
  },

  cancelSubscription(req, res, next) {
    const user = AuthenticationController.getSessionUser(req)
    logger.log({ user_id: user._id }, 'canceling subscription')
    return SubscriptionHandler.cancelSubscription(user, function (err) {
      if (err != null) {
        OError.tag(err, 'something went wrong canceling subscription', {
          user_id: user._id
        })
        return next(err)
      }
      // Note: this redirect isn't used in the main flow as the redirection is
      // handled by Angular
      return res.redirect('/user/subscription/canceled')
    })
  },

  canceledSubscription(req, res, next) {
    const user = AuthenticationController.getSessionUser(req)
    return res.render('subscriptions/canceled_subscription', {
      title: 'subscription_canceled'
    })
  },

  cancelV1Subscription(req, res, next) {
    const user_id = AuthenticationController.getLoggedInUserId(req)
    logger.log({ user_id }, 'canceling v1 subscription')
    return V1SubscriptionManager.cancelV1Subscription(user_id, function (err) {
      if (err != null) {
        OError.tag(err, 'something went wrong canceling v1 subscription', {
          user_id
        })
        return next(err)
      }
      return res.redirect('/user/subscription')
    })
  },

  updateSubscription(req, res, next) {
    const _origin =
      __guard__(req != null ? req.query : undefined, x => x.origin) || null
    const user = AuthenticationController.getSessionUser(req)
    const planCode = req.body.plan_code
    if (planCode == null) {
      const err = new Error('plan_code is not defined')
      logger.warn(
        { user_id: user._id, err, planCode, origin: _origin, body: req.body },
        '[Subscription] error in updateSubscription form'
      )
      return next(err)
    }
    logger.log({ planCode, user_id: user._id }, 'updating subscription')
    return SubscriptionHandler.updateSubscription(
      user,
      planCode,
      null,
      function (err) {
        if (err != null) {
          OError.tag(err, 'something went wrong updating subscription', {
            user_id: user._id
          })
          return next(err)
        }
        return res.redirect('/user/subscription')
      }
    )
  },

  updateAccountEmailAddress(req, res, next) {
    const user = AuthenticationController.getSessionUser(req)
    RecurlyWrapper.updateAccountEmailAddress(
      user._id,
      user.email,
      function (error) {
        if (error) {
          return next(error)
        }
        res.sendStatus(200)
      }
    )
  },

  reactivateSubscription(req, res, next) {
    const user = AuthenticationController.getSessionUser(req)
    logger.log({ user_id: user._id }, 'reactivating subscription')
    return SubscriptionHandler.reactivateSubscription(user, function (err) {
      if (err != null) {
        OError.tag(err, 'something went wrong reactivating subscription', {
          user_id: user._id
        })
        return next(err)
      }
      return res.redirect('/user/subscription')
    })
  },

  recurlyCallback(req, res, next) {
    logger.log({ data: req.body }, 'received recurly callback')
    const event = Object.keys(req.body)[0]
    const eventData = req.body[event]
    if (
      [
        'new_subscription_notification',
        'updated_subscription_notification',
        'expired_subscription_notification'
      ].includes(event)
    ) {
      const recurlySubscription = eventData.subscription
      return SubscriptionHandler.syncSubscription(
        recurlySubscription,
        { ip: req.ip },
        function (err) {
          if (err != null) {
            return next(err)
          }
          return res.sendStatus(200)
        }
      )
    } else if (event === 'billing_info_updated_notification') {
      const recurlyAccountCode = eventData.account.account_code
      return SubscriptionHandler.attemptPaypalInvoiceCollection(
        recurlyAccountCode,
        function (err) {
          if (err) {
            return next(err)
          }
          return res.sendStatus(200)
        }
      )
    } else {
      return res.sendStatus(200)
    }
  },

  renderUpgradeToAnnualPlanPage(req, res, next) {
    const user = AuthenticationController.getSessionUser(req)
    return LimitationsManager.userHasV2Subscription(
      user,
      function (err, hasSubscription, subscription) {
        let planName
        if (err != null) {
          return next(err)
        }
        const planCode =
          subscription != null ? subscription.planCode.toLowerCase() : undefined
        if (
          (planCode != null ? planCode.indexOf('annual') : undefined) !== -1
        ) {
          planName = 'annual'
        } else if (
          (planCode != null ? planCode.indexOf('student') : undefined) !== -1
        ) {
          planName = 'student'
        } else if (
          (planCode != null ? planCode.indexOf('collaborator') : undefined) !==
          -1
        ) {
          planName = 'collaborator'
        }
        if (!hasSubscription) {
          return res.redirect('/user/subscription/plans')
        }
        return res.render('subscriptions/upgradeToAnnual', {
          title: 'Upgrade to annual',
          planName
        })
      }
    )
  },

  processUpgradeToAnnualPlan(req, res, next) {
    const user = AuthenticationController.getSessionUser(req)
    const { planName } = req.body
    const coupon_code = Settings.coupon_codes.upgradeToAnnualPromo[planName]
    const annualPlanName = `${planName}-annual`
    logger.log(
      { user_id: user._id, planName: annualPlanName },
      'user is upgrading to annual billing with discount'
    )
    return SubscriptionHandler.updateSubscription(
      user,
      annualPlanName,
      coupon_code,
      function (err) {
        if (err != null) {
          OError.tag(err, 'error updating subscription', {
            user_id: user._id
          })
          return next(err)
        }
        return res.sendStatus(200)
      }
    )
  },

  extendTrial(req, res, next) {
    const user = AuthenticationController.getSessionUser(req)
    return LimitationsManager.userHasV2Subscription(
      user,
      function (err, hasSubscription, subscription) {
        if (err != null) {
          return next(err)
        }
        return SubscriptionHandler.extendTrial(
          subscription,
          14,
          function (err) {
            if (err != null) {
              return res.sendStatus(500)
            } else {
              return res.sendStatus(200)
            }
          }
        )
      }
    )
  },

  recurlyNotificationParser(req, res, next) {
    let xml = ''
    req.on('data', chunk => (xml += chunk))
    return req.on('end', () =>
      RecurlyWrapper._parseXml(xml, function (error, body) {
        if (error != null) {
          return next(error)
        }
        req.body = body
        return next()
      })
    )
  },

  refreshUserFeatures(req, res, next) {
    const { user_id } = req.params
    return FeaturesUpdater.refreshFeatures(user_id, function (error) {
      if (error != null) {
        return next(error)
      }
      return res.sendStatus(200)
    })
  }
}

function __guard__(value, transform) {
  return typeof value !== 'undefined' && value !== null
    ? transform(value)
    : undefined
}
