/*!
 * Copyright 2010 - 2017 Hitachi Vantara.  All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */
package com.pentaho.di.purge;

import static javax.ws.rs.core.MediaType.WILDCARD;

import java.io.ByteArrayOutputStream;
import java.io.UnsupportedEncodingException;
import java.util.Date;

import javax.ws.rs.Consumes;
import javax.ws.rs.DefaultValue;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;

import org.apache.logging.log4j.Level;
import org.codehaus.enunciate.jaxrs.ResponseCode;
import org.codehaus.enunciate.jaxrs.StatusCodes;
import org.pentaho.di.ui.repository.pur.services.IPurgeService;
import org.pentaho.platform.api.repository2.unified.IUnifiedRepository;
import org.pentaho.platform.util.RepositoryPathEncoder;

import com.sun.jersey.multipart.FormDataParam;

/**
 * Created by tkafalas 7/14/14.
 */
@Path( "/pur-repository-plugin/api/purge" )
public class PurgeResource {

  public static final String PATH_SEPARATOR = "/"; //$NON-NLS-1$

  IUnifiedRepository repository;

  public PurgeResource( IUnifiedRepository unifiedRepository ) {
    this.repository = unifiedRepository;
  }

  /**
   * Provides a utility for purging files and/or revision history for the DI server.
   * 
   * <p>
   * <b>Example Request:</b><br>
   * POST /pur-repository-plugin/api/purge/path:to:file/purge
   * </p>
   * 
   * @param pathId
   *          Colon separated path for the repository file. Processing of files will occur under this path. Exception:
   *          If purgeSharedObject=true other files may be affected as well.
   * @param purgeFiles
   *          If true, files will be purged completely. This options erases files and all history. This effectively
   *          disables all parameters effecting revisions since all revisions will be deleted unconditionally.
   * @param purgeRevisions
   *          If true, all revisions to the targeted files will be purged. The current state of the file will be
   *          retained.
   * @param purgeSharedObjects
   *          If true, Shared objects will also be targeted by the purge operation. This does not replace the pathId and
   *          fileFilter processing, but rather, is in addition to that processing. If it is desired to purge shared
   *          objects only without effecting other files, then set the pathId to a single space character. Some examples
   *          of shared objects database connections, Slave Servers, Cluster Schemas, and partition Schemas.
   * @param versionCount
   *          If present, the number of historical revisions to keep. If there are more revisions for a file than
   *          versionCount, the older ones will be removed.
   * @param purgeBeforeDate
   *          If set, remove all version history created prior to this date.
   * @param fileFilter
   *          The file filter to be applied when determining what files are affected by the purge. This filter is used
   *          by the <code>tree</code> endpoint to determine what files to return. The fileFilter is a list of allowed
   *          names of files separated by the pipe (|) character. Each file name in the filter may be a full name or a
   *          partial name with one or more wildcard characters ("*"). (eg: *.ktr|*.kjb returns all files with a ktr or
   *          kjb extension).
   * @param logLevelName
   *          The standard name for the log level (ie: INFO, DEBUG)
   * @return A text file containing a log of the service execution.
   */
  @POST
  @Path( "{pathId : .+}/purge" )
  @StatusCodes( { @ResponseCode( code = 200, condition = "Successfully purged specified target" ),
    @ResponseCode( code = 500, condition = "Something failed when attempting to purge " ),
    @ResponseCode( code = 404, condition = "Invalid path" ) } )
  @Consumes( MediaType.MULTIPART_FORM_DATA )
  @Produces( { WILDCARD } )
  public Response doDeleteRevisions( @PathParam( "pathId" ) String pathId,
      @DefaultValue( "false" ) @FormDataParam( "purgeFiles" ) boolean purgeFiles,
      @DefaultValue( "false" ) @FormDataParam( "purgeRevisions" ) boolean purgeRevisions,
      @DefaultValue( "false" ) @FormDataParam( "purgeSharedObjects" ) boolean purgeSharedObjects,
      @DefaultValue( "-1" ) @FormDataParam( "versionCount" ) int versionCount,
      @FormDataParam( "purgeBeforeDate" ) Date purgeBeforeDate,
      @DefaultValue( "*" ) @FormDataParam( "fileFilter" ) String fileFilter,
      @DefaultValue( "INFO" ) @FormDataParam( "logLevel" ) String logLevelName ) {

    // A version count of 0 is illegal.
    if ( versionCount == 0 ) {
      return Response.serverError().build();
    }

    if ( purgeRevisions && ( versionCount > 0 || purgeBeforeDate != null ) ) {
      purgeRevisions = false;
    }

    IPurgeService purgeService = new UnifiedRepositoryPurgeService( this.repository );
    Level logLevel = Level.toLevel( logLevelName );

    PurgeUtilitySpecification purgeSpecification = new PurgeUtilitySpecification();
    purgeSpecification.setPath( idToPath( pathId ) );
    purgeSpecification.setPurgeFiles( purgeFiles );
    purgeSpecification.setPurgeRevisions( purgeRevisions );
    purgeSpecification.setSharedObjects( purgeSharedObjects );
    purgeSpecification.setVersionCount( versionCount );
    purgeSpecification.setBeforeDate( purgeBeforeDate );
    purgeSpecification.setFileFilter( fileFilter );
    purgeSpecification.setLogLevel( logLevel );

    // Initialize the logger
    ByteArrayOutputStream purgeUtilityStream = new ByteArrayOutputStream();
    PurgeUtilityLogger.createNewInstance( purgeUtilityStream, purgeSpecification.getPath(), logLevel );

    try {
      purgeService.doDeleteRevisions( purgeSpecification );
    } catch ( Exception e ) {
      PurgeUtilityLogger.getPurgeUtilityLogger().error( e );
      return Response.ok( encodeOutput( purgeUtilityStream ), MediaType.TEXT_HTML ).build();
    }

    return Response.ok( encodeOutput( purgeUtilityStream ), MediaType.TEXT_HTML ).build();
  }

  public static String idToPath( String pathId ) {
    String path = pathId;
    path = RepositoryPathEncoder.decodeRepositoryPath( path );
    if ( path == null || path.trim().isEmpty() ) {
      path = "";
    } else {
      if ( !path.startsWith( PATH_SEPARATOR ) ) {
        path = PATH_SEPARATOR + path;
      }
    }
    return path;
  }

  private String encodeOutput( ByteArrayOutputStream purgeUtilityStream ) {
    String responseBody = null;
    try {
      responseBody = purgeUtilityStream.toString( "UTF-8" );
    } catch ( UnsupportedEncodingException e ) {
      responseBody = purgeUtilityStream.toString();
    }
    return responseBody;
  }
}
