class V1::LearningObjects::ChunksController < ApplicationController
  before_action :authorize!

  # GET /learning_objects/:learning_object_id/chunk
  def show
    # chunk exist?
    render status: File.exist?(chunk_file_path) ? 200 : 204
  end

  # POST /learning_objects/:learning_object_id/chunk
  def create
    save_file!

    if last_chunk?
      combine_file!
      attachment = post_file!
      render json: attachment, status: :ok
    else
      render status: :ok
    end
  rescue
    render status: :internal_server_error
  end

  private

  def authorize!
    @learning_object = LearningObject.find chunks_params[:id]
    authorize(@learning_object, :update?)
  end

  # Never trust parameters from the scary internet, only allow the white list through.
  def chunks_params
    params.permit(:id, :file, :_chunkNumber, :_totalChunks, :_chunkFilename, :_chunkIdentifier, :_chunkSize, :_currentChunkSize, :_totalSize)
  end

  def post_file!
    publisher = LearningObjectPublisher.new(DspaceService.create_client)
    publisher.upload @learning_object, final_file_path
  end

  ##
  # Move the temporary Sinatra upload to the chunk file location
  def save_file!
    return nil unless chunks_params[:file].try(:tempfile).is_a? Tempfile

    # Ensure required paths exist
    FileUtils.mkpath(chunk_file_directory)
    # Move the temporary file upload to the temporary chunk file path
    FileUtils.mv(chunks_params[:file].tempfile, chunk_file_path, force: true)
  end

  ##
  # Build final file
  def combine_file!
    # Ensure required paths exist
    FileUtils.mkpath(final_file_directory)
    # Open final file in append mode
    File.open(final_file_path, 'a') do |f|
      file_chunks.each do |file_chunk_path|
        # Write each chunk to the permanent file
        f.write File.read(file_chunk_path)
      end
    end
    # Cleanup chunk file directory and all chunk files
    FileUtils.rm_rf(chunk_file_directory)
  end

  def valid_mime_type?
    mime_types = @learning_object.object_type.mime_types.map(&:extension)
    return true if mime_types.empty?
    mime_types.include? chunks_params[:_chunkFilename].split('.').last
  end

  ##
  # Determine if this is the last chunk based in parts count.
  def last_chunk?
    Dir["#{chunk_file_directory}/#{chunks_params[:_chunkFilename]}.part*"].count == chunks_params[:_totalChunks].to_i
  end

  ##
  # ./tmp/file-chunks/abc-123/upload.txt.part1
  def chunk_file_path
    File.join(chunk_file_directory, "#{chunks_params[:_chunkFilename]}.part#{chunks_params[:_chunkNumber]}")
  end

  ##
  # ./tmp/file-chunks/abc-123
  def chunk_file_directory
    File.join('tmp', 'file-chunks', chunks_params[:_chunkIdentifier])
  end

  ##
  # /tmp/file-chunks/upload.txt
  def final_file_path
    File.join(final_file_directory, chunks_params[:_chunkFilename])
  end

  ##
  # /tmp/file-chunks
  def final_file_directory
    File.join('tmp', 'files', chunks_params[:_chunkIdentifier])
  end

  ##
  # Get all file chunks sorted by cardinality of their part number
  def file_chunks
    Dir["#{chunk_file_directory}/*.part*"].sort_by { |f| f.split('.part')[1].to_i }
  end
end