defmodule Mv.Accounts.UserMemberLinkingEmailTest do
  @moduledoc """
  Tests email validation during user-member linking.
  Implements rules from docs/email-sync.md.
  Tests for Issue #168, specifically Problem #4: Email validation bug.
  """

  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 "link with same email" do
    test "succeeds when user.email == member.email", %{actor: actor} do
      # Create member with specific email
      {:ok, member} =
        Membership.create_member(
          %{
            first_name: "Alice",
            last_name: "Johnson",
            email: "alice@example.com"
          },
          actor: actor
        )

      # Create user with same email and link to member
      result =
        Accounts.create_user(
          %{
            email: "alice@example.com",
            member: %{id: member.id}
          },
          actor: actor
        )

      # Should succeed without errors
      assert {:ok, user} = result
      assert to_string(user.email) == "alice@example.com"

      # Reload to verify link
      user = Ash.load!(user, [:member], domain: Mv.Accounts, actor: actor)
      assert user.member.id == member.id
      assert user.member.email == "alice@example.com"
    end

    test "no validation error triggered when updating linked pair with same email", %{
      actor: actor
    } do
      # Create member
      {:ok, member} =
        Membership.create_member(
          %{
            first_name: "Bob",
            last_name: "Smith",
            email: "bob@example.com"
          },
          actor: actor
        )

      # Create user and link
      {:ok, user} =
        Accounts.create_user(
          %{
            email: "bob@example.com",
            member: %{id: member.id}
          },
          actor: actor
        )

      # Update user (should not trigger email validation error)
      result = Accounts.update_user(user, %{email: "bob@example.com"}, actor: actor)

      assert {:ok, updated_user} = result
      assert to_string(updated_user.email) == "bob@example.com"
    end
  end

  describe "link with different emails" do
    test "fails if member.email is used by a DIFFERENT linked user", %{actor: actor} do
      # Create first user and link to a different member
      {:ok, other_member} =
        Membership.create_member(
          %{
            first_name: "Other",
            last_name: "Member",
            email: "other@example.com"
          },
          actor: actor
        )

      {:ok, _user1} =
        Accounts.create_user(
          %{
            email: "user1@example.com",
            member: %{id: other_member.id}
          },
          actor: actor
        )

      # Reload to ensure email sync happened
      _other_member = Ash.reload!(other_member, actor: actor)

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

      # Try to create user2 with email that matches the linked other_member
      result =
        Accounts.create_user(
          %{
            email: "user1@example.com",
            member: %{id: member.id}
          },
          actor: actor
        )

      # Should fail because user1@example.com is already used by other_member (which is linked to user1)
      assert {:error, _error} = result
    end

    test "succeeds for unique emails", %{actor: actor} do
      # Create member
      {:ok, member} =
        Membership.create_member(
          %{
            first_name: "David",
            last_name: "Wilson",
            email: "david@example.com"
          },
          actor: actor
        )

      # Create user with different but unique email
      result =
        Accounts.create_user(
          %{
            email: "user@example.com",
            member: %{id: member.id}
          },
          actor: actor
        )

      # Should succeed
      assert {:ok, user} = result

      # Email sync should update member's email to match user's
      user = Ash.load!(user, [:member], domain: Mv.Accounts, actor: actor)
      assert user.member.email == "user@example.com"
    end
  end

  describe "edge cases" do
    test "unlinking and relinking with same email works (Problem #4)", %{actor: actor} do
      # This is the exact scenario from Problem #4:
      # 1. Link user and member (both have same email)
      # 2. Unlink them (member keeps the email)
      # 3. Try to relink (validation should NOT fail)

      # Create member
      {:ok, member} =
        Membership.create_member(
          %{
            first_name: "Emma",
            last_name: "Davis",
            email: "emma@example.com"
          },
          actor: actor
        )

      # Create user and link
      {:ok, user} =
        Accounts.create_user(
          %{
            email: "emma@example.com",
            member: %{id: member.id}
          },
          actor: actor
        )

      # Verify they are linked
      user = Ash.load!(user, [:member], domain: Mv.Accounts, actor: actor)
      assert user.member.id == member.id
      assert user.member.email == "emma@example.com"

      # Unlink
      {:ok, unlinked_user} = Accounts.update_user(user, %{member: nil}, actor: actor)
      assert is_nil(unlinked_user.member_id)

      # Member still has the email after unlink
      member = Ash.reload!(member, actor: actor)
      assert member.email == "emma@example.com"

      # Relink (should work - this is Problem #4)
      result = Accounts.update_user(unlinked_user, %{member: %{id: member.id}}, actor: actor)

      assert {:ok, relinked_user} = result
      assert relinked_user.member_id == member.id
    end
  end
end
