defmodule Mv.Accounts.UserMemberLinkingTest do
  @moduledoc """
  Integration tests for User-Member linking functionality.

  Tests the complete workflow of linking and unlinking members to users,
  including email synchronization and validation rules.
  """
  use Mv.DataCase, async: false
  alias Mv.Accounts
  alias Mv.Membership

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

  describe "User-Member Linking with Email Sync" do
    test "link user to member with different email syncs member email", %{actor: actor} do
      # Create user with one email
      {:ok, user} = Accounts.create_user(%{email: "user@example.com"}, actor: actor)

      # Create member with different email
      {:ok, member} =
        Membership.create_member(
          %{
            first_name: "John",
            last_name: "Doe",
            email: "member@example.com"
          },
          actor: actor
        )

      # Link user to member
      {:ok, updated_user} = Accounts.update_user(user, %{member: %{id: member.id}}, actor: actor)

      # Verify link exists
      user_with_member =
        Ash.get!(Mv.Accounts.User, updated_user.id, actor: actor, load: [:member])

      assert user_with_member.member.id == member.id

      # Verify member email was synced to match user email
      synced_member = Ash.get!(Mv.Membership.Member, member.id, actor: actor)
      assert synced_member.email == "user@example.com"
    end

    test "unlink member from user sets member to nil", %{actor: actor} do
      # Create and link user and member
      {:ok, user} = Accounts.create_user(%{email: "user@example.com"}, actor: actor)

      {:ok, member} =
        Membership.create_member(
          %{
            first_name: "Jane",
            last_name: "Smith",
            email: "jane@example.com"
          },
          actor: actor
        )

      {:ok, linked_user} = Accounts.update_user(user, %{member: %{id: member.id}}, actor: actor)

      # Verify link exists
      user_with_member = Ash.get!(Mv.Accounts.User, linked_user.id, actor: actor, load: [:member])
      assert user_with_member.member.id == member.id

      # Unlink by setting member to nil
      {:ok, unlinked_user} = Accounts.update_user(linked_user, %{member: nil}, actor: actor)

      # Verify link is removed
      user_without_member =
        Ash.get!(Mv.Accounts.User, unlinked_user.id, actor: actor, load: [:member])

      assert is_nil(user_without_member.member)

      # Verify member still exists independently
      member_still_exists = Ash.get!(Mv.Membership.Member, member.id, actor: actor)
      assert member_still_exists.id == member.id
    end

    test "cannot link member already linked to another user", %{actor: actor} do
      # Create first user and link to member
      {:ok, user1} = Accounts.create_user(%{email: "user1@example.com"}, actor: actor)

      {:ok, member} =
        Membership.create_member(
          %{
            first_name: "Bob",
            last_name: "Wilson",
            email: "bob@example.com"
          },
          actor: actor
        )

      {:ok, _linked_user1} =
        Accounts.update_user(user1, %{member: %{id: member.id}}, actor: actor)

      # Create second user and try to link to same member
      {:ok, user2} = Accounts.create_user(%{email: "user2@example.com"}, actor: actor)

      # Should fail because member is already linked
      assert {:error, %Ash.Error.Invalid{}} =
               Accounts.update_user(user2, %{member: %{id: member.id}}, actor: actor)
    end

    test "cannot change member link directly, must unlink first", %{actor: actor} do
      # Create user and link to first member
      {:ok, user} = Accounts.create_user(%{email: "user@example.com"}, actor: actor)

      {:ok, member1} =
        Membership.create_member(
          %{
            first_name: "Alice",
            last_name: "Johnson",
            email: "alice@example.com"
          },
          actor: actor
        )

      {:ok, linked_user} = Accounts.update_user(user, %{member: %{id: member1.id}}, actor: actor)

      # Create second member
      {:ok, member2} =
        Membership.create_member(
          %{
            first_name: "Charlie",
            last_name: "Brown",
            email: "charlie@example.com"
          },
          actor: actor
        )

      # Try to directly change member link (should fail)
      assert {:error, %Ash.Error.Invalid{errors: errors}} =
               Accounts.update_user(linked_user, %{member: %{id: member2.id}}, actor: actor)

      # Verify error message mentions "Remove existing member first"
      error_messages = Enum.map(errors, & &1.message)
      assert Enum.any?(error_messages, &String.contains?(&1, "Remove existing member first"))

      # Two-step process: first unlink, then link new member
      {:ok, unlinked_user} = Accounts.update_user(linked_user, %{member: nil}, actor: actor)

      # After unlinking, member1 still has the user's email
      # Change member1's email to avoid conflict when relinking to member2
      {:ok, _} =
        Membership.update_member(member1, %{email: "alice_changed@example.com"}, actor: actor)

      {:ok, relinked_user} =
        Accounts.update_user(unlinked_user, %{member: %{id: member2.id}}, actor: actor)

      # Verify new link is established
      user_with_new_member =
        Ash.get!(Mv.Accounts.User, relinked_user.id, actor: actor, load: [:member])

      assert user_with_new_member.member.id == member2.id
    end
  end
end
