defmodule MvWeb.MemberLive.Index.FieldVisibilityTest do
  @moduledoc """
  Tests for FieldVisibility module handling field visibility merging logic.
  """
  use ExUnit.Case, async: true

  alias MvWeb.MemberLive.Index.FieldVisibility

  # Mock custom field structs for testing
  defp create_custom_field(id, name, show_in_overview \\ true) do
    %{
      id: id,
      name: name,
      show_in_overview: show_in_overview
    }
  end

  describe "get_all_available_fields/1" do
    test "returns member fields and custom fields" do
      custom_fields = [
        create_custom_field("cf1", "Custom Field 1"),
        create_custom_field("cf2", "Custom Field 2")
      ]

      result = FieldVisibility.get_all_available_fields(custom_fields)

      # Should include all member fields
      assert :first_name in result
      assert :email in result
      assert :street in result

      # Should include custom fields as strings
      assert "custom_field_cf1" in result
      assert "custom_field_cf2" in result
    end

    test "handles empty custom fields list" do
      result = FieldVisibility.get_all_available_fields([])

      # Should only have member fields
      assert :first_name in result
      assert :email in result

      refute Enum.any?(result, fn field ->
               is_binary(field) and String.starts_with?(field, "custom_field_")
             end)
    end

    test "includes all member fields from constants" do
      custom_fields = []
      result = FieldVisibility.get_all_available_fields(custom_fields)

      member_fields = Mv.Constants.member_fields()

      Enum.each(member_fields, fn field ->
        assert field in result
      end)
    end
  end

  describe "merge_with_global_settings/3" do
    test "user selection overrides global settings" do
      user_selection = %{"first_name" => false}
      settings = %{member_field_visibility: %{first_name: true, email: true}}
      custom_fields = []

      result = FieldVisibility.merge_with_global_settings(user_selection, settings, custom_fields)

      assert result["first_name"] == false
      assert result["email"] == true
    end

    test "falls back to global settings when user selection is empty" do
      user_selection = %{}
      settings = %{member_field_visibility: %{first_name: false, email: true}}
      custom_fields = []

      result = FieldVisibility.merge_with_global_settings(user_selection, settings, custom_fields)

      assert result["first_name"] == false
      assert result["email"] == true
    end

    test "defaults to true when field not in settings" do
      user_selection = %{}
      settings = %{member_field_visibility: %{first_name: false}}
      custom_fields = []

      result = FieldVisibility.merge_with_global_settings(user_selection, settings, custom_fields)

      # first_name from settings
      assert result["first_name"] == false
      # email defaults to true (not in settings)
      assert result["email"] == true
    end

    test "handles custom fields visibility" do
      user_selection = %{}
      settings = %{member_field_visibility: %{}}

      custom_fields = [
        create_custom_field("cf1", "Custom 1", true),
        create_custom_field("cf2", "Custom 2", false)
      ]

      result = FieldVisibility.merge_with_global_settings(user_selection, settings, custom_fields)

      assert result["custom_field_cf1"] == true
      assert result["custom_field_cf2"] == false
    end

    test "user selection overrides custom field visibility" do
      user_selection = %{"custom_field_cf1" => false}
      settings = %{member_field_visibility: %{}}

      custom_fields = [
        create_custom_field("cf1", "Custom 1", true)
      ]

      result = FieldVisibility.merge_with_global_settings(user_selection, settings, custom_fields)

      assert result["custom_field_cf1"] == false
    end

    test "handles string keys in settings (JSONB format)" do
      user_selection = %{}
      settings = %{member_field_visibility: %{"first_name" => false, "email" => true}}
      custom_fields = []

      result = FieldVisibility.merge_with_global_settings(user_selection, settings, custom_fields)

      assert result["first_name"] == false
      assert result["email"] == true
    end

    test "handles mixed atom and string keys in settings" do
      user_selection = %{}
      # Use string keys only (as JSONB would return)
      settings = %{member_field_visibility: %{"first_name" => false, "email" => true}}
      custom_fields = []

      result = FieldVisibility.merge_with_global_settings(user_selection, settings, custom_fields)

      assert result["first_name"] == false
      assert result["email"] == true
    end

    test "handles nil settings gracefully" do
      user_selection = %{}
      settings = %{member_field_visibility: nil}
      custom_fields = []

      result = FieldVisibility.merge_with_global_settings(user_selection, settings, custom_fields)

      # Should default all fields to true
      assert result["first_name"] == true
      assert result["email"] == true
    end

    test "handles missing member_field_visibility key" do
      user_selection = %{}
      settings = %{}
      custom_fields = []

      result = FieldVisibility.merge_with_global_settings(user_selection, settings, custom_fields)

      # Should default all fields to true
      assert result["first_name"] == true
      assert result["email"] == true
    end

    test "includes all fields in result" do
      user_selection = %{"first_name" => false}
      settings = %{member_field_visibility: %{email: true}}

      custom_fields = [
        create_custom_field("cf1", "Custom 1", true)
      ]

      result = FieldVisibility.merge_with_global_settings(user_selection, settings, custom_fields)

      # Should include all member fields
      member_fields = Mv.Constants.member_fields()

      Enum.each(member_fields, fn field ->
        assert Map.has_key?(result, Atom.to_string(field))
      end)

      # Should include custom fields
      assert Map.has_key?(result, "custom_field_cf1")
    end
  end

  describe "get_visible_fields/1" do
    test "returns only fields with true visibility" do
      selection = %{
        "first_name" => true,
        "email" => false,
        "street" => true,
        "custom_field_123" => false
      }

      result = FieldVisibility.get_visible_fields(selection)

      assert :first_name in result
      assert :street in result
      refute :email in result
      refute "custom_field_123" in result
    end

    test "converts member field strings to atoms" do
      selection = %{"first_name" => true, "email" => true}

      result = FieldVisibility.get_visible_fields(selection)

      assert :first_name in result
      assert :email in result
    end

    test "keeps custom fields as strings" do
      selection = %{"custom_field_abc-123" => true}

      result = FieldVisibility.get_visible_fields(selection)

      assert "custom_field_abc-123" in result
    end

    test "handles empty selection" do
      assert FieldVisibility.get_visible_fields(%{}) == []
    end

    test "handles all fields hidden" do
      selection = %{"first_name" => false, "email" => false}

      assert FieldVisibility.get_visible_fields(selection) == []
    end

    test "handles invalid input" do
      assert FieldVisibility.get_visible_fields(nil) == []
    end
  end

  describe "get_visible_member_fields/1" do
    test "returns only member fields that are visible" do
      selection = %{
        "first_name" => true,
        "email" => true,
        "custom_field_123" => true,
        "street" => false
      }

      result = FieldVisibility.get_visible_member_fields(selection)

      assert :first_name in result
      assert :email in result
      refute :street in result
      refute "custom_field_123" in result
    end

    test "filters out custom fields" do
      selection = %{
        "first_name" => true,
        "custom_field_123" => true,
        "custom_field_456" => true
      }

      result = FieldVisibility.get_visible_member_fields(selection)

      assert :first_name in result
      refute "custom_field_123" in result
      refute "custom_field_456" in result
    end

    test "handles empty selection" do
      assert FieldVisibility.get_visible_member_fields(%{}) == []
    end

    test "handles invalid input" do
      assert FieldVisibility.get_visible_member_fields(nil) == []
    end
  end

  describe "get_visible_custom_fields/1" do
    test "returns only custom fields that are visible" do
      selection = %{
        "first_name" => true,
        "custom_field_123" => true,
        "custom_field_456" => false,
        "email" => true
      }

      result = FieldVisibility.get_visible_custom_fields(selection)

      assert "custom_field_123" in result
      refute "custom_field_456" in result
      refute :first_name in result
      refute :email in result
    end

    test "filters out member fields" do
      selection = %{
        "first_name" => true,
        "email" => true,
        "custom_field_123" => true
      }

      result = FieldVisibility.get_visible_custom_fields(selection)

      assert "custom_field_123" in result
      refute :first_name in result
      refute :email in result
    end

    test "handles empty selection" do
      assert FieldVisibility.get_visible_custom_fields(%{}) == []
    end

    test "handles fields that look like custom fields but aren't" do
      selection = %{
        "custom_field_123" => true,
        "custom_field_like_name" => true,
        "not_custom_field" => true
      }

      result = FieldVisibility.get_visible_custom_fields(selection)

      assert "custom_field_123" in result
      assert "custom_field_like_name" in result
      refute "not_custom_field" in result
    end

    test "handles invalid input" do
      assert FieldVisibility.get_visible_custom_fields(nil) == []
    end
  end
end
