package com.faforever.api.map;

import com.faforever.api.config.FafApiProperties;
import com.faforever.api.data.domain.Player;
import com.faforever.api.error.ApiException;
import com.faforever.api.error.Error;
import com.faforever.api.error.ErrorCode;
import com.faforever.api.player.PlayerService;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.io.Files;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiResponse;
import io.swagger.annotations.ApiResponses;
import org.springframework.http.MediaType;
import org.springframework.security.core.Authentication;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import javax.inject.Inject;
import javax.validation.ValidationException;
import java.io.IOException;
import java.util.Arrays;

@RestController
@RequestMapping(path = "/maps")
public class MapsController {
  private final MapService mapService;
  private final FafApiProperties fafApiProperties;
  private final ObjectMapper objectMapper;
  private final PlayerService playerService;

  @Inject
  public MapsController(MapService mapService, FafApiProperties fafApiProperties, ObjectMapper objectMapper, PlayerService playerService) {
    this.mapService = mapService;
    this.fafApiProperties = fafApiProperties;
    this.objectMapper = objectMapper;
    this.playerService = playerService;
  }

  @ApiOperation("Uploads a map")
  @ApiResponses(value = {
      @ApiResponse(code = 200, message = "Success", response = Void.class),
      @ApiResponse(code = 401, message = "Unauthorized"),
      @ApiResponse(code = 500, message = "Failure")})
  @RequestMapping(path = "/upload", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
  public void uploadMap(@RequestParam("file") MultipartFile file,
                        @RequestParam("metadata") String jsonString,
                        Authentication authentication) throws IOException {
    Player player = playerService.getPlayer(authentication);
    if (file == null) {
      throw new ApiException(new Error(ErrorCode.UPLOAD_FILE_MISSING));
    }

    String extension = Files.getFileExtension(file.getOriginalFilename());
    if (Arrays.stream(fafApiProperties.getMap().getAllowedExtensions()).noneMatch(extension::equals)) {
      throw new ApiException(new Error(ErrorCode.UPLOAD_INVALID_FILE_EXTENSION, fafApiProperties.getMap().getAllowedExtensions()));
    }

    boolean ranked;
    try {
      JsonNode node = objectMapper.readTree(jsonString);
      ranked = node.path("is_ranked").asBoolean(false);
    } catch (IOException e) {
      throw new ApiException(new Error(ErrorCode.MAP_NO_VALID_JSON_METADATA));
    }

    mapService.uploadMap(file.getBytes(), file.getOriginalFilename(), player, ranked);
  }

  @ExceptionHandler(ValidationException.class)
  public void handleValidationException(ValidationException exception) {
    throw new ApiException(new Error(ErrorCode.VALIDATION_FAILED, exception.getMessage()));
  }
}

@@ -388,24 +388,71 @@
 jectMapper;%0A
+import com.google.common.collect.ImmutableMap;%0A
 import com.g
@@ -1193,19 +1193,70 @@
 ava.
-util.Arrays
+io.Serializable;%0Aimport java.util.Arrays;%0Aimport java.util.Map
 ;%0A%0A@
@@ -3281,20 +3281,41 @@
  public 
-void
+Map%3CString, Serializable%3E
  handleV
@@ -3374,40 +3374,28 @@
     
-throw new ApiException(new Error
+return errorResponse
 (Err
@@ -3418,16 +3418,27 @@
 N_FAILED
+.getTitle()
 , except
@@ -3454,16 +3454,283 @@
 ssage())
+;%0A  %7D%0A%0A  private Map%3CString, Serializable%3E errorResponse(String title, String message) %7B%0A    ImmutableMap%3CString, Serializable%3E error = ImmutableMap.of(%0A        %22title%22, title,%0A        %22detail%22, message);%0A    return ImmutableMap.of(%22errors%22, new ImmutableMap%5B%5D%7Berror%7D
 );%0A  %7D%0A%7D

Return JSON API valid error object