lib/chore_runner/downloads/temporary_disk_storage_service.ex

defmodule ChoreRunner.Downloads.TemporaryDiskStorageService do
  alias ChoreRunner.Downloads.StorageService
  @behaviour StorageService
  @file_dir_name "chore_runner_temporary_disk_storage_service_directory"

  @impl StorageService
  def save_file(name, opts \\ []) do
    with :ok <- ensure_dir(),
         id = ChoreRunner.gen_id(),
         file = %{
           id: id,
           name: name,
           type: MIME.from_path(name),
           path: build_write_filepath(id, name)
         },
         :ok <- write_file(file.path, opts[:path], opts[:body]) do
      {:ok, Map.put(file, :created_at, File.stat!(file.path).mtime)}
    end
  end

  @impl StorageService
  def list_files do
    case File.ls(dir()) do
      {:ok, files} ->
        {:ok, Enum.map(files, &build_file_from_saved_path/1)}

      {:error, :enoent} ->
        {:ok, []}

      error ->
        error
    end
  end

  @impl StorageService
  def delete_file(%{path: path} = _file), do: File.rm(path)

  @impl StorageService
  def file_url(%{id: id} = _file, download_plug_path: download_plug_path),
    do: Path.join(download_plug_path, id)

  # @impl StorageService
  # def extra_ui_info do
  #   Application.ensure_started(:sasl)
  #   Application.ensure_started(:os_mon)

  #   percentage_available =
  #     :disksup.get_disk_data()
  #     |> List.first()
  #     |> case do
  #       {_, _, percentage_available} ->
  #         "#{percentage_available}%"

  #       _ ->
  #         "Unknown"
  #     end

  #   %{
  #     "Available Disk Space" => percentage_available
  #   }
  # end

  defp write_file(write_path, path, nil) when not is_nil(path), do: File.cp(path, write_path)
  defp write_file(write_path, nil, body) when not is_nil(body), do: File.write(write_path, body)

  defp build_file_from_saved_path(path) do
    %{"id" => id, "name" => name} = Regex.named_captures(~r/^(?<id>[\da-z]+)_(?<name>.+)$/, path)
    path = Path.join(dir(), path)
    stat = File.stat!(path)

    %{
      id: id,
      name: name,
      type: MIME.from_path(path),
      path: path,
      created_at: stat.mtime |> NaiveDateTime.from_erl!(),
      size: stat.size,
      node: node()
    }
  end

  defp build_write_filepath(id, name), do: Path.join(dir(), "#{id}_#{name}")

  defp dir, do: Path.join(System.tmp_dir!(), @file_dir_name)

  defp ensure_dir do
    case File.mkdir(dir()) do
      :ok -> :ok
      {:error, :eexist} -> :ok
      error -> error
    end
  end
end