defmodule Schemas.Clip.Segment do
  @doc """
  Indivudual segment of a video.
  """
  use Ecto.Schema

  alias Db.Repo
  alias Schemas.Clip.Video

  @type ss_to :: {number(), number()}
  @type channel :: :video | :audio

  @type t :: %__MODULE__{
          type: :live | :vod,
          channel: channel(),
          ss: number(),
          to: number(),
          path: String.t(),
          seq_no: non_neg_integer(),
          video_id: Ecto.Schema.belongs_to(Video.t())
        }

  @schema_prefix "clip"
  schema "segments" do
    field(:type, Ecto.Enum, values: [:live, :vod])
    field(:channel, Ecto.Enum, values: [:video, :audio])
    field(:ss, :float)
    field(:to, :float)
    field(:path, :string)
    field(:seq_no, :integer)
    belongs_to(:video, Video)
  end

  import Ecto.Query

  @doc """
  Get all segments that intersect the interval from DB, ordered.
  """
  @spec get_segments_in_intv(term(), ss_to(), :live | :vod, [channel()]) :: %{
          channel() => [t]
        }
  def get_segments_in_intv(video_id, {ss, to}, type, channels) do
    segments =
      from(s in __MODULE__,
        where: [video_id: ^video_id, type: ^type],
        where: s.channel in ^channels,
        where: s.ss < ^to and s.to > ^ss,
        order_by: [s.channel, s.seq_no]
      )
      |> Repo.all()

    non_existing =
      for %__MODULE__{path: path} = segment <- segments,
          not File.exists?(path, [:raw]),
          into: MapSet.new() do
        Repo.delete(segment)
        segment
      end

    segments
    |> Enum.reject(&(&1 in non_existing))
    |> Enum.group_by(& &1.channel)
  end
end
