defmodule Mv.Membership.FuzzySearchTest do
  use Mv.DataCase, async: false

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

  test "fuzzy_search/2 function exists" do
    assert function_exported?(Mv.Membership.Member, :fuzzy_search, 2)
  end

  test "fuzzy_search returns only John Doe by fuzzy query 'john'", %{actor: actor} do
    {:ok, john} =
      Mv.Membership.create_member(
        %{
          first_name: "John",
          last_name: "Doe",
          email: "john.doe@example.com"
        },
        actor: actor
      )

    {:ok, _jane} =
      Mv.Membership.create_member(
        %{
          first_name: "Adriana",
          last_name: "Smith",
          email: "adriana.smith@example.com"
        },
        actor: actor
      )

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

    result =
      Mv.Membership.Member
      |> Mv.Membership.Member.fuzzy_search(%{
        query: "john"
      })
      |> Ash.read!(actor: actor)

    assert Enum.map(result, & &1.id) == [john.id, alice.id]
  end

  test "fuzzy_search finds 'Thomas' when searching misspelled 'tomas'", %{actor: actor} do
    {:ok, thomas} =
      Mv.Membership.create_member(
        %{
          first_name: "Thomas",
          last_name: "Doe",
          email: "john.doe@example.com"
        },
        actor: actor
      )

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

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

    result =
      Mv.Membership.Member
      |> Mv.Membership.Member.fuzzy_search(%{
        query: "tomas"
      })
      |> Ash.read!(actor: actor)

    ids = Enum.map(result, & &1.id)
    assert thomas.id in ids
    refute jane.id in ids
    assert not Enum.empty?(ids)
  end

  test "empty query returns all members", %{actor: actor} do
    {:ok, a} =
      Mv.Membership.create_member(%{first_name: "A", last_name: "One", email: "a1@example.com"},
        actor: actor
      )

    {:ok, b} =
      Mv.Membership.create_member(%{first_name: "B", last_name: "Two", email: "b2@example.com"},
        actor: actor
      )

    result =
      Mv.Membership.Member
      |> Mv.Membership.Member.fuzzy_search(%{query: ""})
      |> Ash.read!(actor: actor)

    assert Enum.sort(Enum.map(result, & &1.id))
           |> Enum.uniq()
           |> Enum.sort()
           |> Enum.all?(fn id -> id in [a.id, b.id] end)
  end

  test "substring numeric search matches postal_code mid-string", %{actor: actor} do
    {:ok, m1} =
      Mv.Membership.create_member(
        %{
          first_name: "Num",
          last_name: "One",
          email: "n1@example.com",
          postal_code: "12345"
        },
        actor: actor
      )

    {:ok, _m2} =
      Mv.Membership.create_member(
        %{
          first_name: "Num",
          last_name: "Two",
          email: "n2@example.com",
          postal_code: "67890"
        },
        actor: actor
      )

    result =
      Mv.Membership.Member
      |> Mv.Membership.Member.fuzzy_search(%{query: "345"})
      |> Ash.read!(actor: actor)

    ids = Enum.map(result, & &1.id)
    assert m1.id in ids
  end

  test "substring numeric search matches house_number mid-string", %{actor: actor} do
    {:ok, m1} =
      Mv.Membership.create_member(
        %{
          first_name: "Home",
          last_name: "One",
          email: "h1@example.com",
          house_number: "A345B"
        },
        actor: actor
      )

    {:ok, _m2} =
      Mv.Membership.create_member(
        %{
          first_name: "Home",
          last_name: "Two",
          email: "h2@example.com",
          house_number: "77"
        },
        actor: actor
      )

    result =
      Mv.Membership.Member
      |> Mv.Membership.Member.fuzzy_search(%{query: "345"})
      |> Ash.read!(actor: actor)

    ids = Enum.map(result, & &1.id)
    assert m1.id in ids
  end

  test "fuzzy matches street misspelling", %{actor: actor} do
    {:ok, s1} =
      Mv.Membership.create_member(
        %{
          first_name: "Road",
          last_name: "Test",
          email: "s1@example.com",
          street: "Main Street"
        },
        actor: actor
      )

    {:ok, _s2} =
      Mv.Membership.create_member(
        %{
          first_name: "Road",
          last_name: "Other",
          email: "s2@example.com",
          street: "Second Avenue"
        },
        actor: actor
      )

    result =
      Mv.Membership.Member
      |> Mv.Membership.Member.fuzzy_search(%{query: "mainn"})
      |> Ash.read!(actor: actor)

    ids = Enum.map(result, & &1.id)
    assert s1.id in ids
  end

  test "substring in city matches mid-string", %{actor: actor} do
    {:ok, b} =
      Mv.Membership.create_member(
        %{
          first_name: "City",
          last_name: "One",
          email: "city1@example.com",
          city: "Berlin"
        },
        actor: actor
      )

    {:ok, _m} =
      Mv.Membership.create_member(
        %{
          first_name: "City",
          last_name: "Two",
          email: "city2@example.com",
          city: "München"
        },
        actor: actor
      )

    result =
      Mv.Membership.Member
      |> Mv.Membership.Member.fuzzy_search(%{query: "erl"})
      |> Ash.read!(actor: actor)

    ids = Enum.map(result, & &1.id)
    assert b.id in ids
  end

  test "blank character handling: query with spaces matches full name", %{actor: actor} do
    {:ok, member} =
      Mv.Membership.create_member(
        %{
          first_name: "John",
          last_name: "Doe",
          email: "john.doe@example.com"
        },
        actor: actor
      )

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

    result =
      Mv.Membership.Member
      |> Mv.Membership.Member.fuzzy_search(%{query: "john doe"})
      |> Ash.read!(actor: actor)

    ids = Enum.map(result, & &1.id)
    assert member.id in ids
  end

  test "blank character handling: query with multiple spaces is handled", %{actor: actor} do
    {:ok, member} =
      Mv.Membership.create_member(
        %{
          first_name: "Mary",
          last_name: "Jane",
          email: "mary.jane@example.com"
        },
        actor: actor
      )

    result =
      Mv.Membership.Member
      |> Mv.Membership.Member.fuzzy_search(%{query: "mary   jane"})
      |> Ash.read!(actor: actor)

    ids = Enum.map(result, & &1.id)
    assert member.id in ids
  end

  test "special character handling: @ symbol in query matches email", %{actor: actor} do
    {:ok, member} =
      Mv.Membership.create_member(
        %{
          first_name: "Test",
          last_name: "User",
          email: "test.user@example.com"
        },
        actor: actor
      )

    {:ok, _other} =
      Mv.Membership.create_member(
        %{
          first_name: "Other",
          last_name: "Person",
          email: "other.person@different.org"
        },
        actor: actor
      )

    result =
      Mv.Membership.Member
      |> Mv.Membership.Member.fuzzy_search(%{query: "example"})
      |> Ash.read!(actor: actor)

    ids = Enum.map(result, & &1.id)
    assert member.id in ids
  end

  test "special character handling: dot in query matches email", %{actor: actor} do
    {:ok, member} =
      Mv.Membership.create_member(
        %{
          first_name: "Dot",
          last_name: "Test",
          email: "dot.test@example.com"
        },
        actor: actor
      )

    {:ok, _other} =
      Mv.Membership.create_member(
        %{
          first_name: "No",
          last_name: "Dot",
          email: "nodot@example.com"
        },
        actor: actor
      )

    result =
      Mv.Membership.Member
      |> Mv.Membership.Member.fuzzy_search(%{query: "dot.test"})
      |> Ash.read!(actor: actor)

    ids = Enum.map(result, & &1.id)
    assert member.id in ids
  end

  test "special character handling: hyphen in query matches data", %{actor: actor} do
    {:ok, member} =
      Mv.Membership.create_member(
        %{
          first_name: "Mary-Jane",
          last_name: "Watson",
          email: "mary.jane@example.com"
        },
        actor: actor
      )

    {:ok, _other} =
      Mv.Membership.create_member(
        %{
          first_name: "Mary",
          last_name: "Smith",
          email: "mary.smith@example.com"
        },
        actor: actor
      )

    result =
      Mv.Membership.Member
      |> Mv.Membership.Member.fuzzy_search(%{query: "mary-jane"})
      |> Ash.read!(actor: actor)

    ids = Enum.map(result, & &1.id)
    assert member.id in ids
  end

  test "unicode character handling: umlaut ö in query matches data", %{actor: actor} do
    {:ok, member} =
      Mv.Membership.create_member(
        %{
          first_name: "Jörg",
          last_name: "Schmidt",
          email: "joerg.schmidt@example.com"
        },
        actor: actor
      )

    {:ok, _other} =
      Mv.Membership.create_member(
        %{
          first_name: "John",
          last_name: "Smith",
          email: "john.smith@example.com"
        },
        actor: actor
      )

    result =
      Mv.Membership.Member
      |> Mv.Membership.Member.fuzzy_search(%{query: "jörg"})
      |> Ash.read!(actor: actor)

    ids = Enum.map(result, & &1.id)
    assert member.id in ids
  end

  test "unicode character handling: umlaut ä in query matches data", %{actor: actor} do
    {:ok, member} =
      Mv.Membership.create_member(
        %{
          first_name: "Märta",
          last_name: "Andersson",
          email: "maerta.andersson@example.com"
        },
        actor: actor
      )

    {:ok, _other} =
      Mv.Membership.create_member(
        %{
          first_name: "Marta",
          last_name: "Johnson",
          email: "marta.johnson@example.com"
        },
        actor: actor
      )

    result =
      Mv.Membership.Member
      |> Mv.Membership.Member.fuzzy_search(%{query: "märta"})
      |> Ash.read!(actor: actor)

    ids = Enum.map(result, & &1.id)
    assert member.id in ids
  end

  test "unicode character handling: umlaut ü in query matches data", %{actor: actor} do
    {:ok, member} =
      Mv.Membership.create_member(
        %{
          first_name: "Günther",
          last_name: "Müller",
          email: "guenther.mueller@example.com"
        },
        actor: actor
      )

    {:ok, _other} =
      Mv.Membership.create_member(
        %{
          first_name: "Gunter",
          last_name: "Miller",
          email: "gunter.miller@example.com"
        },
        actor: actor
      )

    result =
      Mv.Membership.Member
      |> Mv.Membership.Member.fuzzy_search(%{query: "müller"})
      |> Ash.read!(actor: actor)

    ids = Enum.map(result, & &1.id)
    assert member.id in ids
  end

  test "unicode character handling: query without umlaut matches data with umlaut", %{
    actor: actor
  } do
    {:ok, member} =
      Mv.Membership.create_member(
        %{
          first_name: "Müller",
          last_name: "Schmidt",
          email: "mueller.schmidt@example.com"
        },
        actor: actor
      )

    {:ok, _other} =
      Mv.Membership.create_member(
        %{
          first_name: "Miller",
          last_name: "Smith",
          email: "miller.smith@example.com"
        },
        actor: actor
      )

    result =
      Mv.Membership.Member
      |> Mv.Membership.Member.fuzzy_search(%{query: "muller"})
      |> Ash.read!(actor: actor)

    ids = Enum.map(result, & &1.id)
    assert member.id in ids
  end

  test "very long search strings: handles long query without error", %{actor: actor} do
    {:ok, _member} =
      Mv.Membership.create_member(
        %{
          first_name: "Test",
          last_name: "User",
          email: "test@example.com"
        },
        actor: actor
      )

    long_query = String.duplicate("a", 1000)

    result =
      Mv.Membership.Member
      |> Mv.Membership.Member.fuzzy_search(%{query: long_query})
      |> Ash.read!(actor: actor)

    # Should not crash, may return empty or some results
    assert is_list(result)
  end

  test "very long search strings: handles extremely long query", %{actor: actor} do
    {:ok, _member} =
      Mv.Membership.create_member(
        %{
          first_name: "Test",
          last_name: "User",
          email: "test@example.com"
        },
        actor: actor
      )

    very_long_query = String.duplicate("test query ", 1000)

    result =
      Mv.Membership.Member
      |> Mv.Membership.Member.fuzzy_search(%{query: very_long_query})
      |> Ash.read!(actor: actor)

    # Should not crash, may return empty or some results
    assert is_list(result)
  end
end
