package org.ambraproject.rhino.rest.controller;

import com.google.gson.Gson;
import org.ambraproject.rhino.identity.CommentIdentifier;
import org.ambraproject.rhino.identity.Doi;
import org.ambraproject.rhino.identity.IssueIdentifier;
import org.ambraproject.rhino.identity.VolumeIdentifier;
import org.ambraproject.rhino.rest.DoiEscaping;
import org.ambraproject.rhino.rest.RestClientException;
import org.ambraproject.rhino.service.ArticleCrudService;
import org.ambraproject.rhino.service.CommentCrudService;
import org.ambraproject.rhino.service.IssueCrudService;
import org.ambraproject.rhino.service.VolumeCrudService;
import org.ambraproject.rhino.util.response.Transceiver;
import org.ambraproject.rhino.view.ResolvedDoiView;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Controller;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Calendar;
import java.util.EnumSet;
import java.util.Optional;

@Controller
public class ArticleItemReadController extends RestController {

  @Autowired
  private ArticleCrudService articleCrudService;
  @Autowired
  private VolumeCrudService volumeCrudService;
  @Autowired
  private IssueCrudService issueCrudService;
  @Autowired
  private CommentCrudService commentCrudService;
  @Autowired
  private Gson entityGson;

  @Transactional(readOnly = true)
  @RequestMapping(value = "/dois/{doi:.+}", method = RequestMethod.GET)
  public void resolve(HttpServletRequest request, HttpServletResponse response,
                      @PathVariable("doi") String escapedDoi)
      throws IOException {
    Doi doi = DoiEscaping.unescape(escapedDoi);
    asTransceiver(findDoiTarget(doi)).respond(request, response, entityGson);
  }

  private Transceiver asTransceiver(ResolvedDoiView view) {
    return new Transceiver() {
      @Override
      protected Object getData() throws IOException {
        return view;
      }

      @Override
      protected Calendar getLastModifiedDate() throws IOException {
        return null;
      }
    };
  }

  private ResolvedDoiView findDoiTarget(Doi doi) throws IOException {
    // Look for the DOI in the corpus of articles
    Optional<ResolvedDoiView> itemOverview = articleCrudService.getItemOverview(doi);
    if (itemOverview.isPresent()) {
      return itemOverview.get();
    }

    // Not found as an Article or ArticleItem. Check other object types that use DOIs.
    return EnumSet.allOf(DoiLookup.class).stream()
        .filter(lookup -> lookup.exists(this, doi))
        .findAny()
        .map(matchedLookup -> ResolvedDoiView.create(doi, matchedLookup.getType()))
        .orElseThrow(() -> new RestClientException("DOI not found: " + doi.getName(), HttpStatus.NOT_FOUND));
  }

  private static enum DoiLookup {
    VOLUME(ResolvedDoiView.DoiWorkType.VOLUME) {
      @Override
      public boolean exists(ArticleItemReadController controller, Doi doi) {
        return controller.volumeCrudService.getVolume(VolumeIdentifier.create(doi)).isPresent();
      }
    },
    ISSUE(ResolvedDoiView.DoiWorkType.ISSUE) {
      @Override
      public boolean exists(ArticleItemReadController controller, Doi doi) {
        return controller.issueCrudService.getIssue(IssueIdentifier.create(doi)).isPresent();
      }
    },
    COMMENT(ResolvedDoiView.DoiWorkType.COMMENT) {
      @Override
      public boolean exists(ArticleItemReadController controller, Doi doi) {
        return controller.commentCrudService.getComment(CommentIdentifier.create(doi)).isPresent();
      }
    };

    private final ResolvedDoiView.DoiWorkType type;

    private DoiLookup(ResolvedDoiView.DoiWorkType type) {
      this.type = type;
    }

    public ResolvedDoiView.DoiWorkType getType() {
      return type;
    }

    public abstract boolean exists(ArticleItemReadController controller, Doi doi);
  }

}

@@ -1289,34 +1289,8 @@
 ar;%0A
-import java.util.EnumSet;%0A
 impo
@@ -2772,171 +2772,59 @@
     
-return EnumSet.allOf(DoiLookup.class).stream()%0A        .filter(lookup -%3E lookup.exists(this, doi))%0A        .findAny()%0A        .map(matchedLookup -%3E ResolvedDoiView
+if (commentCrudService.getComment(CommentIdentifier
 .cre
@@ -2834,358 +2834,124 @@
 (doi
-, matchedLookup.getType()))
+)).isPresent()) %7B
 %0A      
-  .orElseThrow(() -%3E new RestClientException(%22DOI not found: %22 + doi.getName(), HttpStatus.NOT_FOUND));%0A  %7D%0A%0A  private static enum DoiLookup %7B%0A    VOLUME(ResolvedDoiView.DoiWorkType.VOLUME) %7B%0A      @Override%0A      public boolean exists(ArticleItemReadController controller, Doi doi) %7B%0A        return controller.volum
+return ResolvedDoiView.create(doi, ResolvedDoiView.DoiWorkType.COMMENT);%0A    %7D else if (issu
 eCru
@@ -2966,20 +2966,18 @@
 .get
-Volume(Volum
+Issue(Issu
 eIde
@@ -3012,232 +3012,40 @@
 nt()
-;%0A      %7D%0A    %7D,%0A    ISSUE(ResolvedDoiView.DoiWorkType.ISSUE) %7B%0A      @Override%0A      public boolean exists(ArticleItemReadController controller, Doi doi) %7B%0A        return controller.issueCrudService.getIssue(IssueIdentifier
+) %7B%0A      return ResolvedDoiView
 .cre
@@ -3055,600 +3055,324 @@
 (doi
-)).isPresent();%0A      %7D%0A    %7D,%0A    COMMENT(ResolvedDoiView.DoiWorkType.COMMENT) %7B%0A      @Override%0A      public boolean exists(ArticleItemReadController controller, Doi doi) %7B%0A        return controller.commentCrudService.getComment(CommentIdentifier.create(doi)).isPresent();%0A      %7D%0A    %7D;%0A%0A    private final ResolvedDoiView.DoiWorkType type;%0A%0A    private DoiLookup(ResolvedDoiView.DoiWorkType type) %7B%0A      this.type = type;%0A    %7D%0A%0A    public ResolvedDoiView.DoiWorkType getType() %7B%0A      return type;%0A    %7D%0A%0A    public abstract boolean exists(ArticleItemReadController controller, Doi doi);
+, ResolvedDoiView.DoiWorkType.ISSUE);%0A    %7D else if (volumeCrudService.getVolume(VolumeIdentifier.create(doi)).isPresent()) %7B%0A      return ResolvedDoiView.create(doi, ResolvedDoiView.DoiWorkType.VOLUME);%0A    %7D else %7B%0A      throw new RestClientException(%22DOI not found: %22 + doi.getName(), HttpStatus.NOT_FOUND);%0A    %7D
 %0A  %7D

Refactor ArticleItemReadController.findDoiTarget for simplicity