defmodule Ash.Test.ReactorChangeTest do
  @moduledoc false
  use ExUnit.Case, async: false

  defmodule Post do
    @moduledoc false
    use Ash.Resource, data_layer: Ash.DataLayer.Ets, domain: Ash.Test.Domain

    ets do
      private? true
    end

    attributes do
      uuid_primary_key :id
      attribute :title, :string, allow_nil?: false, public?: true
    end

    actions do
      defaults [:read, :destroy, update: :*, create: :*]
    end
  end

  test "it can create a post" do
    defmodule SimpleCreatePostReactor do
      @moduledoc false
      use Reactor, extensions: [Ash.Reactor]

      input :title

      change :set_title, set_attribute(:title, arg(:title)) do
        initial(Post)
        argument :title, input(:title)
      end

      create :create_post, Post, :create do
        initial(result(:set_title))
      end
    end

    assert {:ok, post} = Reactor.run(SimpleCreatePostReactor, %{title: "Title"})
    assert is_binary(post.id)
    assert post.title == "Title"
  end

  test "it honours the `fail_if_invalid?` option" do
    defmodule SimpleChangeFailReactor do
      @moduledoc false
      use Ash.Reactor

      input :title

      step :changeset do
        run fn _ ->
          {:ok, Ash.Changeset.for_action(Post, :create)}
        end
      end

      change :set_title, set_attribute(:title, arg(:title)) do
        initial(result(:changeset))
        argument :title, input(:title)
        fail_if_invalid?(true)
      end
    end

    assert {:error, invalid} = Reactor.run(SimpleChangeFailReactor, %{title: nil})
    assert Exception.message(invalid) =~ "valid?: false"
  end

  test "it honours the `only_when_valid?` option" do
    defmodule SimpleOnlyWhenValidReactor do
      @moduledoc false
      use Ash.Reactor

      input :changeset
      input :title

      change :set_title, set_attribute(:title, arg(:title)) do
        initial(input(:changeset))
        only_when_valid? true
      end
    end

    valid_changeset = Ash.Changeset.new(Post)

    {:ok, changeset} =
      Reactor.run(SimpleOnlyWhenValidReactor, %{changeset: valid_changeset, title: "Title"})

    assert Ash.Changeset.changing_attribute?(changeset, :title)

    invalid_changeset = %{valid_changeset | valid?: false}

    {:ok, changeset} =
      Reactor.run(SimpleOnlyWhenValidReactor, %{changeset: invalid_changeset, title: "Title"})

    refute Ash.Changeset.changing_attribute?(changeset, :title)
  end

  test "it honours the `where` option" do
    defmodule SimpleWhereChangeReactor do
      @moduledoc false
      use Ash.Reactor

      input :changeset

      change :set_title, set_attribute(:title, "Default title") do
        initial(input(:changeset))
        where [absent(:title)]
      end
    end

    blank_changeset = Ash.Changeset.for_create(Post, :create, %{})
    {:ok, changeset} = Reactor.run(SimpleWhereChangeReactor, %{changeset: blank_changeset})
    assert Ash.Changeset.get_attribute(changeset, :title) == "Default title"

    with_title_changeset = Ash.Changeset.for_create(Post, :create, %{title: "Explicit title"})
    {:ok, changeset} = Reactor.run(SimpleWhereChangeReactor, %{changeset: with_title_changeset})
    assert Ash.Changeset.get_attribute(changeset, :title) == "Explicit title"
  end
end
