defmodule Mobilizon.Conversations.Conversation do
  @moduledoc """
  Represents a conversation
  """

  use Ecto.Schema

  import Ecto.Changeset

  alias Ecto.Changeset
  alias Mobilizon.Actors.Actor
  alias Mobilizon.Conversations.ConversationParticipant
  alias Mobilizon.Discussions.Comment
  alias Mobilizon.Events.Event

  @type t :: %__MODULE__{
          id: String.t(),
          origin_comment: Comment.t(),
          last_comment: Comment.t(),
          participants: list(Actor.t())
        }

  @required_attrs [:origin_comment_id, :last_comment_id]
  @optional_attrs [:event_id]
  @attrs @required_attrs ++ @optional_attrs

  schema "conversations" do
    belongs_to(:origin_comment, Comment)
    belongs_to(:last_comment, Comment)
    belongs_to(:event, Event)
    has_many(:comments, Comment)

    many_to_many(:participants, Actor,
      join_through: ConversationParticipant,
      join_keys: [conversation_id: :id, actor_id: :id],
      on_replace: :delete
    )

    timestamps(type: :utc_datetime)
  end

  @doc false
  @spec changeset(t | Ecto.Schema.t(), map) :: Ecto.Changeset.t()
  def changeset(%__MODULE__{} = conversation, attrs) do
    conversation
    |> cast(attrs, @attrs)
    |> maybe_set_participants(attrs)
    |> validate_required(@required_attrs)
  end

  defp maybe_set_participants(%Changeset{} = changeset, %{participants: participants})
       when length(participants) > 0 do
    put_assoc(changeset, :participants, participants)
  end

  defp maybe_set_participants(%Changeset{} = changeset, _), do: changeset
end