defmodule Pinchflat.Downloading.DownloadingHelpers do
  @moduledoc """
  Methods for helping download media

  Many of these methods are made to be kickoff or be consumed by workers.
  """

  require Logger

  use Pinchflat.Media.MediaQuery

  alias Pinchflat.Repo
  alias Pinchflat.Media
  alias Pinchflat.Tasks
  alias Pinchflat.Sources.Source
  alias Pinchflat.Media.MediaItem
  alias Pinchflat.Downloading.MediaDownloadWorker

  @doc """
  Starts tasks for downloading media for any of a sources _pending_ media items.
  Jobs are not enqueued if the source is set to not download media. This will return :ok.

  NOTE: this starts a download for each media item that is pending,
  not just the ones that were indexed in this job run. This should ensure
  that any stragglers are caught if, for some reason, they weren't enqueued
  or somehow got de-queued.

  Returns :ok
  """
  def enqueue_pending_download_tasks(source, job_opts \\ [])

  def enqueue_pending_download_tasks(%Source{download_media: true} = source, job_opts) do
    source
    |> Media.list_pending_media_items_for()
    |> Enum.each(&MediaDownloadWorker.kickoff_with_task(&1, %{}, job_opts))
  end

  def enqueue_pending_download_tasks(%Source{download_media: false}, _job_opts) do
    :ok
  end

  @doc """
  Deletes ALL pending tasks for a source's media items.

  Returns :ok
  """
  def dequeue_pending_download_tasks(%Source{} = source) do
    source
    |> Media.list_pending_media_items_for()
    |> Enum.each(&Tasks.delete_pending_tasks_for/1)
  end

  @doc """
  Takes a single media item and enqueues a download job if the media should be
  downloaded, based on the source's download settings and whether media is
  considered pending.

  Returns {:ok, %Task{}} | {:error, :should_not_download} | {:error, any()}
  """
  def kickoff_download_if_pending(%MediaItem{} = media_item, job_opts \\ []) do
    media_item = Repo.preload(media_item, :source)

    if media_item.source.download_media && Media.pending_download?(media_item) do
      Logger.info("Kicking off download for media item ##{media_item.id} (#{media_item.media_id})")

      MediaDownloadWorker.kickoff_with_task(media_item, %{}, job_opts)
    else
      {:error, :should_not_download}
    end
  end

  @doc """
  For a given source, enqueues download jobs for all media items _that have already been downloaded_.

  This is useful for when a source's download settings have changed and you want to run through all
  existing media and retry the download. For instance, if the source didn't originally download thumbnails
  and you've changed the source to download them, you can use this to download all the thumbnails for
  existing media items.

  NOTE: does not delete existing files whatsoever. Does not overwrite the existing media file if it exists
  at the location it expects. Will cause a full redownload of everything if the output template has changed

  NOTE: unrelated to the MediaQualityUpgradeWorker, which is for redownloading media items for quality upgrades
  or improved sponsorblock segments

  Returns [{:ok, %Task{}} | {:error, any()}]
  """
  def kickoff_redownload_for_existing_media(%Source{} = source) do
    MediaQuery.new()
    |> MediaQuery.require_assoc(:media_profile)
    |> where(
      ^dynamic(
        [m, s, mp],
        ^MediaQuery.for_source(source) and
          ^MediaQuery.downloaded() and
          not (^MediaQuery.download_prevented())
      )
    )
    |> Repo.all()
    |> Enum.map(&MediaDownloadWorker.kickoff_with_task/1)
  end
end
