/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

package org.apache.streams.youtube.serializer;

import org.apache.streams.exceptions.ActivitySerializerException;
import org.apache.streams.pojo.extensions.ExtensionUtil;
import org.apache.streams.pojo.json.Activity;
import org.apache.streams.pojo.json.ActivityObject;
import org.apache.streams.pojo.json.Image;
import org.apache.streams.pojo.json.Provider;

import com.google.api.services.youtube.YouTube;
import com.google.api.services.youtube.model.Channel;
import com.google.api.services.youtube.model.Thumbnail;
import com.google.api.services.youtube.model.ThumbnailDetails;
import com.google.api.services.youtube.model.Video;
import org.joda.time.DateTime;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class YoutubeActivityUtil {

  private static final Logger LOGGER = LoggerFactory.getLogger(YoutubeActivityUtil.class);

  /**
   * Given a {@link YouTube.Videos} object and an
   * {@link Activity} object, fill out the appropriate details
   *
   * @param video Video
   * @param activity Activity
   * @throws ActivitySerializerException ActivitySerializerException
   */
  public static void updateActivity(Video video, Activity activity, String channelId) throws ActivitySerializerException {
    activity.setActor(buildActor(video, video.getSnippet().getChannelId()));
    activity.setVerb("post");

    activity.setId(formatId(activity.getVerb(), Optional.ofNullable(video.getId()).orElse(null)));

    activity.setPublished(new DateTime(video.getSnippet().getPublishedAt().getValue()));
    activity.setTitle(video.getSnippet().getTitle());
    activity.setContent(video.getSnippet().getDescription());
    activity.setUrl("https://www.youtube.com/watch?v=" + video.getId());

    activity.setProvider(getProvider());

    activity.setObject(buildActivityObject(video));

    addYoutubeExtensions(activity, video);
  }


  /**
   * Given a {@link Channel} object and an
   * {@link Activity} object, fill out the appropriate details
   *
   * @param channel Channel
   * @param activity Activity
   * @throws ActivitySerializerException ActivitySerializerException
   */
  public static void updateActivity(Channel channel, Activity activity, String channelId) throws ActivitySerializerException {
    try {
      activity.setProvider(getProvider());
      activity.setVerb("post");
      activity.setActor(createActorForChannel(channel));
      Map<String, Object> extensions = new HashMap<>();
      extensions.put("youtube", channel);
      activity.setAdditionalProperty("extensions", extensions);
    } catch (Throwable throwable) {
      throw new ActivitySerializerException(throwable);
    }
  }

  /**
   * createActorForChannel.
   * @param channel Channel
   * @return $.actor
   */
  public static ActivityObject createActorForChannel(Channel channel) {
    ActivityObject actor = new ActivityObject();
    // TODO: use generic provider id concatenator
    actor.setId("id:youtube:" + channel.getId());
    actor.setSummary(channel.getSnippet().getDescription());
    actor.setDisplayName(channel.getSnippet().getTitle());
    Image image = new Image();
    image.setUrl(channel.getSnippet().getThumbnails().getHigh().getUrl());
    actor.setImage(image);
    actor.setUrl("https://youtube.com/user/" + channel.getId());
    Map<String, Object> actorExtensions = new HashMap<>();
    actorExtensions.put("followers", channel.getStatistics().getSubscriberCount());
    actorExtensions.put("posts", channel.getStatistics().getVideoCount());
    actor.setAdditionalProperty("extensions", actorExtensions);
    return actor;
  }

  /**
   * Given a video object, create the appropriate activity object with a valid image
   * (thumbnail) and video URL.
   * @param video Video
   * @return Activity Object with Video URL and a thumbnail image
   */
  private static ActivityObject buildActivityObject(Video video) {
    ActivityObject activityObject = new ActivityObject();

    ThumbnailDetails thumbnailDetails = video.getSnippet().getThumbnails();
    Thumbnail thumbnail = thumbnailDetails.getDefault();

    if (thumbnail != null) {
      Image image = new Image();
      image.setUrl(thumbnail.getUrl());
      image.setHeight(thumbnail.getHeight());
      image.setWidth(thumbnail.getWidth());

      activityObject.setImage(image);
    }

    activityObject.setUrl("https://www.youtube.com/watch?v=" + video.getId());
    activityObject.setObjectType("video");

    return activityObject;
  }

  /**
   * Add the Youtube extensions to the Activity object that we're building.
   * @param activity Activity
   * @param video Video
   */
  private static void addYoutubeExtensions(Activity activity, Video video) {
    Map<String, Object> extensions = ExtensionUtil.getInstance().ensureExtensions(activity);

    extensions.put("youtube", video);

    if (video.getStatistics() != null) {
      Map<String, Object> likes = new HashMap<>();
      likes.put("count", video.getStatistics().getCommentCount());
      extensions.put("likes", likes);
    }
  }

  /**
   * Build an {@link ActivityObject} actor given the video object
   * @param video Video
   * @param id id
   * @return Actor object
   */
  private static ActivityObject buildActor(Video video, String id) {
    ActivityObject actor = new ActivityObject();

    actor.setId("id:youtube:" + id);
    actor.setDisplayName(video.getSnippet().getChannelTitle());
    actor.setSummary(video.getSnippet().getDescription());
    actor.setAdditionalProperty("handle", video.getSnippet().getChannelTitle());

    return actor;
  }

  /**
   * Gets the common youtube {@link Provider} object
   * @return a provider object representing YouTube
   */
  public static Provider getProvider() {
    Provider provider = new Provider();
    provider.setId("id:providers:youtube");
    provider.setDisplayName("YouTube");
    return provider;
  }

  /**
   * Formats the ID to conform with the Apache Streams activity ID convention
   * @param idparts the parts of the ID to join
   * @return a valid Activity ID in format "id:youtube:part1:part2:...partN"
   */
  public static String formatId(String... idparts) {
    return String.join(":",
        Stream.concat(Arrays.stream(new String[] {"id:youtube"}), Arrays.stream(idparts)).collect(Collectors.toList()));
  }
}