defmodule Mv.Membership.Email do
  @moduledoc """
  Custom Ash type for validated email addresses.

  ## Overview
  This type extends `:string` with email-specific validation constraints.
  It ensures that email values stored in CustomFieldValue resources are valid email
  addresses according to a standard regex pattern.

  ## Validation Rules
  - **Optional**: `nil` and empty strings are allowed (custom fields are optional)
  - Minimum length: 5 characters (for non-empty values)
  - Maximum length: 254 characters (RFC 5321 maximum)
  - Pattern: Standard email format (username@domain.tld)
  - Automatic trimming of leading/trailing whitespace (empty strings become `nil`)

  ## Usage
  This type is used in the CustomFieldValue union type for custom fields with
  `value_type: :email` in CustomField definitions.

  ## Example
      # In a custom field definition
      CustomField.create!(%{
        name: "work_email",
        value_type: :email
      })

      # Valid values
      "user@example.com"
      "first.last@company.co.uk"

      # Invalid values
      "not-an-email"      # Missing @ and domain
      "a@b"               # Too short
  """
  @match_pattern ~S/^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$/
  @match_regex Regex.compile!(@match_pattern)
  @min_length 5
  @max_length 254

  use Ash.Type.NewType,
    subtype_of: :string,
    constraints: [
      match: @match_pattern,
      trim?: true,
      min_length: @min_length,
      max_length: @max_length
    ]

  @impl true
  def cast_input(nil, _), do: {:ok, nil}

  @impl true
  def cast_input(value, _) when is_binary(value) do
    value = String.trim(value)

    cond do
      # Empty string after trim becomes nil (optional field)
      value == "" ->
        {:ok, nil}

      String.length(value) < @min_length ->
        :error

      String.length(value) > @max_length ->
        :error

      !Regex.match?(@match_regex, value) ->
        :error

      true ->
        {:ok, value}
    end
  end

  @impl true
  def cast_input(_, _), do: :error
end
