package com.twitter.home_mixer.functional_component.side_effect

import com.twitter.eventbus.client.EventBusPublisher
import com.twitter.home_mixer.model.request.FollowingProduct
import com.twitter.home_mixer.model.request.ForYouProduct
import com.twitter.home_mixer.model.request.HasSeenTweetIds
import com.twitter.home_mixer.service.HomeMixerAlertConfig
import com.twitter.product_mixer.core.functional_component.side_effect.PipelineResultSideEffect
import com.twitter.product_mixer.core.model.common.identifier.SideEffectIdentifier
import com.twitter.product_mixer.core.model.common.presentation.CandidateWithDetails
import com.twitter.product_mixer.core.model.marshalling.HasMarshalling
import com.twitter.product_mixer.core.pipeline.PipelineQuery
import com.twitter.stitch.Stitch
import com.twitter.timelines.impressionstore.thriftscala.Impression
import com.twitter.timelines.impressionstore.thriftscala.ImpressionList
import com.twitter.timelines.impressionstore.thriftscala.PublishedImpressionList
import com.twitter.timelines.impressionstore.thriftscala.SurfaceArea
import com.twitter.util.Time
import javax.inject.Inject
import javax.inject.Singleton

object PublishClientSentImpressionsEventBusSideEffect {
  val HomeSurfaceArea: Option[Set[SurfaceArea]] = Some(Set(SurfaceArea.HomeTimeline))
  val HomeLatestSurfaceArea: Option[Set[SurfaceArea]] = Some(Set(SurfaceArea.HomeLatestTimeline))
}

/**
 * Side effect that publishes seen tweet IDs sent from clients. The seen tweet IDs are sent to a
 * heron topology which writes to a memcache dataset.
 */
@Singleton
class PublishClientSentImpressionsEventBusSideEffect @Inject() (
  eventBusPublisher: EventBusPublisher[PublishedImpressionList])
    extends PipelineResultSideEffect[PipelineQuery with HasSeenTweetIds, HasMarshalling]
    with PipelineResultSideEffect.Conditionally[
      PipelineQuery with HasSeenTweetIds,
      HasMarshalling
    ] {
  import PublishClientSentImpressionsEventBusSideEffect._

  override val identifier: SideEffectIdentifier =
    SideEffectIdentifier("PublishClientSentImpressionsEventBus")

  override def onlyIf(
    query: PipelineQuery with HasSeenTweetIds,
    selectedCandidates: Seq[CandidateWithDetails],
    remainingCandidates: Seq[CandidateWithDetails],
    droppedCandidates: Seq[CandidateWithDetails],
    response: HasMarshalling
  ): Boolean = query.seenTweetIds.exists(_.nonEmpty)

  def buildEvents(
    query: PipelineQuery with HasSeenTweetIds,
    currentTime: Long
  ): Option[Seq[Impression]] = {
    val surfaceArea = query.product match {
      case ForYouProduct => HomeSurfaceArea
      case FollowingProduct => HomeLatestSurfaceArea
      case _ => None
    }
    query.seenTweetIds.map { seenTweetIds =>
      seenTweetIds.map { tweetId =>
        Impression(
          tweetId = tweetId,
          impressionTime = Some(currentTime),
          surfaceAreas = surfaceArea
        )
      }
    }
  }

  final override def apply(
    inputs: PipelineResultSideEffect.Inputs[PipelineQuery with HasSeenTweetIds, HasMarshalling]
  ): Stitch[Unit] = {
    val currentTime = Time.now.inMilliseconds
    val impressions = buildEvents(inputs.query, currentTime)

    Stitch.callFuture(
      eventBusPublisher.publish(
        PublishedImpressionList(
          inputs.query.getRequiredUserId,
          ImpressionList(impressions),
          currentTime
        )
      )
    )
  }

  override val alerts = Seq(
    HomeMixerAlertConfig.BusinessHours.defaultSuccessRateAlert(99.4)
  )
}
