defmodule LastFm.Track do
  @moduledoc """
  Data is not always guaranteed:

  - musicbrainz_id can be an empty string
  """

  use Ecto.Schema

  import Ecto.Changeset

  alias LastFm.{Album, Artist}

  @type t :: %__MODULE__{
          musicbrainz_id: String.t(),
          title: String.t(),
          artist: Artist.t(),
          album: Album.t(),
          cover_url: String.t(),
          scrobbled_at_uts: integer(),
          scrobbled_at_label: String.t(),
          last_fm_data: map()
        }

  @primary_key {:scrobbled_at_uts, :integer, autogenerate: false}
  schema "scrobbled_tracks" do
    field :musicbrainz_id, :string
    field :title, :string
    field :cover_url, :string
    field :scrobbled_at_label, :string

    embeds_one :artist, Artist, on_replace: :update
    embeds_one :album, Album, on_replace: :update

    field :last_fm_data, :map, default: %{}
  end

  @spec from_api_response([map()]) :: [t()]
  def from_api_response(raw_tracks) do
    raw_tracks
    |> Enum.reject(&now_playing?/1)
    |> Enum.map(fn t ->
      album = %Album{
        musicbrainz_id: t["album"]["mbid"],
        title: t["album"]["#text"]
      }

      artist = %Artist{
        musicbrainz_id: t["artist"]["mbid"],
        name: t["artist"]["#text"]
      }

      %__MODULE__{
        musicbrainz_id: t["mbid"],
        title: t["name"],
        artist: artist,
        album: album,
        cover_url: parse_cover_url(t),
        scrobbled_at_uts: parse_scrobble_at_uts(t),
        scrobbled_at_label: t["date"]["#text"],
        last_fm_data: t
      }
    end)
  end

  defp now_playing?(raw_track) do
    get_in(raw_track, ["@attr", "nowplaying"]) == "true"
  end

  defp parse_cover_url(track) do
    track["image"]
    |> Enum.find(%{"#text" => nil}, fn i -> i["size"] == "medium" end)
    |> Map.get("#text")
  end

  defp parse_scrobble_at_uts(track) do
    case Integer.parse(track["date"]["uts"]) do
      {int, ""} -> int
      _ -> nil
    end
  end

  @spec changeset(t(), map()) :: Ecto.Changeset.t()
  def changeset(track, attrs) do
    track
    |> cast(attrs, [
      :scrobbled_at_uts,
      :musicbrainz_id,
      :title,
      :cover_url,
      :scrobbled_at_label,
      :last_fm_data
    ])
    |> cast_embed(:artist)
    |> cast_embed(:album)
    |> validate_required([:scrobbled_at_uts, :title])
  end
end
