# frozen_string_literal: true

module Decidim
  module Exporters
    # This class serves as a DSL to declaratively specify which artifacts are
    # exportable in a parent manifest.
    # A parent manifest is the manifest that will be used by the Serializer
    # to take the settings of the element to serialize. For example a parent
    # manifest may be a ParticipatorySpaceManifest or a ComponentManifest.
    #
    class ExportManifest
      include ActiveModel::Model
      include Decidim::AttributeObject::Model

      # A setting to choose if the collection exported by this manifest should
      # be included in the open data export available for all users.
      #
      # Optionally, you can define a specific serializer using the `open_data_serializer` method
      # to have a granular control on which fields are added to the open data export.
      # This is specially useful for ParticipatorySpaces, as the serializer is used for the
      # export/import feature, and we do not want to publish some of the data available there
      # (as the components settings).
      attribute :include_in_open_data, Boolean, default: false

      attr_reader :name, :manifest

      # Initializes the manifest.
      #
      # name - The name of the export artifact. It should be unique in the
      #        space or component.
      #
      # manifest - The parent manifest where this export manifest belongs to.
      #
      def initialize(name, manifest)
        super()
        @name = name.to_sym
        @manifest = manifest
      end

      # Public: Sets the +collection block+ when a block is given, or returns
      # the previously set +collection block+ if no block is provided.
      #
      # The +collection block+ knows how to obtain the collection of elements
      # to be serialized by the +Serializer+.
      #
      # The +collection block+ should be invoked like
      # `export.collection.call(artifact_type)` and, when evaluated,
      # will get passed an instance of the parent artifact type,
      # `Decidim::ParticipatorySpace` or `Decidim::Component` for example,
      # so you can easily find the elements to export.  It also receives, as a
      # second parameter, the user triggering the action, in case you need to
      # filter the collection based on the user.
      #
      # The +collection block+ in the end should return the collection of
      # elements to be serialized.
      #
      # &block - An optional block that returns the collection once evaluated.
      #
      # Returns the stored collection.
      def collection(&block)
        if block_given?
          @collection = block
        else
          @collection
        end
      end

      # Public: Sets the serializer when an argument is provided, returns the
      # stored serializer otherwise.
      #
      # A `Serializer` will be run against each and every element of the collection
      # in order to extract and process the relevant fields.
      #
      # serializer - A subclass of `Decidim::Exporters::Serializer`.
      #
      # Returns the stored serializer if previously stored, or
      # `Decidim::Exporters::Serializer` as a default implementation.
      def serializer(serializer = nil)
        @serializer ||= serializer || Decidim::Exporters::Serializer
      end

      # Public: Sets the open data serializer when an argument is provided, returns the
      # stored serializer otherwise.
      #
      # See more information in the `serializer` method.
      def open_data_serializer(serializer = nil)
        @open_data_serializer ||= serializer || nil
      end

      DEFAULT_FORMATS = %w(CSV JSON Excel).freeze

      # Public: Sets the available formats if an argument is provided and
      # loads the required exporters, returns the array with the default available
      # formats otherwise.
      #
      # The formats array is used to define which exporters are available
      # in the component. Each member of the array is a string with the name
      # of the exporter class that will instantiated when needed.
      #
      # formats - The array containing the available formats.
      #
      # Returns the stored formats if previously stored, or
      # the default formats array.
      def formats(formats = nil)
        load_exporters(formats)
        @formats ||= formats || DEFAULT_FORMATS
      end

      private

      # Private: Loads the given exporters when formats argument is provided.
      #
      # formats - The array containing the formats for which to load exporters.
      #
      def load_exporters(formats)
        formats&.each { |f| require "decidim/exporters/#{f.underscore}" }
      end
    end
  end
end
