package com.twitter.home_mixer.util

import com.twitter.home_mixer.model.HomeFeatures.AuthorIdFeature
import com.twitter.home_mixer.model.HomeFeatures.FavoritedByUserIdsFeature
import com.twitter.home_mixer.model.HomeFeatures.HasImageFeature
import com.twitter.home_mixer.model.HomeFeatures.IsRetweetFeature
import com.twitter.home_mixer.model.HomeFeatures.MediaUnderstandingAnnotationIdsFeature
import com.twitter.home_mixer.model.HomeFeatures.RepliedByEngagerIdsFeature
import com.twitter.home_mixer.model.HomeFeatures.RetweetedByEngagerIdsFeature
import com.twitter.home_mixer.model.HomeFeatures.ScoreFeature
import com.twitter.home_mixer.model.HomeFeatures.SourceTweetIdFeature
import com.twitter.home_mixer.model.HomeFeatures.SourceUserIdFeature
import com.twitter.product_mixer.component_library.model.candidate.CursorCandidate
import com.twitter.product_mixer.component_library.model.candidate.TweetCandidate
import com.twitter.product_mixer.core.feature.featuremap.FeatureMap
import com.twitter.product_mixer.core.model.common.CandidateWithFeatures
import com.twitter.product_mixer.core.model.common.UniversalNoun
import com.twitter.product_mixer.core.model.common.presentation.CandidateWithDetails
import com.twitter.product_mixer.core.model.common.presentation.ItemCandidateWithDetails
import com.twitter.product_mixer.core.model.common.presentation.ModuleCandidateWithDetails
import com.twitter.product_mixer.core.pipeline.PipelineQuery
import com.twitter.product_mixer.core.pipeline.pipeline_failure.PipelineFailure
import com.twitter.product_mixer.core.pipeline.pipeline_failure.UnexpectedCandidateResult

import scala.reflect.ClassTag

object CandidatesUtil {
  def getItemCandidates(candidates: Seq[CandidateWithDetails]): Seq[ItemCandidateWithDetails] = {
    candidates.collect {
      case item: ItemCandidateWithDetails if !item.isCandidateType[CursorCandidate] => Seq(item)
      case module: ModuleCandidateWithDetails => module.candidates
    }.flatten
  }

  def getItemCandidatesWithOnlyModuleLast(
    candidates: Seq[CandidateWithDetails]
  ): Seq[ItemCandidateWithDetails] = {
    candidates.collect {
      case item: ItemCandidateWithDetails if !item.isCandidateType[CursorCandidate] => item
      case module: ModuleCandidateWithDetails => module.candidates.last
    }
  }

  def containsType[CandidateType <: UniversalNoun[_]](
    candidates: Seq[CandidateWithDetails]
  )(
    implicit tag: ClassTag[CandidateType]
  ): Boolean = candidates.exists {
    case ItemCandidateWithDetails(_: CandidateType, _, _) => true
    case module: ModuleCandidateWithDetails =>
      module.candidates.head.isCandidateType[CandidateType]()
    case _ => false
  }

  def getOriginalAuthorId(candidateFeatures: FeatureMap): Option[Long] =
    if (candidateFeatures.getOrElse(IsRetweetFeature, false))
      candidateFeatures.getOrElse(SourceUserIdFeature, None)
    else candidateFeatures.getOrElse(AuthorIdFeature, None)

  def getEngagerUserIds(
    candidateFeatures: FeatureMap
  ): Seq[Long] = {
    candidateFeatures.getOrElse(FavoritedByUserIdsFeature, Seq.empty) ++
      candidateFeatures.getOrElse(RetweetedByEngagerIdsFeature, Seq.empty) ++
      candidateFeatures.getOrElse(RepliedByEngagerIdsFeature, Seq.empty)
  }

  def getMediaUnderstandingAnnotationIds(
    candidateFeatures: FeatureMap
  ): Seq[Long] = {
    if (candidateFeatures.get(HasImageFeature))
      candidateFeatures.getOrElse(MediaUnderstandingAnnotationIdsFeature, Seq.empty)
    else Seq.empty
  }

  def getTweetIdAndSourceId(candidate: CandidateWithFeatures[TweetCandidate]): Seq[Long] =
    Seq(candidate.candidate.id) ++ candidate.features.getOrElse(SourceTweetIdFeature, None)

  def isAuthoredByViewer(query: PipelineQuery, candidateFeatures: FeatureMap): Boolean =
    candidateFeatures.getOrElse(AuthorIdFeature, None).contains(query.getRequiredUserId) ||
      (candidateFeatures.getOrElse(IsRetweetFeature, false) &&
        candidateFeatures.getOrElse(SourceUserIdFeature, None).contains(query.getRequiredUserId))

  val reverseChronTweetsOrdering: Ordering[CandidateWithDetails] =
    Ordering.by[CandidateWithDetails, Long] {
      case ItemCandidateWithDetails(candidate: TweetCandidate, _, _) => -candidate.id
      case ModuleCandidateWithDetails(candidates, _, _) if candidates.nonEmpty =>
        -candidates.last.candidateIdLong
      case _ => throw PipelineFailure(UnexpectedCandidateResult, "Invalid candidate type")
    }

  val scoreOrdering: Ordering[CandidateWithDetails] = Ordering.by[CandidateWithDetails, Double] {
    case ItemCandidateWithDetails(_, _, features) =>
      -features.getOrElse(ScoreFeature, None).getOrElse(0.0)
    case ModuleCandidateWithDetails(candidates, _, _) =>
      -candidates.last.features.getOrElse(ScoreFeature, None).getOrElse(0.0)
    case _ => throw PipelineFailure(UnexpectedCandidateResult, "Invalid candidate type")
  }

  val conversationModuleTweetsOrdering: Ordering[CandidateWithDetails] =
    Ordering.by[CandidateWithDetails, Long] {
      case ItemCandidateWithDetails(candidate: TweetCandidate, _, _) => candidate.id
      case _ => throw PipelineFailure(UnexpectedCandidateResult, "Only Item candidate expected")
    }
}
