defmodule Schemas.Clip.Video do
  @doc """
  A stream or a VOD
  """
  alias Ytex.Infodict

  alias Db.Repo
  alias Schemas.Clip.{Channel, Segment}

  use Ecto.Schema

  @type t :: %__MODULE__{
          id: term(),
          platform: [:youtube | :twitch],
          url: String.t() | nil,
          start_time: DateTime.t(),
          duration: float() | nil,
          title: String.t(),
          channel: Ecto.Schema.belongs_to(struct()),
          channel_id: term(),
          segments: Ecto.Schema.has_many(struct())
        }

  @schema_prefix "clip"
  schema "videos" do
    # unique: platform & non-null url
    field(:platform, Ecto.Enum, values: [:youtube, :twitch])
    # nullable
    field(:url, :string)
    field(:start_time, :utc_datetime)
    # nullable
    field(:duration, :float)
    field(:title, :string)
    field :status, Ecto.Enum, values: [:live, :postlive, :vod]

    belongs_to(:channel, Channel)

    has_many(:segments, Segment)
  end

  @doc """
  Canonized url of given url
  """
  @spec canon_url(String.t()) :: {:ok, String.t()} | {:error, any()}
  def canon_url(url) do
    r = ~r(^https://www\.youtube\.com/watch\?v=[a-zA-Z0-9_-]{11})

    case Regex.run(r, url) do
      nil -> {:error, :not_valid}
      [url] -> {:ok, url}
    end
  end

  import Ecto.Query

  @spec from_cnnurl(String.t()) :: t() | nil
  def from_cnnurl(url) do
    q =
      from(v in __MODULE__,
        where: v.url == ^url,
        select: v
      )

    Repo.one(q)
  end

  def upsert_from_infodict!(%Infodict{
        url: url,
        start_time: start_time,
        duration: duration,
        uploader_url: uploader_url,
        username: username,
        title: title,
        status: status
      }) do
    {:ok, url} = canon_url(url)
    platform = platform_from_cnnurl(url)

    channel = Channel.upsert_uploader_url!(uploader_url, username)

    # URL may actually change
    case Repo.one(from(v in __MODULE__, where: v.url == ^url, preload: :channel)) do
      nil -> %__MODULE__{}
      %__MODULE__{url: ^url} = chn -> chn
    end
    |> Ecto.Changeset.change(
      url: url,
      platform: platform,
      start_time: start_time,
      duration: duration && duration * 1.0,
      title: title,
      status: status
    )
    |> Ecto.Changeset.put_assoc(:channel, channel)
    |> Repo.insert_or_update!()
  end

  def platform_from_cnnurl(url) do
    cond do
      url =~ ~r(^https://www\.youtube\.com/watch\?v=[a-zA-Z0-9_-]{11}) -> :youtube
    end
  end
end
