/**
  * Get only the active deployment for the given application on the given cloud
  *
  * @param applicationId id of the topology
  * @return the active deployment
  */
 @ApiOperation(
     value = "Get active deployment for the given application on the given cloud.",
     notes =
         "Application role required [ APPLICATION_MANAGER | APPLICATION_DEVOPS ] and Application environment role required [ DEPLOYMENT_MANAGER ]")
 @RequestMapping(
     value = "/{applicationId:.+}/environments/{applicationEnvironmentId}/active-deployment",
     method = RequestMethod.GET,
     produces = MediaType.APPLICATION_JSON_VALUE)
 @PreAuthorize("isAuthenticated()")
 public RestResponse<Deployment> getActiveDeployment(
     @PathVariable String applicationId, @PathVariable String applicationEnvironmentId) {
   Application application = applicationService.checkAndGetApplication(applicationId);
   // get the topology from the version and the cloud from the environment
   ApplicationEnvironment environment =
       applicationEnvironmentService.getEnvironmentByIdOrDefault(
           application.getId(), applicationEnvironmentId);
   // when a user is application manager, he can manipulate environment
   if (!AuthorizationUtil.hasAuthorizationForApplication(
       application, ApplicationRole.APPLICATION_MANAGER)) {
     AuthorizationUtil.checkAuthorizationForEnvironment(
         environment,
         ApplicationEnvironmentRole.DEPLOYMENT_MANAGER,
         ApplicationEnvironmentRole.APPLICATION_USER);
   }
   Deployment deployment = deploymentService.getActiveDeployment(environment.getId());
   return RestResponseBuilder.<Deployment>builder().data(deployment).build();
 }
 /**
  * Trigger un-deployment of the application for a given environment on the current configured
  * PaaS.
  *
  * @param applicationId The id of the application to undeploy.
  * @return An empty rest response.
  */
 @ApiOperation(
     value = "Un-Deploys the application on the configured PaaS.",
     notes =
         "The logged-in user must have the [ APPLICATION_MANAGER ] role for this application. Application environment role required [ DEPLOYMENT_MANAGER ]")
 @RequestMapping(
     value = "/{applicationId:.+}/environments/{applicationEnvironmentId}/deployment",
     method = RequestMethod.DELETE,
     produces = MediaType.APPLICATION_JSON_VALUE)
 @PreAuthorize("isAuthenticated()")
 @Audit
 public RestResponse<Void> undeploy(
     @PathVariable String applicationId, @PathVariable String applicationEnvironmentId) {
   ApplicationEnvironment environment =
       applicationEnvironmentService.getEnvironmentByIdOrDefault(
           applicationId, applicationEnvironmentId);
   Application application = applicationService.checkAndGetApplication(applicationId);
   if (!AuthorizationUtil.hasAuthorizationForApplication(
       application, ApplicationRole.APPLICATION_MANAGER)) {
     AuthorizationUtil.checkAuthorizationForEnvironment(
         environment, ApplicationEnvironmentRole.DEPLOYMENT_MANAGER);
   }
   try {
     undeployService.undeployEnvironment(applicationEnvironmentId);
   } catch (OrchestratorDisabledException e) {
     return RestResponseBuilder.<Void>builder()
         .error(new RestError(RestErrorCode.CLOUD_DISABLED_ERROR.getCode(), e.getMessage()))
         .build();
   }
   return RestResponseBuilder.<Void>builder().build();
 }
  /**
   * Get detailed informations for every instances of every node of the application on the PaaS.
   *
   * @param applicationId The id of the application to be deployed.
   * @return A {@link RestResponse} that contains detailed informations (See {@link
   *     InstanceInformation}) of the application on the PaaS it is deployed.
   */
  @ApiOperation(
      value =
          "Get detailed informations for every instances of every node of the application on the PaaS.",
      notes =
          "Application role required [ APPLICATION_MANAGER | APPLICATION_DEVOPS ] and Application environment role required [ APPLICATION_USER | DEPLOYMENT_MANAGER ]")
  @RequestMapping(
      value = "/{applicationId:.+}/environments/{applicationEnvironmentId}/deployment/informations",
      method = RequestMethod.GET,
      produces = MediaType.APPLICATION_JSON_VALUE)
  @PreAuthorize("isAuthenticated()")
  public DeferredResult<RestResponse<Map<String, Map<String, InstanceInformation>>>>
      getInstanceInformation(
          @PathVariable String applicationId, @PathVariable String applicationEnvironmentId) {
    Application application = applicationService.checkAndGetApplication(applicationId);
    ApplicationEnvironment environment =
        applicationEnvironmentService.getEnvironmentByIdOrDefault(
            application.getId(), applicationEnvironmentId);
    if (!AuthorizationUtil.hasAuthorizationForApplication(
        application, ApplicationRole.APPLICATION_MANAGER)) {
      AuthorizationUtil.checkAuthorizationForEnvironment(
          environment, ApplicationEnvironmentRole.values());
    }

    Deployment deployment = applicationEnvironmentService.getActiveDeployment(environment.getId());
    final DeferredResult<RestResponse<Map<String, Map<String, InstanceInformation>>>>
        instancesDeferredResult = new DeferredResult<>(5L * 60L * 1000L);
    if (deployment
        == null) { // if there is no topology associated with the version it could not have been
      // deployed.
      instancesDeferredResult.setResult(
          RestResponseBuilder.<Map<String, Map<String, InstanceInformation>>>builder().build());
    } else {
      try {
        deploymentRuntimeStateService.getInstancesInformation(
            deployment,
            new IPaaSCallback<Map<String, Map<String, InstanceInformation>>>() {
              @Override
              public void onSuccess(Map<String, Map<String, InstanceInformation>> data) {
                instancesDeferredResult.setResult(
                    RestResponseBuilder.<Map<String, Map<String, InstanceInformation>>>builder()
                        .data(data)
                        .build());
              }

              @Override
              public void onFailure(Throwable throwable) {
                instancesDeferredResult.setErrorResult(throwable);
              }
            });
      } catch (OrchestratorDisabledException e) {
        log.error("Cannot get instance informations as topology plugin cannot be found.", e);
        instancesDeferredResult.setResult(
            RestResponseBuilder.<Map<String, Map<String, InstanceInformation>>>builder().build());
      }
    }
    return instancesDeferredResult;
  }
  private ApplicationEnvironment getAppEnvironmentAndCheckAuthorization(
      String applicationId, String applicationEnvironmentId) {
    Application application = applicationService.checkAndGetApplication(applicationId);

    // get the topology from the version and the cloud from the environment
    ApplicationEnvironment environment =
        applicationEnvironmentService.getEnvironmentByIdOrDefault(
            application.getId(), applicationEnvironmentId);
    if (!AuthorizationUtil.hasAuthorizationForApplication(
        application, ApplicationRole.APPLICATION_MANAGER)) {
      AuthorizationUtil.checkAuthorizationForEnvironment(
          environment, ApplicationEnvironmentRole.DEPLOYMENT_MANAGER);
    }
    return environment;
  }
 @ApiOperation(value = "Get a location from it's id.")
 @RequestMapping(value = "/{id}", method = RequestMethod.GET)
 @PreAuthorize("isAuthenticated()")
 public RestResponse<LocationDTO> get(
     @ApiParam(value = "Id of the orchestrator for which the location is defined.") @PathVariable
         String orchestratorId,
     @ApiParam(value = "Id of the location to get", required = true) @PathVariable String id) {
   Location location = locationService.getOrFail(id);
   AuthorizationUtil.checkAuthorizationForLocation(location, DeployerRole.DEPLOYER);
   return RestResponseBuilder.<LocationDTO>builder().data(buildLocationDTO(location)).build();
 }
  @ApiOperation(
      value =
          "Get the deployment status for the environements that the current user is allowed to see for a given application.",
      notes =
          "Returns the current status of an application list from the PaaS it is deployed on for all environments.")
  @RequestMapping(
      value = "/statuses",
      method = RequestMethod.POST,
      produces = MediaType.APPLICATION_JSON_VALUE)
  @PreAuthorize("isAuthenticated()")
  public RestResponse<Map<String, Map<String, EnvironmentStatusDTO>>> getApplicationsStatuses(
      @RequestBody List<String> applicationIds) {
    Map<String, Map<String, EnvironmentStatusDTO>> statuses = Maps.newHashMap();

    for (String applicationId : applicationIds) {
      Map<String, EnvironmentStatusDTO> environmentStatuses = Maps.newHashMap();
      Application application = applicationService.checkAndGetApplication(applicationId);
      // get all environments status for the current application
      ApplicationEnvironment[] environments =
          applicationEnvironmentService.getByApplicationId(application.getId());
      for (ApplicationEnvironment env : environments) {
        if (AuthorizationUtil.hasAuthorizationForEnvironment(
            env, ApplicationEnvironmentRole.values())) {
          DeploymentStatus status = DeploymentStatus.UNKNOWN;
          try {
            status = applicationEnvironmentService.getStatus(env);
          } catch (Exception e) {
            log.debug(
                "Getting status for the environment <"
                    + env.getId()
                    + "> failed because the associated orchestrator seems disabled. Returned status is UNKNOWN.",
                e);
          }
          environmentStatuses.put(env.getId(), new EnvironmentStatusDTO(env.getName(), status));
        }
      }
      statuses.put(applicationId, environmentStatuses);
    }
    return RestResponseBuilder.<Map<String, Map<String, EnvironmentStatusDTO>>>builder()
        .data(statuses)
        .build();
  }
  /**
   * Delete a tag for the application.
   *
   * @param applicationId Id of the application for which to remove the tag.
   * @param tagId The key of the tag to remove.
   * @return An empty {@link RestResponse}.
   */
  @ApiOperation(
      value = "Delete a tag for the application.",
      notes =
          "The logged-in user must have the application manager role for this application. Application role required [ APPLICATION_MANAGER ]")
  @RequestMapping(
      value = "/{tagId}",
      method = RequestMethod.DELETE,
      produces = MediaType.APPLICATION_JSON_VALUE)
  @PreAuthorize("isAuthenticated()")
  @Audit
  public RestResponse<Void> deleteTag(
      @PathVariable String applicationId, @PathVariable String tagId) {
    Application application = applicationService.getOrFail(applicationId);
    AuthorizationUtil.checkAuthorizationForApplication(
        application, ApplicationRole.APPLICATION_MANAGER);

    tagService.removeTag(application, tagId);

    return RestResponseBuilder.<Void>builder().build();
  }
  /**
   * Update or create a tag for a given application
   *
   * @param applicationId The id of the application for which to update/create a tag.
   * @param updateApplicationTagRequest The object that contains the tag's key and value.
   * @return An empty rest response.
   */
  @ApiOperation(
      value = "Update/Create a tag for the application.",
      notes =
          "The logged-in user must have the application manager role for this application. Application role required [ APPLICATION_MANAGER ]")
  @RequestMapping(
      method = RequestMethod.POST,
      consumes = MediaType.APPLICATION_JSON_VALUE,
      produces = MediaType.APPLICATION_JSON_VALUE)
  @PreAuthorize("isAuthenticated()")
  @Audit
  public RestResponse<Void> upsertTag(
      @PathVariable String applicationId,
      @RequestBody UpdateTagRequest updateApplicationTagRequest) {
    Application application = applicationService.getOrFail(applicationId);
    AuthorizationUtil.checkAuthorizationForApplication(
        application, ApplicationRole.APPLICATION_MANAGER);

    tagService.upsertTag(
        application,
        updateApplicationTagRequest.getTagKey(),
        updateApplicationTagRequest.getTagValue());

    return RestResponseBuilder.<Void>builder().build();
  }
  /**
   * Trigger deployment of the application on the current configured PaaS.
   *
   * @param deployApplicationRequest application details for deployment (applicationId +
   *     deploymentProperties)
   * @return An empty rest response.
   */
  @ApiOperation(
      value = "Deploys the application on the configured Cloud.",
      notes =
          "Application role required [ APPLICATION_MANAGER | APPLICATION_DEVOPS ] and Application environment role required [ DEPLOYMENT_MANAGER ]")
  @RequestMapping(
      value = "/deployment",
      method = RequestMethod.POST,
      consumes = MediaType.APPLICATION_JSON_VALUE,
      produces = MediaType.APPLICATION_JSON_VALUE)
  @PreAuthorize("isAuthenticated()")
  @Audit
  public RestResponse<?> deploy(
      @Valid @RequestBody DeployApplicationRequest deployApplicationRequest) {
    String applicationId = deployApplicationRequest.getApplicationId();
    String environmentId = deployApplicationRequest.getApplicationEnvironmentId();
    Application application = applicationService.checkAndGetApplication(applicationId);
    ApplicationEnvironment environment =
        applicationEnvironmentService.getEnvironmentByIdOrDefault(applicationId, environmentId);
    if (!environment.getApplicationId().equals(applicationId)) {
      throw new NotFoundException(
          "Unable to find environment with id <"
              + environmentId
              + "> for application <"
              + applicationId
              + ">");
    }
    // Security check user must be authorized to deploy the environment (or be application manager)
    if (!AuthorizationUtil.hasAuthorizationForApplication(
        application, ApplicationRole.APPLICATION_MANAGER)) {
      AuthorizationUtil.checkAuthorizationForEnvironment(
          environment, ApplicationEnvironmentRole.DEPLOYMENT_MANAGER);
    }

    // check that the environment is not already deployed
    boolean isEnvironmentDeployed = applicationEnvironmentService.isDeployed(environment.getId());
    if (isEnvironmentDeployed) {
      throw new AlreadyExistException(
          "Environment with id <"
              + environmentId
              + "> for application <"
              + applicationId
              + "> is already deployed");
    }
    // Get the deployment configurations
    DeploymentConfiguration deploymentConfiguration =
        deploymentTopologyService.getDeploymentConfiguration(environment.getId());
    DeploymentTopology deploymentTopology = deploymentConfiguration.getDeploymentTopology();
    // Check authorization on the location
    // get the target locations of the deployment topology
    Map<String, Location> locationMap = deploymentTopologyService.getLocations(deploymentTopology);
    for (Location location : locationMap.values()) {
      AuthorizationUtil.checkAuthorizationForLocation(location, DeployerRole.DEPLOYER);
    }

    // prepare the deployment
    TopologyValidationResult validation = deployService.prepareForDeployment(deploymentTopology);

    // if not valid, then return validation errors
    if (!validation.isValid()) {
      return RestResponseBuilder.<TopologyValidationResult>builder()
          .error(
              new RestError(
                  RestErrorCode.INVALID_DEPLOYMENT_TOPOLOGY.getCode(),
                  "The deployment topology for the application <"
                      + application.getName()
                      + "> on the environment <"
                      + environment.getName()
                      + "> is not valid."))
          .data(validation)
          .build();
    }

    // process with the deployment
    deployService.deploy(deploymentTopology, application);
    // TODO OrchestratorDisabledException handling in the ExceptionHandler
    // return RestResponseBuilder.<Void> builder().error(
    // new RestError(RestErrorCode.CLOUD_DISABLED_ERROR.getCode(), "Cloud with id <" +
    // environment.getCloudId() + "> is disabled or not found"))
    // .build();

    return RestResponseBuilder.<Void>builder().build();
  }