defmodule Core.Posts.Post do
  @moduledoc false

  import Ecto.Changeset

  def content_changeset(%Schema.Post{} = post, attrs) do
    changeset =
      post
      |> cast(attrs, [:tid, :kind, :slug, :title, :body])
      |> validate_required([:kind], message: "must have a kind")
      |> validate_required([:body], message: "must have a body")

    changeset =
      case get_field(changeset, :kind) do
        :blog ->
          validate_required(changeset, [:title, :slug])

        :status ->
          changeset
          |> put_change(:slug, nil)
          |> put_change(:title, nil)

        _ ->
          changeset
      end

    changeset
    |> validate_format(:slug, ~r/^[a-z](?:[a-z-]*[a-z])?$/)
    |> unique_constraint([:slug])
  end

  def publish_changeset(%Schema.Post{} = post, published_at) do
    changeset = change(post)

    if is_nil(get_field(changeset, :published_at)) do
      put_change(changeset, :published_at, published_at)
    else
      changeset
    end
  end

  def delete_changeset(%Schema.Post{} = post, deleted_at) do
    changeset = change(post)

    if is_nil(get_field(changeset, :deleted_at)) do
      put_change(changeset, :deleted_at, deleted_at)
    else
      changeset
    end
  end

  def unpublish_changeset(%Schema.Post{} = post) do
    post
    |> change()
    |> put_change(:published_at, nil)
  end

  def undelete_changeset(%Schema.Post{} = post) do
    post
    |> change()
    |> put_change(:deleted_at, nil)
  end

  defmodule Query do
    @moduledoc false
    import Ecto.Query

    def base do
      from _ in Schema.Post, as: :posts
    end

    def current(query \\ base()) do
      where(query, [posts: p], is_nil(p.deleted_at))
    end

    def default_order(query \\ base()) do
      order_by(query, [posts: p], desc: :inserted_at, desc: :updated_at)
    end

    def where_publish_date_and_slug(query \\ current(), publish_date, slug) do
      where(
        query,
        [posts: p],
        p.slug == ^slug and
          fragment("(? at time zone 'UTC' at time zone 'America/New_York')::date", p.published_at) ==
            ^publish_date
      )
    end

    def published(query \\ base()) do
      query
      |> current()
      |> where([posts: p], not is_nil(p.published_at))
    end

    def deleted(query \\ base()) do
      where(query, [posts: p], not is_nil(p.deleted_at))
    end
  end
end