package org.ambraproject.wombat.controller;

import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import org.ambraproject.wombat.config.site.RequestMappingContextDictionary;
import org.ambraproject.wombat.config.site.Site;
import org.ambraproject.wombat.config.site.SiteParam;
import org.ambraproject.wombat.config.site.url.Link;
import org.ambraproject.wombat.model.ScholarlyWorkId;
import org.ambraproject.wombat.service.ApiAddress;
import org.ambraproject.wombat.service.EntityNotFoundException;
import org.ambraproject.wombat.service.remote.ArticleApi;
import org.ambraproject.wombat.service.remote.ContentKey;
import org.ambraproject.wombat.service.remote.CorpusContentApi;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Map;
import java.util.UUID;

@Controller
public class ScholarlyWorkController extends WombatController {

  @Autowired
  private ArticleApi articleApi;
  @Autowired
  private RequestMappingContextDictionary requestMappingContextDictionary;
  @Autowired
  private CorpusContentApi corpusContentApi;

  @RequestMapping(name = "work", value = "/work")
  public String redirectToWork(HttpServletRequest request,
                               @SiteParam Site site,
                               ScholarlyWorkId workId)
      throws IOException {
    return getRedirectFor(site, workId).getRedirect(request);
  }

  Map<String, Object> getWorkMetadata(ScholarlyWorkId workId) throws IOException {
    ApiAddress address = ApiAddress.builder("works").embedDoi(workId.getDoi()).build(); // TODO: Update to service
    try {
      return articleApi.requestObject(address, Map.class);
    } catch (EntityNotFoundException e) {
      throw new NotFoundException("No work exists with ID: " + workId, e);
    }
  }

  private String getTypeOf(ScholarlyWorkId workId) throws IOException {
    return (String) getWorkMetadata(workId).get("type");
  }

  private static final ImmutableMap<String, String> REDIRECT_HANDLERS = ImmutableMap.<String, String>builder()
      .put("article", "article")
      .put("figure", "figurePage")
      .put("table", "figurePage")
      // TODO: supp info
      .build();

  private Link getRedirectFor(Site site, ScholarlyWorkId workId) throws IOException {
    String handlerName = REDIRECT_HANDLERS.get(getTypeOf(workId));
    if (handlerName == null) {
      throw new RuntimeException("Unrecognized type: " + workId);
    }
    Link.Factory.PatternBuilder handlerLink = Link.toLocalSite(site)
        .toPattern(requestMappingContextDictionary, handlerName);
    return pointLinkToWork(handlerLink, workId);
  }

  private static Link pointLinkToWork(Link.Factory.PatternBuilder link, ScholarlyWorkId workId) {
    link.addQueryParameter("id", workId.getDoi());
    workId.getRevisionNumber().ifPresent(revisionNumber ->
        link.addQueryParameter("rev", revisionNumber));
    return link.build();
  }

  @RequestMapping(name = "workFile", value = "/work", params = {"fileType"})
  public void serveWorkFile(HttpServletResponse responseToClient,
                            @SiteParam Site site,
                            ScholarlyWorkId workId,
                            @RequestParam(value = "fileType", required = true) String fileType,
                            @RequestParam(value = "download", required = false) String isDownload)
      throws IOException {
    Map<String, ?> metadata = getWorkMetadata(workId);
    Map<String, ?> files = (Map<String, ?>) metadata.get("files");
    Map<String, ?> fileRepoKey = (Map<String, ?>) files.get(fileType);
    if (fileRepoKey == null) {
      String message = String.format("Unrecognized file type (\"%s\") for workId: %s", fileType, workId);
      throw new NotFoundException(message);
    }

    ContentKey contentKey = createKey(fileRepoKey);
    try (CloseableHttpResponse responseFromApi = corpusContentApi.request(contentKey, ImmutableList.of())) {
      forwardAssetResponse(responseFromApi, responseToClient, booleanParameter(isDownload));
    }
  }

  private static ContentKey createKey(Map<String, ?> fileRepoKey) {
    String key = (String) fileRepoKey.get("key");
    UUID uuid = UUID.fromString((String) fileRepoKey.get("uuid"));
    return ContentKey.createForUuid(key, uuid);
  }

}

@@ -470,16 +470,81 @@
 ddress;%0A
+import org.ambraproject.wombat.service.ArticleResolutionService;%0A
 import o
@@ -1513,16 +1513,90 @@
 tentApi;
+%0A  @Autowired%0A  private ArticleResolutionService articleResolutionService;
 %0A%0A  @Req
@@ -3913,43 +3913,338 @@
  ?%3E 
-metadata = getWorkMetadata(workId);
+itemResponse = articleApi.requestObject(%0A        articleResolutionService.toIngestion(workId).addToken(%22items%22).build(),%0A        Map.class);%0A    Map%3CString, ?%3E items = (Map%3CString, ?%3E) itemResponse.get(%22items%22);%0A    Map%3CString, ?%3E requestedItem = (Map%3CString, ?%3E) items.get(workId.getDoi()); // TODO: Deal with ambiguous URI forms
 %0A   
@@ -4288,16 +4288,21 @@
 ?%3E) 
-metadata
+requestedItem
 .get
@@ -4900,24 +4900,61 @@
 eRepoKey) %7B%0A
+    // TODO: Account for bucket name%0A
     String k
@@ -4984,17 +4984,22 @@
 ey.get(%22
-k
+crepoK
 ey%22);%0A  
@@ -5054,17 +5054,22 @@
 ey.get(%22
-u
+crepoU
 uid%22));%0A

Reimplement ScholarlyWorkController.serveWorkFile for new API