defmodule Mobilizon.Events.TagRelation do
  @moduledoc """
  Represents a tag relation.
  """

  use Ecto.Schema

  import Ecto.Changeset

  alias Mobilizon.Events.Tag

  @type t :: %__MODULE__{
          weight: integer,
          tag: Tag.t(),
          link: Tag.t()
        }

  @required_attrs [:tag_id, :link_id]
  @optional_attrs [:weight]
  @attrs @required_attrs ++ @optional_attrs

  @primary_key false
  schema "tag_relations" do
    field(:weight, :integer, default: 1)

    belongs_to(:tag, Tag, primary_key: true)
    belongs_to(:link, Tag, primary_key: true)
  end

  @doc false
  @spec changeset(t | Ecto.Schema.t(), map) :: Ecto.Changeset.t()
  def changeset(%__MODULE__{} = tag, attrs) do
    # Return if tag_id or link_id are not set because it will fail later otherwise
    with %Ecto.Changeset{errors: [], changes: changes} = changeset <-
           tag
           |> cast(attrs, @attrs)
           |> validate_required(@required_attrs) do
      changeset
      |> put_change(:tag_id, min(changes.tag_id, changes.link_id))
      |> put_change(:link_id, max(changes.tag_id, changes.link_id))
      |> unique_constraint(:tag_id, name: :tag_relations_pkey)
      |> check_constraint(:tag_id,
        name: :no_self_loops_check,
        message: "Can't add a relation on self"
      )
    end
  end
end
