defmodule Mv.Membership.MemberTest do
  use Mv.DataCase, async: false
  alias Mv.Membership

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

  describe "Fields and Validations" do
    @valid_attrs %{
      first_name: "John",
      last_name: "Doe",
      email: "john@example.com",
      join_date: ~D[2020-01-01],
      exit_date: nil,
      notes: "Test note",
      city: "Berlin",
      street: "Main Street",
      house_number: "1A",
      postal_code: "12345"
    }

    test "First name is optional", %{actor: actor} do
      attrs = Map.delete(@valid_attrs, :first_name)
      assert {:ok, _member} = Membership.create_member(attrs, actor: actor)
    end

    test "Last name is optional", %{actor: actor} do
      attrs = Map.delete(@valid_attrs, :last_name)
      assert {:ok, _member} = Membership.create_member(attrs, actor: actor)
    end

    test "Email is required", %{actor: actor} do
      attrs = Map.put(@valid_attrs, :email, "")

      assert {:error, %Ash.Error.Invalid{errors: errors}} =
               Membership.create_member(attrs, actor: actor)

      assert error_message(errors, :email) =~ "must be present"
    end

    test "Email must be valid", %{actor: actor} do
      attrs = Map.put(@valid_attrs, :email, "test@")

      assert {:error, %Ash.Error.Invalid{errors: errors}} =
               Membership.create_member(attrs, actor: actor)

      assert error_message(errors, :email) =~ "is not a valid email"
    end

    test "Join date cannot be in the future", %{actor: actor} do
      attrs = Map.put(@valid_attrs, :join_date, Date.utc_today() |> Date.add(1))

      assert {:error,
              %Ash.Error.Invalid{errors: [%Ash.Error.Changes.InvalidAttribute{field: :join_date}]}} =
               Membership.create_member(attrs, actor: actor)
    end

    test "Exit date is optional but must not be before join date if both are specified", %{
      actor: actor
    } do
      attrs = Map.put(@valid_attrs, :exit_date, ~D[2010-01-01])

      assert {:error, %Ash.Error.Invalid{errors: errors}} =
               Membership.create_member(attrs, actor: actor)

      assert error_message(errors, :exit_date) =~ "cannot be before join date"
      attrs2 = Map.delete(@valid_attrs, :exit_date)
      assert {:ok, _member} = Membership.create_member(attrs2, actor: actor)
    end

    test "Notes is optional", %{actor: actor} do
      attrs = Map.delete(@valid_attrs, :notes)
      assert {:ok, _member} = Membership.create_member(attrs, actor: actor)
    end

    test "City, street, house number are optional", %{actor: actor} do
      attrs = @valid_attrs |> Map.drop([:city, :street, :house_number])
      assert {:ok, _member} = Membership.create_member(attrs, actor: actor)
    end

    test "Postal code is optional", %{actor: actor} do
      attrs = Map.delete(@valid_attrs, :postal_code)
      assert {:ok, _member} = Membership.create_member(attrs, actor: actor)
    end
  end

  describe "Settings-driven required fields" do
    @valid_attrs %{
      first_name: "John",
      last_name: "Doe",
      email: "john@example.com"
    }

    setup do
      {:ok, settings} = Membership.get_settings()
      saved_visibility = settings.member_field_visibility || %{}
      saved_required = settings.member_field_required || %{}

      on_exit(fn ->
        {:ok, s} = Membership.get_settings()

        Membership.update_settings(s, %{
          member_field_visibility: saved_visibility,
          member_field_required: saved_required
        })
      end)

      :ok
    end

    test "when first_name is required in settings, create without first_name fails", %{
      actor: actor
    } do
      {:ok, settings} = Membership.get_settings()

      {:ok, _} =
        Membership.update_single_member_field(settings,
          field: "first_name",
          show_in_overview: true,
          required: true
        )

      attrs = Map.delete(@valid_attrs, :first_name)

      assert {:error, %Ash.Error.Invalid{errors: errors}} =
               Membership.create_member(attrs, actor: actor)

      assert error_message(errors, :first_name) =~ "can't be blank"
    end

    test "when first_name is required in settings, create with first_name succeeds", %{
      actor: actor
    } do
      {:ok, settings} = Membership.get_settings()

      {:ok, _} =
        Membership.update_single_member_field(settings,
          field: "first_name",
          show_in_overview: true,
          required: true
        )

      assert {:ok, _member} = Membership.create_member(@valid_attrs, actor: actor)
    end
  end

  describe "Authorization" do
    @valid_attrs %{
      first_name: "John",
      last_name: "Doe",
      email: "john@example.com"
    }

    test "user without role cannot create member" do
      # Create a user without a role
      user = Mv.Fixtures.user_fixture()
      # Ensure user has no role (nil role)
      user_without_role = %{user | role: nil}

      # Attempt to create a member with user without role as actor
      # This should fail with Ash.Error.Forbidden containing a Policy error
      assert {:error, %Ash.Error.Forbidden{errors: [%Ash.Error.Forbidden.Policy{}]}} =
               Membership.create_member(@valid_attrs, actor: user_without_role)
    end
  end

  # Helper function for error evaluation
  defp error_message(errors, field) do
    errors
    |> Enum.filter(fn err -> Map.get(err, :field) == field end)
    |> Enum.map(&Map.get(&1, :message, ""))
    |> List.first()
  end
end
