package tv.pluto.mongo.utils.populated_timelines;

import com.mongodb.BasicDBObject;
import com.mongodb.client.model.Aggregates;
import com.mongodb.client.model.Filters;
import com.mongodb.client.model.Projections;
import com.mongodb.client.model.Sorts;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import org.bson.conversions.Bson;
import org.bson.types.ObjectId;
import tv.pluto.mongo.entities.episodes.Episodes;
import tv.pluto.mongo.entities.timelines.populated_timelines.TimelineByChannelId;
import tv.pluto.mongo.utils.MongoUtils;
import tv.pluto.mongo.utils.episodes.EpisodeUtils;
import tv.pluto.mongo.utils.populated_timeline_drafts.TimelineDraftsByChannelIdUtils;

public class TimelineByChannelIdUtils extends MongoUtils {

  TimelineDraftsByChannelIdUtils timelineDraftsByChannelIdUtils =
      new TimelineDraftsByChannelIdUtils();
  EpisodeUtils episodeUtils = new EpisodeUtils();

  public TimelineByChannelIdUtils() {
    super("timelines", new TimelineByChannelId());
  }

  public List<TimelineByChannelId> getPopulatedTimelinesByChannelId(
      String channelId, long start, long stop, List<String> timelinesToExclude) {
    List<TimelineByChannelId> result = new ArrayList<>();

    Bson matchStage = this.createMatchStage(channelId, start, stop, timelinesToExclude);

    List<Bson> pipeline =
        Arrays.asList(
            matchStage,
            Aggregates.lookup(
                "episodes", // Target collection name
                "episode", // Local field to match
                "_id", // Foreign field from the "orderDetails" collection
                "episode" // Output array field name
                ),
            Aggregates.unwind("$episode"),
            Aggregates.lookup(
                "series", // Target collection name
                "episode.series", // Local field to match
                "_id", // Foreign field from the "orderDetails" collection
                "series" // Output array field name
                ),
            Aggregates.unwind("$series"),
            Aggregates.lookup(
                "clips", // Target collection name
                "episode.sources.clip", // Local field to match
                "_id", // Foreign field from the "orderDetails" collection
                "clipData" // Output array field name
                ),
            this.createProjectionStage(),
            Aggregates.sort(Sorts.ascending("start")));

    genericCollection.aggregate(pipeline).forEach(e -> result.add((TimelineByChannelId) e));

    result.forEach(
        timeline -> {
          Episodes episode = episodeUtils.getDocumentById(timeline.getEpisode().getId());
          timeline.setAllClipsReady(timelineDraftsByChannelIdUtils.allClipsReady(episode));
          timeline.setIsAvailableNow(
              timelineDraftsByChannelIdUtils.isAvailableNow(episode, new Date()));
          timeline.setHasRegionAvailability(
              timelineDraftsByChannelIdUtils.hasRegionAvailability(episode));
          timeline.setProviderIsJwOnly(
              timelineDraftsByChannelIdUtils.hasUniqueProvider(episode, "jwplatform"));
          // TODO https://plutotv.atlassian.net/browse/IP-1773
          //
          // timeline.setProviderIsJwMixed(timelineDraftsByChannelIdUtils.hasMixedProviderWith(episode, "jwplatform"));
          timeline.setHasEmptySources(
              timelineDraftsByChannelIdUtils.hasClipWithEmptySources(episode));
          //
          // timeline.setHasLinearAvailabilityWindows(timelineDraftsByChannelIdUtils.hasLinearAvailabilityWindows(episode));
          //
          // timeline.setHasLinearAvailabilityWindowForDate(timelineDraftsByChannelIdUtils.isAvailableNow(episode, new Date(timeline.getStart().getTime())));
          timeline.setHasAdPods(episode.getAdPodsDuration() > 0);
          timeline.setFulfilled(Objects.equals(episode.getAllotment(), episode.getDuration()));
          //
          // timeline.setAuthor(timelineDraftsByChannelIdUtils.calculateAuthorValue(episode));
        });

    return result;
  }

  public Bson createMatchStage(
      String channelId, long start, long stop, List<String> timelinesToExclude) {
    Date startDate = new Date(start);
    Date stopDate = new Date(stop);

    Bson matchChannel = Filters.eq("channel", new ObjectId(channelId));

    Bson matchDate = new BasicDBObject();

    if (start != 0 && stop != 0) {
      Bson condition1 = Filters.gte("start", startDate);
      Bson condition2 = Filters.lt("start", stopDate);
      Bson condition3 = Filters.eq("channel", new ObjectId(channelId));

      Bson condition4 = Filters.gt("stop", startDate);
      Bson condition5 = Filters.lte("stop", stopDate);
      Bson condition6 = Filters.eq("channel", new ObjectId(channelId));

      Bson condition7 = Filters.lte("start", startDate);
      Bson condition8 = Filters.gte("stop", stopDate);
      Bson condition9 = Filters.eq("channel", new ObjectId(channelId));

      matchDate =
          Filters.or(
              Filters.and(condition1, condition2, condition3),
              Filters.and(condition4, condition5, condition6),
              Filters.and(condition7, condition8, condition9));
    } else if (start != 0) {
      matchDate =
          Filters.and(
              Filters.eq("channel", new ObjectId(channelId)), Filters.gte("start", startDate));
    }

    Bson nin = new BasicDBObject();

    if (!timelinesToExclude.isEmpty()) {
      nin = Filters.nin("_id", transformStringsToIds(timelinesToExclude));
    }

    Bson filter = Filters.and(matchChannel, matchDate, nin);

    return Aggregates.match(filter);
  }

  public Bson createProjectionStage() {
    return Aggregates.project(
        Projections.fields(
            Projections.include("name"),
            Projections.include("start"), // Include specific fields from "orderDetails"
            Projections.include("stop"),
            Projections.include("manualOverrideMarker"),
            Projections.include("episode._id"),
            Projections.include("episode.name"),
            Projections.include("episode.allotment"),
            Projections.include("episode.duration"),
            Projections.include("episode.published"),
            Projections.include("episode.is_feed"),
            Projections.include("episode.status"),
            Projections.include("episode.flagged"),
            Projections.include("series"),
            Projections.computed("episode.series._id", "$series._id"),
            Projections.computed("episode.series.name", "$series.name"),
            Projections.include("episode.number"),
            Projections.include("episode.season"),
            Projections.include("episode.adPodsDuration"),
            Projections.include("episode.featured"),
            Projections.include("episode.firstAired"),
            Projections.include("episode.regionFilter"),
            Projections.include("episode.activeRegion"),
            Projections.include("episode.lastAired"),
            Projections.include("episode.availabilityWindows"),
            Projections.include("episode.nextTimeline")));
  }
}
