# frozen_string_literal: true

require "rom/changeset/pipe_registry"

module ROM
  class Changeset
    # Composable data transformation pipe used by default in changesets
    #
    # @api private
    class Pipe < Dry::Transformer[PipeRegistry]
      extend Initializer

      define!(&:identity)

      param :processor, optional: true

      option :use_for_diff, optional: true, default: -> { true }
      option :diff_processor, default: -> { self[processor] }

      def self.new(*args, **opts)
        if args.empty?
          initialize(**opts)
        else
          super
        end
      end

      def self.initialize(**opts)
        transformer = allocate
        transformer.__send__(:initialize, dsl.(transformer), **opts)
        transformer
      end

      def self.[](name_or_proc)
        container[name_or_proc]
      end

      def [](*args)
        self.class[*args]
      end

      def bind(context)
        if processor.is_a?(Proc)
          bound_processor = self[-> *args { context.instance_exec(*args, &processor) }]
          bound_diff_processor = self[-> *args { context.instance_exec(*args, &diff_processor) }]

          new(bound_processor, diff_processor: bound_diff_processor)
        else
          self
        end
      end

      def compose(other, for_diff: other.is_a?(self.class) ? other.use_for_diff : false)
        new_proc = processor >> other

        if for_diff
          diff_proc = diff_processor >> (
            other.is_a?(self.class) ? other.diff_processor : other
          )

          new(new_proc, use_for_diff: true, diff_processor: diff_proc)
        else
          new(new_proc)
        end
      end
      alias_method :>>, :compose

      def call(data)
        processor.call(data)
      end

      def for_diff(data)
        use_for_diff ? diff_processor.call(data) : data
      end

      def new(processor, **opts)
        self.class.new(processor, **options, **opts)
      end
    end
  end
end
