package oasis.jongo.applications.v2;

import javax.inject.Inject;

import org.jongo.Jongo;
import org.jongo.MongoCollection;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.base.Preconditions;
import com.google.common.base.Strings;
import com.mongodb.DuplicateKeyException;

import oasis.jongo.JongoBootstrapper;
import oasis.model.InvalidVersionException;
import oasis.model.applications.v2.Service;
import oasis.model.applications.v2.ServiceRepository;

public class JongoServiceRepository implements ServiceRepository, JongoBootstrapper {
  private static final Logger logger = LoggerFactory.getLogger(ServiceRepository.class);

  private final Jongo jongo;

  @Inject
  JongoServiceRepository(Jongo jongo) {
    this.jongo = jongo;
  }

  private MongoCollection getServicesCollection() {
    return jongo.getCollection("services");
  }

  @Override
  public Iterable<Service> getVisibleServices() {
    return getServicesCollection().find("{ visible: true }").as(Service.class);
  }

  @Override
  public Service createService(Service application) {
    JongoService jongoService = new JongoService(application);
    try {
      getServicesCollection().insert(jongoService);
    } catch (DuplicateKeyException e) {
      return null;
    }
    return jongoService;
  }

  @Override
  public Service getService(String serviceId) {
    return getServicesCollection()
        .findOne("{ id: # }", serviceId)
        .as(JongoService.class);
  }

  @Override
  public Iterable<Service> getServicesOfInstance(String instanceId) {
    return getServicesCollection()
        .find("{ instance_id: # }", instanceId)
        .as(Service.class);
  }

  @Override
  public Service getServiceByRedirectUri(String instanceId, String redirect_uri) {
    return getServicesCollection()
        .findOne("{ instance_id: #, redirect_uris: # }", instanceId, redirect_uri)
        .as(JongoService.class);
  }

  @Override
  public Service getServiceByPostLogoutRedirectUri(String instanceId, String post_logout_redirect_uri) {
    return getServicesCollection()
        .findOne("{ instance_id: #, post_logout_redirect_uris: # }", instanceId, post_logout_redirect_uri)
        .as(JongoService.class);
  }

  @Override
  public boolean deleteService(String serviceId, long[] versions) throws InvalidVersionException {
    int n = getServicesCollection()
        .remove("{ id: #, modified: { $in: # } }", serviceId, versions)
        .getN();

    if (n == 0) {
      if (getServicesCollection().count("{ id: # }", serviceId) > 0) {
        throw new InvalidVersionException("service", serviceId);
      }
      return false;
    }

    if (n > 1) {
      logger.error("Deleted {} services with ID {}, that shouldn't have happened", n, serviceId);
    }
    return true;
  }

  @Override
  public Service updateService(Service service, long[] versions) throws InvalidVersionException {
    Preconditions.checkArgument(!Strings.isNullOrEmpty(service.getId()));
    String serviceId = service.getId();
    // XXX: don't allow updating those properties (should we return an error if attempted?)
    service.setId(null);
    service.setLocal_id(null);
    service.setInstance_id(null);
    // FIXME: allow unsetting properties
    service = getServicesCollection()
        .findAndModify("{ id: #, modified: { $in: # } }", serviceId, versions)
        .with("{ $set: # }", new JongoService(service))
        .returnNew()
        .as(JongoService.class);
    if (service == null) {
      if (getServicesCollection().count("{ id: # }", serviceId) > 0) {
        throw new InvalidVersionException("service", serviceId);
      }
      return null;
    }
    return service;
  }

  @Override
  public void bootstrap() {
    getServicesCollection().ensureIndex("{ id: 1 }", "{ unique: 1 }");
    getServicesCollection().ensureIndex("{ instance_id: 1, local_id: 1 }", "{ unique: 1, sparse: 1 }");
    getServicesCollection().ensureIndex("{ instance_id: 1, redirect_uris: 1 }", "{ unique: 1, sparse: 1 }");
    getServicesCollection().ensureIndex("{ instance_id: 1, post_logout_redirect_uris: 1 }", "{ unique: 1, sparse: 1 }");
  }
}

@@ -260,16 +260,60 @@
 trings;%0A
+import com.mongodb.CommandFailureException;%0A
 import c
@@ -526,16 +526,64 @@
 ository;
+%0Aimport oasis.openidconnect.OpenIdConnectModule;
 %0A%0Apublic
@@ -785,81 +785,205 @@
 go;%0A
-%0A  @Inject%0A  JongoServiceRepository(Jongo jongo) %7B%0A    this.jongo = jongo
+  private final OpenIdConnectModule.Settings settings;%0A%0A  @Inject%0A  JongoServiceRepository(Jongo jongo, OpenIdConnectModule.Settings settings) %7B%0A    this.jongo = jongo;%0A    this.settings = settings
 ;%0A  
@@ -4110,32 +4110,223 @@
  sparse: 1 %7D%22);%0A
+%0A    if (settings.disableRedirectUriValidation) %7B%0A      dropIndex(%22%7B instance_id: 1, redirect_uris: 1 %7D%22);%0A      dropIndex(%22%7B instance_id: 1, post_logout_redirect_uris: 1 %7D%22);%0A    %7D else %7B%0A  
     getServicesC
@@ -4410,32 +4410,34 @@
  sparse: 1 %7D%22);%0A
+  
     getServicesC
@@ -4537,18 +4537,340 @@
 rse: 1 %7D%22);%0A
+    %7D%0A  %7D%0A%0A  private void dropIndex(String keys) %7B%0A    try %7B%0A      getServicesCollection().dropIndex(keys);%0A    %7D catch (CommandFailureException e) %7B%0A      // XXX: info instead of warn as the error will mainly be thrown because the index will not exists?%0A      logger.warn(%22Failure when removing index %22 + keys, e);%0A    %7D%0A
   %7D%0A%7D%0A

Fix NPE while creating a new service without redirect_uris