// Copyright 2016 The Domain Registry Authors. All Rights Reserved.
//
// Licensed 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 google.registry.tools.javascrap;

import static com.google.common.collect.Maps.uniqueIndex;
import static google.registry.model.EppResourceUtils.loadByForeignKey;
import static google.registry.model.EppResourceUtils.loadDomainApplication;
import static google.registry.model.ofy.ObjectifyService.ofy;
import static org.joda.time.DateTimeZone.UTC;

import com.beust.jcommander.Parameter;
import com.beust.jcommander.Parameters;
import com.google.common.base.Function;
import google.registry.model.EppResource;
import google.registry.model.contact.ContactResource;
import google.registry.model.domain.DomainApplication;
import google.registry.model.domain.DomainResource;
import google.registry.model.host.HostResource;
import google.registry.tools.MutatingCommand;
import java.util.Arrays;
import java.util.Map;
import org.joda.time.DateTime;

/** A command to load and resave an entity, which triggers @OnSave changes. */
@Parameters(
    separators = " =",
    commandDescription = "Load and resave an object, to trigger @OnSave changes")
public final class LoadAndResaveCommand extends MutatingCommand {

  @Parameter(
    names = "--type",
    description =
        "Resource type (ContactResource, DomainApplication, DomainResource, HostResource).")
  protected String type;

  @Parameter(
    names = "--id",
    description = "Foreign key of the resource, or application ID of the domain application.")
  protected String uniqueId;

  private static final Map<String, Class<? extends EppResource>> CLASSES_BY_NAME =
      uniqueIndex(
          Arrays.<Class<? extends EppResource>>asList(
              ContactResource.class,
              DomainApplication.class,
              DomainResource.class,
              HostResource.class),
          new Function<Class<?>, String>(){
            @Override
            public String apply(Class<?> clazz) {
              return clazz.getSimpleName();
            }});

  @Override
  protected void init() throws Exception {
    Class<? extends EppResource> clazz = CLASSES_BY_NAME.get(type);
    EppResource existing =
        (clazz == DomainApplication.class)
            ? loadDomainApplication(uniqueId, DateTime.now(UTC))
            : loadByForeignKey(clazz, uniqueId, DateTime.now(UTC));
    // Find the resource by foreign key, and then reload it directly, bypassing loadByUniqueId().
    // We need to do a reload because otherwise stageEntityChange() can fail due to the implicit
    // changes done when forwarding the resource to "now" in cloneProjectedAtTime().
    EppResource resource = ofy().load().entity(existing).now();
    stageEntityChange(resource, resource);
  }
}

@@ -680,259 +680,203 @@
 tic 
-com.google.common.collect.Maps.uniqueIndex;%0Aimport static google.registry.model.EppResourceUtils.loadByForeignKey;%0Aimport static google.registry.model.EppResourceUtils.loadDomainApplication;%0Aimport static google.registry.model.ofy.ObjectifyService.ofy
+google.registry.model.index.ForeignKeyIndex.loadAndGetKey;%0Aimport static google.registry.model.ofy.ObjectifyService.ofy;%0Aimport static google.registry.util.PreconditionsUtils.checkArgumentNotNull
 ;%0Aim
@@ -1020,29 +1020,26 @@
 ogle
-.
 co
-mmon.base.Function
+de.objectify.Key
 ;%0Aim
@@ -1337,55 +1337,8 @@
 nd;%0A
-import java.util.Arrays;%0Aimport java.util.Map;%0A
 impo
@@ -1629,16 +1629,84 @@
 mand %7B%0A%0A
+  private enum ResourceType %7B CONTACT, HOST, DOMAIN, APPLICATION %7D%0A%0A
   @Param
@@ -1750,24 +1750,16 @@
 iption =
-%0A       
  %22Resour
@@ -1769,97 +1769,36 @@
 type
- (ContactResource, DomainApplication, DomainResource, HostResource).%22)%0A  protected String
+.%22)%0A  protected ResourceType
  typ
@@ -1966,626 +1966,617 @@
 %0A%0A  
-private static final Map%3CString, Class%3C? extends EppResource%3E%3E CLASSES_BY_NAME =%0A      uniqueIndex(%0A          Arrays.%3CClass%3C? extends EppResource%3E%3EasList(%0A              ContactResource.class,%0A              DomainApplication.class,%0A              DomainResource.class,%0A              HostResource.class),%0A          new Function%3CClass%3C?%3E, String%3E()%7B%0A            @Override%0A            public String apply(Class%3C?%3E clazz) %7B%0A              return clazz.getSimpleName();%0A            %7D%7D);%0A%0A  @Override%0A  protected void init() throws Exception %7B%0A    Class%3C? extends EppResource%3E clazz = CLASSES_BY_NAME.get(type);
+@Override%0A  protected void init() throws Exception %7B%0A    Key%3C? extends EppResource%3E resourceKey = checkArgumentNotNull(%0A        getResourceKey(type, uniqueId, DateTime.now(UTC)),%0A        %22Could not find active resource of type %25s: %25s%22, type, uniqueId);%0A    // Load the resource directly to bypass running cloneProjectedAtTime() automatically, which can%0A    // cause stageEntityChange() to fail due to implicit projection changes.%0A    EppResource resource = ofy().load().key(resourceKey).now();%0A    stageEntityChange(resource, resource);%0A  %7D%0A%0A  private Key%3C? extends EppResource%3E getResourceKey(
 %0A    
-Epp
+  
 Resource
  exi
@@ -2575,164 +2575,224 @@
 urce
- existing =%0A        (clazz == DomainApplication.class)%0A            ? loadDomainApplication(uniqueId, DateTime.now(UTC))%0A            : loadByForeignKey(clazz
+Type type, String uniqueId, DateTime now) %7B%0A    switch (type) %7B%0A      case CONTACT:%0A        return loadAndGetKey(ContactResource.class, uniqueId, now);%0A      case HOST:%0A        return loadAndGetKey(HostResource.class
 , un
@@ -2803,414 +2803,272 @@
 Id, 
-DateTime.now(UTC));%0A    // Find the resource by foreign key, and then reload it directly, bypassing loadByUniqueId().%0A    // We need to do a reload because otherwise stageEntityChange() can fail due to the implicit%0A    // changes done when forwarding the resource to %22now%22 in cloneProjectedAtTime().%0A    EppResource resource = ofy().load().entity(existing).now();%0A    stageEntityChange(resource, resource);
+now);%0A      case DOMAIN:%0A        return loadAndGetKey(DomainResource.class, uniqueId, now);%0A      case APPLICATION:%0A        return Key.create(DomainApplication.class, uniqueId);%0A      default:%0A        throw new IllegalStateException(%22Unknown type: %22 + type);%0A    %7D
 %0A  %7D

Clean up LoadAndResaveCommand