defmodule Mv.Accounts.EmailSyncEdgeCasesTest do
  @moduledoc """
  Edge case tests for email synchronization between User and Member.
  Tests various boundary conditions and validation scenarios.
  """
  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 "Email sync edge cases" do
    @valid_user_attrs %{
      email: "user@example.com"
    }

    @valid_member_attrs %{
      first_name: "John",
      last_name: "Doe",
      email: "member@example.com"
    }

    test "simultaneous email updates use user email as source of truth", %{actor: actor} do
      # Create linked user and member
      {:ok, member} = Membership.create_member(@valid_member_attrs, actor: actor)

      {:ok, user} =
        Accounts.create_user(Map.put(@valid_user_attrs, :member, %{id: member.id}), actor: actor)

      # Verify link and initial sync
      {:ok, synced_member} = Ash.get(Mv.Membership.Member, member.id, actor: actor)
      assert synced_member.email == "user@example.com"

      # Scenario: Both emails are updated "simultaneously"
      # In practice, this tests that when a member email is updated,
      # it syncs to user, and user remains the source of truth

      # Update member email first
      {:ok, _updated_member} =
        Membership.update_member(member, %{email: "member-new@example.com"}, actor: actor)

      # Verify it synced to user
      {:ok, user_after_member_update} = Ash.get(Mv.Accounts.User, user.id, actor: actor)
      assert to_string(user_after_member_update.email) == "member-new@example.com"

      # Now update user email - this should override
      {:ok, _updated_user} =
        Accounts.update_user(user_after_member_update, %{email: "user-final@example.com"},
          actor: actor
        )

      # Reload both
      {:ok, final_user} = Ash.get(Mv.Accounts.User, user.id, actor: actor)
      {:ok, final_member} = Ash.get(Mv.Membership.Member, member.id, actor: actor)

      # User email should be the final truth
      assert to_string(final_user.email) == "user-final@example.com"
      assert final_member.email == "user-final@example.com"
    end

    test "email validation works for both user and member", %{actor: actor} do
      # Test that invalid emails are rejected for both resources

      # Invalid email for user
      invalid_user_result = Accounts.create_user(%{email: "not-an-email"}, actor: actor)
      assert {:error, %Ash.Error.Invalid{}} = invalid_user_result

      # Invalid email for member
      invalid_member_attrs = Map.put(@valid_member_attrs, :email, "also-not-an-email")
      invalid_member_result = Membership.create_member(invalid_member_attrs, actor: actor)
      assert {:error, %Ash.Error.Invalid{}} = invalid_member_result

      # Valid emails should work
      {:ok, _user} = Accounts.create_user(@valid_user_attrs, actor: actor)
      {:ok, _member} = Membership.create_member(@valid_member_attrs, actor: actor)
    end

    test "identity constraints prevent duplicate emails", %{actor: actor} do
      # Create first user with an email
      {:ok, user1} = Accounts.create_user(%{email: "duplicate@example.com"}, actor: actor)
      assert to_string(user1.email) == "duplicate@example.com"

      # Try to create second user with same email - should fail due to unique constraint
      result = Accounts.create_user(%{email: "duplicate@example.com"}, actor: actor)
      assert {:error, %Ash.Error.Invalid{}} = result

      # Same for members
      member_attrs = Map.put(@valid_member_attrs, :email, "member-dup@example.com")
      {:ok, member1} = Membership.create_member(member_attrs, actor: actor)
      assert member1.email == "member-dup@example.com"

      # Try to create second member with same email - should fail
      result2 = Membership.create_member(member_attrs, actor: actor)
      assert {:error, %Ash.Error.Invalid{}} = result2
    end
  end
end
