defmodule Berrypod.Reviews.Review do
  @moduledoc """
  Schema for product reviews.

  Reviews are linked to a product and optionally to an order (for "verified
  purchase" badge). Each email can only review a product once. Reviews start
  in pending status and must be approved by an admin before displaying.
  """

  use Ecto.Schema
  import Ecto.Changeset

  alias Berrypod.Orders.Order
  alias Berrypod.Products.Product

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

  schema "reviews" do
    field :email, :string
    field :author_name, :string
    field :rating, :integer
    field :title, :string
    field :body, :string
    field :status, :string, default: "pending"
    field :image_ids, {:array, :binary_id}, default: []

    belongs_to :product, Product
    belongs_to :order, Order

    timestamps(type: :utc_datetime)
  end

  @doc """
  Changeset for creating a new review.
  """
  def changeset(review, attrs) do
    review
    |> cast(attrs, [
      :product_id,
      :order_id,
      :email,
      :author_name,
      :rating,
      :title,
      :body,
      :image_ids
    ])
    |> validate_required([:product_id, :email, :author_name, :rating])
    |> validate_inclusion(:rating, 1..5)
    |> validate_length(:title, max: 100)
    |> validate_length(:body, max: 2000)
    |> validate_length(:author_name, max: 50)
    |> validate_length(:image_ids, max: 3)
    |> foreign_key_constraint(:product_id)
    |> foreign_key_constraint(:order_id)
    |> unique_constraint([:email, :product_id], message: "you have already reviewed this product")
    |> normalise_email()
  end

  @doc """
  Changeset for updating an existing review.
  Resets status to pending for re-moderation.
  """
  def update_changeset(review, attrs) do
    review
    |> cast(attrs, [:author_name, :rating, :title, :body, :image_ids])
    |> validate_required([:author_name, :rating])
    |> validate_inclusion(:rating, 1..5)
    |> validate_length(:title, max: 100)
    |> validate_length(:body, max: 2000)
    |> validate_length(:author_name, max: 50)
    |> validate_length(:image_ids, max: 3)
    |> put_change(:status, "pending")
  end

  @doc """
  Changeset for admin moderation (approve/reject).
  """
  def moderation_changeset(review, status) when status in ["approved", "rejected"] do
    review
    |> change(status: status)
  end

  defp normalise_email(changeset) do
    case get_change(changeset, :email) do
      nil -> changeset
      email -> put_change(changeset, :email, String.downcase(String.trim(email)))
    end
  end
end
