defmodule MvWeb.MemberLive.Index.FieldSelectionTest do
  @moduledoc """
  Tests for FieldSelection module handling cookie/session/URL management.
  """
  use ExUnit.Case, async: true

  alias MvWeb.MemberLive.Index.FieldSelection

  describe "get_from_session/1" do
    test "returns empty map when session is empty" do
      assert FieldSelection.get_from_session(%{}) == %{}
    end

    test "returns empty map when session key is missing" do
      session = %{"other_key" => "value"}
      assert FieldSelection.get_from_session(session) == %{}
    end

    test "parses valid JSON from session" do
      json = Jason.encode!(%{"first_name" => true, "email" => false})
      session = %{"member_field_selection" => json}

      result = FieldSelection.get_from_session(session)

      assert result == %{"first_name" => true, "email" => false}
    end

    test "handles invalid JSON gracefully" do
      session = %{"member_field_selection" => "invalid json{["}

      result = FieldSelection.get_from_session(session)

      assert result == %{}
    end

    test "converts non-boolean values to true" do
      json = Jason.encode!(%{"first_name" => "true", "email" => 1, "street" => true})
      session = %{"member_field_selection" => json}

      result = FieldSelection.get_from_session(session)

      # All values should be booleans, non-booleans default to true
      assert result["first_name"] == true
      assert result["email"] == true
      assert result["street"] == true
    end

    test "handles nil session" do
      assert FieldSelection.get_from_session(nil) == %{}
    end

    test "handles non-map session" do
      assert FieldSelection.get_from_session("not a map") == %{}
    end
  end

  describe "save_to_session/2" do
    test "saves field selection to session as JSON" do
      session = %{}
      selection = %{"first_name" => true, "email" => false}

      result = FieldSelection.save_to_session(session, selection)

      assert Map.has_key?(result, "member_field_selection")
      assert Jason.decode!(result["member_field_selection"]) == selection
    end

    test "overwrites existing selection" do
      session = %{"member_field_selection" => Jason.encode!(%{"old" => true})}
      selection = %{"new" => true}

      result = FieldSelection.save_to_session(session, selection)

      assert Jason.decode!(result["member_field_selection"]) == selection
    end

    test "handles empty selection" do
      session = %{}
      selection = %{}

      result = FieldSelection.save_to_session(session, selection)

      assert Jason.decode!(result["member_field_selection"]) == %{}
    end

    test "handles invalid selection gracefully" do
      session = %{}

      result = FieldSelection.save_to_session(session, "not a map")

      assert result == session
    end
  end

  describe "get_from_cookie/1" do
    test "returns empty map when cookie header is missing" do
      conn = %Plug.Conn{}

      result = FieldSelection.get_from_cookie(conn)

      assert result == %{}
    end

    test "returns empty map when cookie is empty string" do
      conn = Plug.Conn.put_req_header(%Plug.Conn{}, "cookie", "")

      result = FieldSelection.get_from_cookie(conn)

      assert result == %{}
    end

    test "parses valid JSON from cookie" do
      selection = %{"first_name" => true, "email" => false}
      cookie_value = selection |> Jason.encode!() |> URI.encode()
      cookie_header = "member_field_selection=#{cookie_value}"
      conn = %Plug.Conn{} |> Plug.Conn.put_req_header("cookie", cookie_header)

      result = FieldSelection.get_from_cookie(conn)

      assert result == selection
    end

    test "handles invalid JSON in cookie gracefully" do
      cookie_value = URI.encode("invalid{[")
      cookie_header = "member_field_selection=#{cookie_value}"
      conn = %Plug.Conn{} |> Plug.Conn.put_req_header("cookie", cookie_header)

      result = FieldSelection.get_from_cookie(conn)

      assert result == %{}
    end

    test "handles cookie with other values" do
      selection = %{"street" => true}
      cookie_value = selection |> Jason.encode!() |> URI.encode()
      cookie_header = "other_cookie=value; member_field_selection=#{cookie_value}; another=test"
      conn = %Plug.Conn{} |> Plug.Conn.put_req_header("cookie", cookie_header)

      result = FieldSelection.get_from_cookie(conn)

      assert result == selection
    end
  end

  describe "save_to_cookie/2" do
    test "saves field selection to cookie" do
      conn = %Plug.Conn{}
      selection = %{"first_name" => true, "email" => false}

      result = FieldSelection.save_to_cookie(conn, selection)

      # Check that cookie is set
      assert result.resp_cookies["member_field_selection"]
      cookie = result.resp_cookies["member_field_selection"]
      assert cookie[:max_age] == 365 * 24 * 60 * 60
      assert cookie[:same_site] == "Lax"
      assert cookie[:http_only] == true
    end

    test "handles invalid selection gracefully" do
      conn = %Plug.Conn{}

      result = FieldSelection.save_to_cookie(conn, "not a map")

      assert result == conn
    end
  end

  describe "parse_from_url/1" do
    test "returns empty map when params is empty" do
      assert FieldSelection.parse_from_url(%{}) == %{}
    end

    test "returns empty map when fields parameter is missing" do
      params = %{"query" => "test", "sort_field" => "first_name"}
      assert FieldSelection.parse_from_url(params) == %{}
    end

    test "parses comma-separated field names" do
      params = %{"fields" => "first_name,email,street"}

      result = FieldSelection.parse_from_url(params)

      assert result == %{
               "first_name" => true,
               "email" => true,
               "street" => true
             }
    end

    test "handles custom field names" do
      params = %{"fields" => "custom_field_abc-123,custom_field_def-456"}

      result = FieldSelection.parse_from_url(params)

      assert result == %{
               "custom_field_abc-123" => true,
               "custom_field_def-456" => true
             }
    end

    test "handles mixed member and custom fields" do
      params = %{"fields" => "first_name,custom_field_123,email"}

      result = FieldSelection.parse_from_url(params)

      assert result == %{
               "first_name" => true,
               "custom_field_123" => true,
               "email" => true
             }
    end

    test "trims whitespace from field names" do
      params = %{"fields" => " first_name , email , street "}

      result = FieldSelection.parse_from_url(params)

      assert result == %{
               "first_name" => true,
               "email" => true,
               "street" => true
             }
    end

    test "handles empty fields string" do
      params = %{"fields" => ""}
      assert FieldSelection.parse_from_url(params) == %{}
    end

    test "handles nil fields parameter" do
      params = %{"fields" => nil}
      assert FieldSelection.parse_from_url(params) == %{}
    end

    test "filters out empty field names" do
      params = %{"fields" => "first_name,,email,"}

      result = FieldSelection.parse_from_url(params)

      assert result == %{
               "first_name" => true,
               "email" => true
             }
    end

    test "handles non-map params" do
      assert FieldSelection.parse_from_url(nil) == %{}
      assert FieldSelection.parse_from_url("not a map") == %{}
    end
  end

  describe "merge_sources/3" do
    test "merges all sources with URL having highest priority" do
      url_selection = %{"first_name" => false}
      session_selection = %{"first_name" => true, "email" => true}
      cookie_selection = %{"first_name" => true, "street" => true}

      result = FieldSelection.merge_sources(url_selection, session_selection, cookie_selection)

      # URL overrides session, session overrides cookie
      assert result["first_name"] == false
      assert result["email"] == true
      assert result["street"] == true
    end

    test "handles empty sources" do
      result = FieldSelection.merge_sources(%{}, %{}, %{})

      assert result == %{}
    end

    test "cookie only" do
      cookie_selection = %{"first_name" => true}

      result = FieldSelection.merge_sources(%{}, %{}, cookie_selection)

      assert result == %{"first_name" => true}
    end

    test "session overrides cookie" do
      session_selection = %{"first_name" => false}
      cookie_selection = %{"first_name" => true}

      result = FieldSelection.merge_sources(%{}, session_selection, cookie_selection)

      assert result["first_name"] == false
    end

    test "URL overrides everything" do
      url_selection = %{"first_name" => true}
      session_selection = %{"first_name" => false}
      cookie_selection = %{"first_name" => false}

      result = FieldSelection.merge_sources(url_selection, session_selection, cookie_selection)

      assert result["first_name"] == true
    end

    test "combines fields from all sources" do
      url_selection = %{"url_field" => true}
      session_selection = %{"session_field" => true}
      cookie_selection = %{"cookie_field" => true}

      result = FieldSelection.merge_sources(url_selection, session_selection, cookie_selection)

      assert result["url_field"] == true
      assert result["session_field"] == true
      assert result["cookie_field"] == true
    end
  end

  describe "to_url_param/1" do
    test "converts selection to comma-separated string" do
      selection = %{"first_name" => true, "email" => true, "street" => false}

      result = FieldSelection.to_url_param(selection)

      # Only visible fields should be included (order may vary)
      fields = String.split(result, ",") |> Enum.sort()
      assert fields == ["email", "first_name"]
    end

    test "handles empty selection" do
      assert FieldSelection.to_url_param(%{}) == ""
    end

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

      result = FieldSelection.to_url_param(selection)

      assert result == ""
    end

    test "preserves field order" do
      selection = %{
        "z_field" => true,
        "a_field" => true,
        "m_field" => true
      }

      result = FieldSelection.to_url_param(selection)

      # Order should be preserved (map iteration order)
      assert String.contains?(result, "z_field")
      assert String.contains?(result, "a_field")
      assert String.contains?(result, "m_field")
    end

    test "handles custom fields" do
      selection = %{
        "first_name" => true,
        "custom_field_abc-123" => true,
        "email" => false
      }

      result = FieldSelection.to_url_param(selection)

      assert String.contains?(result, "first_name")
      assert String.contains?(result, "custom_field_abc-123")
      refute String.contains?(result, "email")
    end

    test "handles invalid input" do
      assert FieldSelection.to_url_param(nil) == ""
      assert FieldSelection.to_url_param("not a map") == ""
    end
  end
end
