/**
  * 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;
  }
  /**
   * 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();
  }