package org.thoughtcrime.securesms.jobs

import org.signal.core.util.fullWalCheckpoint
import org.signal.core.util.logging.Log
import org.thoughtcrime.securesms.database.SignalDatabase
import org.thoughtcrime.securesms.dependencies.AppDependencies
import org.thoughtcrime.securesms.jobmanager.Job
import org.thoughtcrime.securesms.keyvalue.SignalStore
import org.thoughtcrime.securesms.transport.RetryLaterException
import java.lang.Exception
import java.time.LocalDateTime
import java.time.temporal.ChronoUnit
import kotlin.time.Duration.Companion.hours
import kotlin.time.Duration.Companion.seconds

/**
 * Optimizes the message search index incrementally.
 */
class OptimizeMessageSearchIndexJob private constructor(parameters: Parameters) : BaseJob(parameters) {

  companion object {
    const val KEY = "OptimizeMessageSearchIndexJob"

    private val TAG = Log.tag(OptimizeMessageSearchIndexJob::class.java)

    @JvmStatic
    fun enqueue() {
      AppDependencies.jobManager.add(OptimizeMessageSearchIndexJob())
    }

    private fun getInitialDelay(): Long {
      val now = LocalDateTime.now()

      if (now.hour in 0..3) {
        return 0
      }

      val midnight = now.plusDays(1).truncatedTo(ChronoUnit.DAYS)
      val scheduledTime = midnight.plusMinutes((0..4.hours.inWholeMinutes).random())

      return ChronoUnit.MILLIS.between(now, scheduledTime)
    }
  }

  constructor() : this(
    Parameters.Builder()
      .setQueue("OptimizeMessageSearchIndexJob")
      .setMaxAttempts(5)
      .setMaxInstancesForQueue(2)
      .setInitialDelay(getInitialDelay())
      .build()
  )

  override fun serialize(): ByteArray? = null
  override fun getFactoryKey() = KEY
  override fun onFailure() = Unit
  override fun onShouldRetry(e: Exception) = e is RetryLaterException
  override fun getNextRunAttemptBackoff(pastAttemptCount: Int, exception: Exception): Long = 30.seconds.inWholeMilliseconds

  override fun onRun() {
    if (!SignalStore.registration.isRegistrationComplete || SignalStore.account.aci == null) {
      Log.w(TAG, "Registration not finished yet! Skipping.")
      return
    }

    val success = SignalDatabase.messageSearch.optimizeIndex(5.seconds.inWholeMilliseconds)
    if (!SignalDatabase.writableDatabase.fullWalCheckpoint()) {
      Log.w(TAG, "Failed to do a full WAL checkpoint after deletion.")
    }

    if (!success) {
      throw RetryLaterException()
    }
  }

  class Factory : Job.Factory<OptimizeMessageSearchIndexJob> {
    override fun create(parameters: Parameters, serializedData: ByteArray?) = OptimizeMessageSearchIndexJob(parameters)
  }
}
