// @ts-check
import '../app/src/models/User.mjs'
import { batchedUpdateWithResultHandling } from '@overleaf/mongo-utils/batchedUpdate.js'
import { promiseMapWithLimit } from '@overleaf/promise-utils'
import Queues from '../app/src/infrastructure/Queues.mjs'
import SubscriptionLocator from '../app/src/Features/Subscription/SubscriptionLocator.mjs'
import PlansLocator from '../app/src/Features/Subscription/PlansLocator.mjs'
import FeaturesHelper from '../app/src/Features/Subscription/FeaturesHelper.mjs'
import { db } from '../app/src/infrastructure/mongodb.mjs'

const { getQueue } = Queues
const WRITE_CONCURRENCY = parseInt(process.env.WRITE_CONCURRENCY || '10', 10)

const mixpanelSinkQueue = getQueue('analytics-mixpanel-sink')

/**
 * @param {any} user
 */
async function processUser(user) {
  const analyticsId = user.analyticsId || user._id

  await _sendPropertyToQueue(analyticsId, 'user-id', user._id)
  await _sendPropertyToQueue(analyticsId, 'analytics-id', analyticsId)
  await _sendPropertyToQueue(analyticsId, 'created-at', user.signUpDate)
  if (user.alphaProgram !== undefined) {
    await _sendPropertyToQueue(analyticsId, 'alpha-program', user.alphaProgram)
  }
  if (user.betaProgram !== undefined) {
    await _sendPropertyToQueue(analyticsId, 'beta-program', user.betaProgram)
  }

  const groupSubscriptionPlanCode = await _getGroupSubscriptionPlanCode(
    user._id
  )
  if (groupSubscriptionPlanCode) {
    await _sendPropertyToQueue(
      analyticsId,
      'group-subscription-plan-code',
      groupSubscriptionPlanCode
    )
  }

  const matchedFeatureSet = FeaturesHelper.getMatchedFeatureSet(user.features)
  if (matchedFeatureSet !== 'personal') {
    await _sendPropertyToQueue(analyticsId, 'feature-set', matchedFeatureSet)
  }

  if (user.splitTests) {
    for (const splitTestName of Object.keys(user.splitTests)) {
      const assignments = user.splitTests[splitTestName]
      if (Array.isArray(assignments)) {
        for (const assignment of assignments) {
          await _sendPropertyToQueue(
            analyticsId,
            `split-test-${splitTestName}-${assignment.versionNumber}`,
            `${assignment.variantName}`
          )
        }
      }
    }
  }
}

/**
 * @param {any} userId
 */
async function _getGroupSubscriptionPlanCode(userId) {
  const subscriptions =
    await SubscriptionLocator.promises.getMemberSubscriptions(userId)
  let bestPlanCode = null
  let bestFeatures = {}
  for (const subscription of subscriptions) {
    const plan = PlansLocator.findLocalPlanInSettings(subscription.planCode)
    if (
      plan &&
      plan.features &&
      FeaturesHelper.isFeatureSetBetter(plan.features, bestFeatures)
    ) {
      bestPlanCode = plan.planCode
      bestFeatures = plan.features
    }
  }
  return bestPlanCode
}

/**
 * @param {any} analyticsId
 * @param {any} propertyName
 * @param {any} propertyValue
 * @param {any} [createdAt]
 */
async function _sendPropertyToQueue(
  analyticsId,
  propertyName,
  propertyValue,
  createdAt = new Date()
) {
  if (propertyValue == null) {
    return
  }
  await mixpanelSinkQueue.add('user-property', {
    analyticsId,
    propertyName,
    propertyValue,
    createdAt,
  })
}

/**
 * @param {any} _
 * @param {any} users
 */
async function processBatch(_, users) {
  await promiseMapWithLimit(WRITE_CONCURRENCY, users, async user => {
    await processUser(user)
  })
}

batchedUpdateWithResultHandling(
  db.users,
  {
    $nor: [
      { thirdPartyIdentifiers: { $exists: false } },
      { thirdPartyIdentifiers: { $size: 0 } },
    ],
  },
  processBatch,
  {
    _id: true,
    analyticsId: true,
    signUpDate: true,
    splitTests: true,
    alphaProgram: true,
    betaProgram: true,
  }
)
