package info.persistent.pushbot.data;

import com.google.appengine.api.datastore.Link;
import com.google.appengine.api.xmpp.JID;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;

import info.persistent.pushbot.util.Persistence;
import info.persistent.pushbot.util.Xmpp;

import java.net.MalformedURLException;
import java.net.URL;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import java.util.logging.Logger;

import javax.jdo.PersistenceManager;
import javax.jdo.Query;
import javax.jdo.annotations.Extension;
import javax.jdo.annotations.IdGeneratorStrategy;
import javax.jdo.annotations.IdentityType;
import javax.jdo.annotations.PersistenceCapable;
import javax.jdo.annotations.Persistent;
import javax.jdo.annotations.PrimaryKey;

@PersistenceCapable(
    identityType = IdentityType.APPLICATION, detachable = "true")
public class Subscription {
  private static final int MAX_SEEN_ENTRY_SIZE = 1000;
  
  private static final Logger logger =
      Logger.getLogger(Subscription.class.getName());

  @PrimaryKey
  @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
  private Long id;
  
  @Persistent
  private String user;
  
  @Persistent(defaultFetchGroup = "true")
  private Link feedUrl;
  
  @Persistent(defaultFetchGroup = "true")
  @Extension(vendorName = "datanucleus", key = "gae.unindexed", value="true")
  private Link hubUrl;
  
  @Persistent
  @Extension(vendorName = "datanucleus", key = "gae.unindexed", value="true")
  private String title;
  
  @Persistent(defaultFetchGroup = "true")
  @Extension(vendorName = "datanucleus", key = "gae.unindexed", value="true")
  private Set<String> seenEntryIds;
  
  public Subscription(JID user, URL feedUrl, URL hubUrl, String title) {
    this.user = Xmpp.toShortJid(user).getId();
    this.feedUrl = new Link(feedUrl.toString());
    this.hubUrl = new Link (hubUrl.toString());
    this.title = title;
    this.seenEntryIds = Sets.newHashSet();
  }
  
  public Long getId() {
    return id;
  }
  
  public JID getUser() {
    return new JID(user);
  }
  
  public URL getFeedUrl() {
    try {
      return new URL(feedUrl.getValue());
    } catch (MalformedURLException err) {
      // All stored URLs should be valid, since we're creating them from URL
      // instances
      throw new RuntimeException(err);
    }
  }
  
  public URL getHubUrl() {
    try {
      return new URL(hubUrl.getValue());
    } catch (MalformedURLException err) {
      // Ditto
      throw new RuntimeException(err);
    }
  }
  
  public String getTitle() {
    return title;
  }
  
  public ImmutableSet<String> getSeenEntryIds() {
    return seenEntryIds != null
        ? ImmutableSet.<String>copyOf(seenEntryIds)
        : ImmutableSet.<String>of();
  }
  
  public void addSeenEntryIds(Collection<String> entryIds) {
    if (seenEntryIds == null) {
      seenEntryIds = Sets.newHashSet();
    }
    int maxSeenEntrySize = MAX_SEEN_ENTRY_SIZE - entryIds.size();
    if (seenEntryIds.size() >= maxSeenEntrySize) {
      logger.warning("Subscription " + feedUrl.getValue() + " for " + user + 
          " had " + seenEntryIds.size() + " entries, dropping " +
          (seenEntryIds.size() - maxSeenEntrySize));
      // Ideally we'd drop the oldest entries, but we don't store timestamps...
      seenEntryIds = Sets.newHashSet(
          Lists.newArrayList(seenEntryIds).subList(0, maxSeenEntrySize));
    }
    seenEntryIds.addAll(entryIds);
  }
  
  public static List<Subscription> getSubscriptionsForUser(JID user) {
    final String queryUserId = Xmpp.toShortJid(user).getId();
    final List<Subscription> result = Lists.newArrayList();
    Persistence.withManager(new Persistence.Closure() {
      @SuppressWarnings("unchecked")
      @Override public void run(PersistenceManager manager) {
        Query query = manager.newQuery(Subscription.class);
        query.setFilter("user == userParam");
        query.declareParameters("String userParam");
        result.addAll((List<Subscription>) query.execute(queryUserId));
        query.closeAll();
      }
    });
    
    return result;
  }
  
  public static List<Subscription> getSubscriptionsForUserAndFeedUrl(
      JID user, URL feedUrl) {
    final String queryUserId = Xmpp.toShortJid(user).getId();
    final Link queryFeedUrl = new Link(feedUrl.toString());
    final List<Subscription> result = Lists.newArrayList();
    Persistence.withManager(new Persistence.Closure() {
      @SuppressWarnings("unchecked")
      @Override public void run(PersistenceManager manager) {
        Query query = manager.newQuery(Subscription.class);
        query.setFilter("user == userParam && feedUrl == feedUrlParam");
        query.declareParameters("String userParam, " +
            "com.google.appengine.api.datastore.Link feedUrlParam");
        result.addAll(
            (List<Subscription>) query.execute(queryUserId, queryFeedUrl));
        query.closeAll();
      }
    });
    
    return result;
  }  
}
