defmodule Mv.Membership.CustomFieldSlugTest do
  @moduledoc """
  Tests for CustomField slug business rules only.

  We test our business logic, not Ash/slugify implementation details:
  - Slug is generated from name on create (one smoke test)
  - Slug is unique (business rule)
  - Slug is immutable (does not change when name is updated; cannot be set manually)
  - Slug cannot be empty (rejects name with only special characters)

  We do not test: slugify edge cases (umlauts, truncation, etc.) or Ash/Ecto struct/load behavior.
  """
  use Mv.DataCase, async: true

  alias Mv.Membership.CustomField

  setup do
    system_actor = Mv.Helpers.SystemActor.get_system_actor()
    %{actor: system_actor}
  end

  describe "slug generation (business rule)" do
    test "slug is generated from name on create", %{actor: actor} do
      {:ok, custom_field} =
        CustomField
        |> Ash.Changeset.for_create(:create, %{
          name: "Mobile Phone",
          value_type: :string
        })
        |> Ash.create(actor: actor)

      assert custom_field.slug == "mobile-phone"
    end
  end

  describe "slug uniqueness" do
    test "prevents creating custom field with duplicate slug", %{actor: actor} do
      # Create first custom field
      {:ok, _custom_field} =
        CustomField
        |> Ash.Changeset.for_create(:create, %{
          name: "Test",
          value_type: :string
        })
        |> Ash.create(actor: actor)

      # Attempt to create second custom field with same slug (different case in name)
      assert {:error, %Ash.Error.Invalid{} = error} =
               CustomField
               |> Ash.Changeset.for_create(:create, %{
                 name: "test",
                 value_type: :integer
               })
               |> Ash.create(actor: actor)

      assert Exception.message(error) =~ "has already been taken"
    end

    test "allows custom fields with different slugs", %{actor: actor} do
      {:ok, custom_field1} =
        CustomField
        |> Ash.Changeset.for_create(:create, %{
          name: "Test One",
          value_type: :string
        })
        |> Ash.create(actor: actor)

      {:ok, custom_field2} =
        CustomField
        |> Ash.Changeset.for_create(:create, %{
          name: "Test Two",
          value_type: :string
        })
        |> Ash.create(actor: actor)

      assert custom_field1.slug == "test-one"
      assert custom_field2.slug == "test-two"
      assert custom_field1.slug != custom_field2.slug
    end

    test "prevents duplicate slugs when names differ only in special characters", %{actor: actor} do
      {:ok, custom_field1} =
        CustomField
        |> Ash.Changeset.for_create(:create, %{
          name: "Test!!!",
          value_type: :string
        })
        |> Ash.create(actor: actor)

      assert custom_field1.slug == "test"

      # Second custom field with name that generates the same slug should fail
      assert {:error, %Ash.Error.Invalid{} = error} =
               CustomField
               |> Ash.Changeset.for_create(:create, %{
                 name: "Test???",
                 value_type: :string
               })
               |> Ash.create(actor: actor)

      # Should fail with uniqueness constraint error
      assert Exception.message(error) =~ "has already been taken"
    end
  end

  describe "slug immutability" do
    test "slug cannot be manually set on create", %{actor: actor} do
      # Attempting to set slug manually should fail because slug is not writable
      result =
        CustomField
        |> Ash.Changeset.for_create(:create, %{
          name: "Test",
          value_type: :string,
          slug: "custom-slug"
        })
        |> Ash.create(actor: actor)

      # Should fail because slug is not an accepted input
      assert {:error, %Ash.Error.Invalid{}} = result
      assert Exception.message(elem(result, 1)) =~ "No such input"
    end

    test "slug does not change when name is updated", %{actor: actor} do
      # Create custom field
      {:ok, custom_field} =
        CustomField
        |> Ash.Changeset.for_create(:create, %{
          name: "Original Name",
          value_type: :string
        })
        |> Ash.create(actor: actor)

      original_slug = custom_field.slug
      assert original_slug == "original-name"

      # Update the name
      {:ok, updated_custom_field} =
        custom_field
        |> Ash.Changeset.for_update(:update, %{
          name: "New Different Name"
        })
        |> Ash.update(actor: actor)

      # Slug should remain unchanged
      assert updated_custom_field.slug == original_slug
      assert updated_custom_field.slug == "original-name"
      assert updated_custom_field.name == "New Different Name"
    end

    test "slug cannot be manually updated", %{actor: actor} do
      {:ok, custom_field} =
        CustomField
        |> Ash.Changeset.for_create(:create, %{
          name: "Test",
          value_type: :string
        })
        |> Ash.create(actor: actor)

      original_slug = custom_field.slug
      assert original_slug == "test"

      # Attempt to manually update slug should fail because slug is not writable
      result =
        custom_field
        |> Ash.Changeset.for_update(:update, %{
          slug: "new-slug"
        })
        |> Ash.update(actor: actor)

      # Should fail because slug is not an accepted input
      assert {:error, %Ash.Error.Invalid{}} = result
      assert Exception.message(elem(result, 1)) =~ "No such input"

      # Reload to verify slug hasn't changed
      reloaded = Ash.get!(CustomField, custom_field.id, actor: actor)
      assert reloaded.slug == "test"
    end
  end

  describe "slug cannot be empty (business rule)" do
    test "rejects name with only special characters", %{actor: actor} do
      assert {:error, %Ash.Error.Invalid{} = error} =
               CustomField
               |> Ash.Changeset.for_create(:create, %{
                 name: "!!!",
                 value_type: :string
               })
               |> Ash.create(actor: actor)

      error_message = Exception.message(error)
      assert error_message =~ "Slug cannot be empty" or error_message =~ "is required"
    end
  end

  describe "slug-based lookup (future feature)" do
    @tag :skip
    test "can find custom field by slug", %{actor: actor} do
      {:ok, custom_field} =
        CustomField
        |> Ash.Changeset.for_create(:create, %{
          name: "Test Field",
          value_type: :string
        })
        |> Ash.create(actor: actor)

      # This test is for future implementation
      # We might add a custom action like :by_slug
      found = Ash.get!(CustomField, custom_field.slug, load: [:slug], actor: actor)
      assert found.id == custom_field.id
    end
  end
end
