# frozen_string_literal: true
module Api
  module V2
    class ApplicationSerializer < ActiveModel::Serializer
      def self.embeddable
        {}
      end

      def embeds
        # subclasses can override self.embeddable, and then it will whitelist
        # scope[:embeds] based on the contents. That way scope[:embeds] can just pull
        # from params and the whitelisting happens here
        @embeds ||= (scope[:embeds] || []).select { |e| self.class.embeddable.keys.include?(e) }
      end

      # self.embeddable might look like this:
      #   creator: { attr: :first_creator, serializer: UserSerializer }
      #   contributors: { serializer: UserSerializer}
      # This method will remove the :attr key if the underlying attribute name
      # is different than the name provided in the final json output. All other keys
      # in the hash will be passed to the ActiveModel::Serializer `attribute` method
      # directly (e.g. serializer in the examples will be passed).
      #
      # This setup means if you passed this self.embeddable config and sent no
      # ?embed= query param with your API request, you would get the regular attributes
      # plus creator_id and contributor_ids. If you passed ?embed=creator,contributors
      # then instead of an id and an array of ids, you would get a serialized user
      # (the first_creator) and an array of serialized users (the contributors).
      def self.embed_dat
        embeddable.each_pair do |key, opts|
          attr = opts.delete(:attr) || key
          if attr.to_s.pluralize == attr.to_s
            attribute("#{attr.to_s.singularize}_ids".to_sym,
                      opts.merge(unless: -> { embeds.include?(key) })) do
              Pundit.policy_scope(scope[:current_user], object.send(attr))&.map(&:id) || []
            end
            has_many(attr, opts.merge(if: -> { embeds.include?(key) })) do |serializer|
              list = Pundit.policy_scope(scope[:current_user], object.send(attr)) || []
              child_serializer = "Api::V2::#{attr.to_s.singularize.camelize}Serializer".constantize
              resource = ActiveModelSerializers::SerializableResource.new(
                list,
                each_serializer: child_serializer,
                scope: scope.merge(embeds: [])
              )
              resource.as_json
            end
          else
            id_opts = opts.merge(key: "#{key}_id")
            attribute("#{attr}_id".to_sym,
                      id_opts.merge(unless: -> { embeds.include?(key) }))
            attribute(key, opts.merge(if: -> { embeds.include?(key) })) do |serializer|
              object = serializer.object.send(key)
              child_serializer = "Api::V2::#{object.class.name}Serializer".constantize
              resource = ActiveModelSerializers::SerializableResource.new(
                object,
                serializer: child_serializer,
                scope: scope.merge(embeds: [])
              )
              resource.as_json
            end
          end
        end
      end
    end
  end
end