defmodule Pinchflat.Podcasts.PodcastHelpers do
  @moduledoc """
  Methods for fetching postcast-related data from a source
  or its media items
  """

  use Pinchflat.Media.MediaQuery
  use Pinchflat.Sources.SourcesQuery

  alias Pinchflat.Repo
  alias Pinchflat.Metadata.MediaMetadata
  alias Pinchflat.Metadata.SourceMetadata

  @doc """
  Returns a list of sources that are not marked for deletion.

  Returns: [%Source{}]
  """
  def opml_sources() do
    SourcesQuery.new()
    |> select([s], %{custom_name: s.custom_name, uuid: s.uuid})
    |> where([s], is_nil(s.marked_for_deletion_at))
    |> order_by(asc: :custom_name)
    |> Repo.all()
  end

  @doc """
  Returns a list of media items that have been downloaded to disk
  and have been proven to still exist there.

  Useful for podcasts since we don't want to serve media that
  has been deleted or moved, but it's also fairly generally useful
  so I could see this being moved in the future.

  Options:
    - limit: integer - the maximum number of media items to return

  Returns: [%MediaItem{}]
  """
  def persisted_media_items_for(source, opts \\ []) do
    limit = Keyword.get(opts, :limit, 1_000)

    MediaQuery.new()
    |> where(^dynamic(^MediaQuery.for_source(source) and ^MediaQuery.downloaded()))
    |> order_by(desc: :uploaded_at)
    |> Repo.maybe_limit(limit)
    |> Repo.all()
    |> Enum.filter(fn media_item -> File.exists?(media_item.media_filepath) end)
  end

  @doc """
  Selects a cover image for a source based on the source's metadata
  and the metadata of the media items associated with the source. Also
  ensures images exist on disk.

  Only one media item should need to be returned since this is using the
  internal metadata which, so long as the media_item was _downloaded_, should
  be guaranteed to exist.

  Prefers the source's poster, then fanart, then the media item's thumbnail.

  Returns: {:ok, filepath} | {:error, :no_suitable_image}
  """
  def select_cover_image(source, media_items) do
    source_with_preloads = Repo.preload(source, :metadata)

    source_with_preloads
    |> get_images_by_preference(media_items)
    |> Enum.reject(&is_nil(&1))
    |> Enum.find(&File.exists?/1)
    |> case do
      nil -> {:error, :no_suitable_image}
      filepath -> {:ok, filepath}
    end
  end

  defp get_images_by_preference(source_with_preloads, []) do
    source_metadata = source_with_preloads.metadata || %SourceMetadata{}

    [
      source_metadata.poster_filepath,
      source_metadata.fanart_filepath
    ]
  end

  defp get_images_by_preference(source_with_preloads, [media_item | _]) do
    media_item_with_preloads = Repo.preload(media_item, :metadata)
    media_item_metadata = media_item_with_preloads.metadata || %MediaMetadata{}
    source_images = get_images_by_preference(source_with_preloads, [])

    source_images ++ [media_item_metadata.thumbnail_filepath]
  end
end
