package com.twitter.home_mixer.functional_component.feature_hydrator

import com.twitter.finagle.stats.StatsReceiver
import com.twitter.home_mixer.product.scored_tweets.param.ScoredTweetsParam
import com.twitter.ml.api.DataRecord
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.FeatureWithDefaultOnFailure
import com.twitter.product_mixer.core.feature.datarecord.DataRecordInAFeature
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.stitch.Stitch
import com.twitter.timelines.clients.strato.twistly.SimClustersRecentEngagementSimilarityClient
import com.twitter.timelines.configapi.decider.BooleanDeciderParam
import com.twitter.timelines.prediction.adapters.twistly.SimClustersRecentEngagementSimilarityFeaturesAdapter
import javax.inject.Inject
import javax.inject.Singleton

object SimClustersEngagementSimilarityFeature
    extends DataRecordInAFeature[PipelineQuery]
    with FeatureWithDefaultOnFailure[PipelineQuery, DataRecord] {
  override def defaultValue: DataRecord = new DataRecord()
}

@Singleton
class SimClustersEngagementSimilarityFeatureHydrator @Inject() (
  simClustersEngagementSimilarityClient: SimClustersRecentEngagementSimilarityClient,
  statsReceiver: StatsReceiver)
    extends BulkCandidateFeatureHydrator[PipelineQuery, TweetCandidate]
    with Conditionally[PipelineQuery] {

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

  override val features: Set[Feature[_, _]] = Set(SimClustersEngagementSimilarityFeature)

  private val scopedStatsReceiver = statsReceiver.scope(identifier.toString)

  private val hydratedCandidatesSizeStat = scopedStatsReceiver.stat("hydratedCandidatesSize")

  private val simClustersRecentEngagementSimilarityFeaturesAdapter =
    new SimClustersRecentEngagementSimilarityFeaturesAdapter

  override def onlyIf(query: PipelineQuery): Boolean = {
    val param: BooleanDeciderParam =
      ScoredTweetsParam.EnableSimClustersSimilarityFeatureHydrationDeciderParam
    query.params.apply(param)
  }

  override def apply(
    query: PipelineQuery,
    candidates: Seq[CandidateWithFeatures[TweetCandidate]]
  ): Stitch[Seq[FeatureMap]] = {
    val tweetToCandidates = candidates.map(candidate => candidate.candidate.id -> candidate).toMap
    val tweetIds = tweetToCandidates.keySet.toSeq
    val userId = query.getRequiredUserId
    val userTweetEdges = tweetIds.map(tweetId => (userId, tweetId))
    val resultFuture = simClustersEngagementSimilarityClient
      .getSimClustersRecentEngagementSimilarityScores(userTweetEdges).map {
        simClustersRecentEngagementSimilarityScoresMap =>
          hydratedCandidatesSizeStat.add(simClustersRecentEngagementSimilarityScoresMap.size)
          candidates.map { candidate =>
            val similarityFeatureOpt = simClustersRecentEngagementSimilarityScoresMap
              .get(userId -> candidate.candidate.id).flatten
            val dataRecordOpt = similarityFeatureOpt.map { similarityFeature =>
              simClustersRecentEngagementSimilarityFeaturesAdapter
                .adaptToDataRecords(similarityFeature)
                .get(0)
            }
            FeatureMapBuilder()
              .add(SimClustersEngagementSimilarityFeature, dataRecordOpt.getOrElse(new DataRecord))
              .build()
          }
      }
    Stitch.callFuture(resultFuture)
  }

}
