package com.twitter.home_mixer.functional_component.feature_hydrator

import com.twitter.home_mixer.model.HomeFeatures.FavoritedByUserIdsFeature
import com.twitter.home_mixer.model.HomeFeatures.InReplyToTweetIdFeature
import com.twitter.home_mixer.model.HomeFeatures.RealGraphInNetworkScoresFeature
import com.twitter.home_mixer.model.HomeFeatures.RepliedByEngagerIdsFeature
import com.twitter.home_mixer.model.HomeFeatures.RetweetedByEngagerIdsFeature
import com.twitter.home_mixer.model.HomeFeatures.SourceTweetIdFeature
import com.twitter.home_mixer.param.HomeMixerInjectionNames.UtegSocialProofRepository
import com.twitter.product_mixer.component_library.model.candidate.TweetCandidate
import com.twitter.product_mixer.core.feature.Feature
import com.twitter.product_mixer.core.feature.featuremap.FeatureMap
import com.twitter.product_mixer.core.feature.featuremap.FeatureMapBuilder
import com.twitter.product_mixer.core.functional_component.feature_hydrator.BulkCandidateFeatureHydrator
import com.twitter.product_mixer.core.model.common.CandidateWithFeatures
import com.twitter.product_mixer.core.model.common.Conditionally
import com.twitter.product_mixer.core.model.common.identifier.FeatureHydratorIdentifier
import com.twitter.product_mixer.core.pipeline.PipelineQuery
import com.twitter.recos.recos_common.{thriftscala => rc}
import com.twitter.recos.user_tweet_entity_graph.{thriftscala => uteg}
import com.twitter.servo.keyvalue.KeyValueResult
import com.twitter.servo.repository.KeyValueRepository
import com.twitter.stitch.Stitch

import javax.inject.Inject
import javax.inject.Named
import javax.inject.Singleton

@Singleton
class UtegFeatureHydrator @Inject() (
  @Named(UtegSocialProofRepository) client: KeyValueRepository[
    (Seq[Long], (Long, Map[Long, Double])),
    Long,
    uteg.TweetRecommendation
  ]) extends BulkCandidateFeatureHydrator[PipelineQuery, TweetCandidate]
    with Conditionally[PipelineQuery] {

  override val identifier: FeatureHydratorIdentifier = FeatureHydratorIdentifier("Uteg")

  override val features: Set[Feature[_, _]] = Set(
    FavoritedByUserIdsFeature,
    RetweetedByEngagerIdsFeature,
    RepliedByEngagerIdsFeature
  )

  override def onlyIf(query: PipelineQuery): Boolean = query.features
    .exists(_.getOrElse(RealGraphInNetworkScoresFeature, Map.empty[Long, Double]).nonEmpty)

  override def apply(
    query: PipelineQuery,
    candidates: Seq[CandidateWithFeatures[TweetCandidate]]
  ): Stitch[Seq[FeatureMap]] = {
    val seedUserWeights = query.features.map(_.get(RealGraphInNetworkScoresFeature)).get

    val sourceTweetIds = candidates.flatMap(_.features.getOrElse(SourceTweetIdFeature, None))
    val inReplyToTweetIds = candidates.flatMap(_.features.getOrElse(InReplyToTweetIdFeature, None))
    val tweetIds = candidates.map(_.candidate.id)
    val tweetIdsToSend = (tweetIds ++ sourceTweetIds ++ inReplyToTweetIds).distinct

    val utegQuery = (tweetIdsToSend, (query.getRequiredUserId, seedUserWeights))

    Stitch
      .callFuture(client(utegQuery))
      .map(handleResponse(candidates, _))
  }

  private def handleResponse(
    candidates: Seq[CandidateWithFeatures[TweetCandidate]],
    results: KeyValueResult[Long, uteg.TweetRecommendation],
  ): Seq[FeatureMap] = {
    candidates.map { candidate =>
      val candidateProof = results(candidate.candidate.id).toOption.flatten
      val sourceProof = candidate.features
        .getOrElse(SourceTweetIdFeature, None).flatMap(results(_).toOption.flatten)
      val proofs = Seq(candidateProof, sourceProof).flatten.map(_.socialProofByType)

      val favoritedBy = proofs.flatMap(_.get(rc.SocialProofType.Favorite)).flatten
      val retweetedBy = proofs.flatMap(_.get(rc.SocialProofType.Retweet)).flatten
      val repliedBy = proofs.flatMap(_.get(rc.SocialProofType.Reply)).flatten

      FeatureMapBuilder()
        .add(FavoritedByUserIdsFeature, favoritedBy)
        .add(RetweetedByEngagerIdsFeature, retweetedBy)
        .add(RepliedByEngagerIdsFeature, repliedBy)
        .build()
    }
  }
}
