defmodule Berrypod.Pages.Page do
  use Ecto.Schema
  import Ecto.Changeset

  @primary_key {:id, :binary_id, autogenerate: true}
  @foreign_key_type :binary_id

  @system_slugs ~w(
    home about delivery privacy terms contact
    collection pdp cart search
    checkout_success orders order_detail error
  )

  @reserved_paths ~w(
    about delivery privacy terms contact
    collections products cart search checkout orders
    coming-soon admin health image_cache images favicon
    sitemap.xml robots.txt setup dev
  )

  schema "pages" do
    field :slug, :string
    field :title, :string
    field :blocks, {:array, :map}, default: []
    field :type, :string, default: "system"
    field :published, :boolean, default: true
    field :meta_description, :string
    field :show_in_nav, :boolean, default: false
    field :nav_label, :string
    field :nav_position, :integer

    timestamps(type: :utc_datetime)
  end

  def system_changeset(page, attrs) do
    page
    |> cast(attrs, [:slug, :title, :blocks])
    |> validate_required([:slug, :title, :blocks])
    |> validate_inclusion(:slug, @system_slugs)
    |> unique_constraint(:slug)
  end

  def custom_changeset(page, attrs) do
    page
    |> cast(attrs, [
      :slug,
      :title,
      :blocks,
      :type,
      :published,
      :meta_description,
      :show_in_nav,
      :nav_label,
      :nav_position
    ])
    |> validate_required([:slug, :title])
    |> validate_format(:slug, ~r/^[a-z0-9]([a-z0-9-]*[a-z0-9])?$/,
      message: "must be lowercase letters, numbers, and hyphens only"
    )
    |> validate_exclusion(:slug, @system_slugs ++ @reserved_paths, message: "is reserved")
    |> validate_length(:slug, max: 100)
    |> validate_length(:title, max: 200)
    |> validate_length(:meta_description, max: 300)
    |> put_defaults()
    |> unique_constraint(:slug)
  end

  defp put_defaults(changeset) do
    changeset
    |> put_change(:type, "custom")
    |> maybe_put(:blocks, [])
  end

  # Only puts the change if no explicit value was provided in the changeset
  defp maybe_put(changeset, field, default) do
    if get_change(changeset, field), do: changeset, else: put_change(changeset, field, default)
  end

  def system_slugs, do: @system_slugs
  def reserved_paths, do: @reserved_paths
  def system_slug?(slug), do: slug in @system_slugs
  def reserved_path?(slug), do: slug in @reserved_paths
end
