/** This class is a REST handler for lineage master requests. */
@NotThreadSafe
@Path(LineageMasterClientRestServiceHandler.SERVICE_PREFIX)
@Produces(MediaType.APPLICATION_JSON)
public final class LineageMasterClientRestServiceHandler {
  private static final Logger LOG = LoggerFactory.getLogger(Constants.LOGGER_TYPE);

  public static final String SERVICE_PREFIX = "master/lineage";
  public static final String SERVICE_NAME = "service_name";
  public static final String SERVICE_VERSION = "service_version";
  public static final String CREATE_LINEAGE = "create_lineage";
  public static final String DELETE_LINEAGE = "delete_lineage";
  public static final String GET_LINEAGE_INFO_LIST = "lineage_info_list";
  public static final String REINITIALIZE_FILE = "reinitialize_file";
  public static final String REPORT_LOST_FILE = "report_lost_file";

  private final LineageMaster mLineageMaster = AlluxioMaster.get().getLineageMaster();

  /**
   * @summary get the service name
   * @return the response object
   */
  @GET
  @Path(SERVICE_NAME)
  @ReturnType("java.lang.String")
  public Response getServiceName() {
    return RestUtils.createResponse(Constants.LINEAGE_MASTER_CLIENT_SERVICE_NAME);
  }

  /**
   * @summary get the service version
   * @return the response object
   */
  @GET
  @Path(SERVICE_VERSION)
  @ReturnType("java.lang.Long")
  public Response getServiceVersion() {
    return RestUtils.createResponse(Constants.LINEAGE_MASTER_CLIENT_SERVICE_VERSION);
  }

  /**
   * @summary create a lineage
   * @param inputFiles a colon-separated list of the lineage input files
   * @param outputFiles a colon-separated list of the lineage output files
   * @param command the job command
   * @param outputFile the job output file
   * @return the response object
   */
  @POST
  @Path(CREATE_LINEAGE)
  @ReturnType("java.lang.Long")
  public Response createLineage(
      @QueryParam("inputFiles") String inputFiles,
      @QueryParam("outputFiles") String outputFiles,
      @QueryParam("command") String command,
      @QueryParam("commandOutputFile") String outputFile) {
    Preconditions.checkNotNull(inputFiles, "required 'inputFiles' parameter is missing");
    Preconditions.checkNotNull(outputFiles, "required 'outputFiles' parameter is missing");
    Preconditions.checkNotNull(command, "required 'command' parameter is missing");
    Preconditions.checkNotNull(outputFile, "required 'commandOutputFile' parameter is missing");
    List<AlluxioURI> inputFilesUri = new ArrayList<>();
    for (String path : inputFiles.split(":", -1)) {
      inputFilesUri.add(new AlluxioURI(path));
    }
    List<AlluxioURI> outputFilesUri = new ArrayList<>();
    for (String path : outputFiles.split(":", -1)) {
      outputFilesUri.add(new AlluxioURI(path));
    }
    CommandLineJob job = new CommandLineJob(command, new JobConf(outputFile));
    try {
      return RestUtils.createResponse(
          mLineageMaster.createLineage(inputFilesUri, outputFilesUri, job));
    } catch (AlluxioException | IOException | NullPointerException e) {
      LOG.warn(e.getMessage());
      return RestUtils.createErrorResponse(e.getMessage());
    }
  }

  /**
   * @summary delete a lineage
   * @param lineageId the lineage id
   * @param cascade whether to delete lineage recursively
   * @return the response object
   */
  @POST
  @Path(DELETE_LINEAGE)
  @ReturnType("java.lang.Boolean")
  public Response deleteLineage(
      @QueryParam("lineageId") Long lineageId, @QueryParam("cascade") boolean cascade) {
    try {
      Preconditions.checkNotNull(lineageId, "required 'lineageId' parameter is missing");
      return RestUtils.createResponse(mLineageMaster.deleteLineage(lineageId, cascade));
    } catch (AlluxioException | NullPointerException e) {
      LOG.warn(e.getMessage());
      return RestUtils.createErrorResponse(e.getMessage());
    }
  }

  /**
   * @summary get the list of lineage descriptors
   * @return the response object
   */
  @GET
  @Path(GET_LINEAGE_INFO_LIST)
  @ReturnType("java.util.List<alluxio.wire.LineageInfo>")
  public Response getLineageInfoList() {
    try {
      return RestUtils.createResponse(mLineageMaster.getLineageInfoList());
    } catch (AlluxioException e) {
      LOG.warn(e.getMessage());
      return RestUtils.createErrorResponse(e.getMessage());
    }
  }

  /**
   * @summary reinitialize a file
   * @param path the file path
   * @param blockSizeBytes the file block size (in bytes)
   * @param ttl the file time-to-live (in seconds)
   * @return the response object
   */
  @POST
  @Path(REINITIALIZE_FILE)
  @ReturnType("java.lang.Long")
  public Response reinitializeFile(
      @QueryParam("path") String path,
      @QueryParam("blockSizeBytes") Long blockSizeBytes,
      @QueryParam("ttl") Long ttl) {
    try {
      Preconditions.checkNotNull(path, "required 'path' parameter is missing");
      Preconditions.checkNotNull(blockSizeBytes, "required 'blockSizeBytes' parameter is missing");
      Preconditions.checkNotNull(ttl, "required 'ttl' parameter is missing");
      return RestUtils.createResponse(mLineageMaster.reinitializeFile(path, blockSizeBytes, ttl));
    } catch (AlluxioException | NullPointerException e) {
      LOG.warn(e.getMessage());
      return RestUtils.createErrorResponse(e.getMessage());
    }
  }

  /**
   * @summary report a lost file
   * @param path the file path
   * @return the response object
   */
  @POST
  @Path(REPORT_LOST_FILE)
  @ReturnType("java.lang.Void")
  public Response reportLostFile(@QueryParam("path") String path) {
    try {
      Preconditions.checkNotNull(path, "required 'path' parameter is missing");
      mLineageMaster.reportLostFile(path);
      return RestUtils.createResponse();
    } catch (AlluxioException | NullPointerException e) {
      LOG.warn(e.getMessage());
      return RestUtils.createErrorResponse(e.getMessage());
    }
  }
}