# frozen_string_literal: true

module Bridgetown
  module Utils
    module RubyExec
      extend self

      # TODO: Deprecate storing Ruby code in YAML, Rb, etc. and just use native Ruby Front Matter
      def search_data_for_ruby_code(convertible, renderer) # rubocop:todo Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
        return if convertible.data.empty?

        # Iterate using `keys` here so inline Ruby script can add new data keys
        # if necessary without an error
        data_keys = convertible.data.keys
        data_keys.each do |k|
          v = convertible.data[k]
          next unless v.is_a?(Rb) || v.is_a?(Hash) || v.is_a?(Proc)

          if v.is_a?(Proc)
            convertible.data[k] = convertible.instance_exec(&v)
          elsif v.is_a?(Hash)
            v.each do |nested_k, nested_v|
              next unless nested_v.is_a?(Rb)

              convertible.data[k][nested_k] = run(nested_v, convertible, renderer)
            end
          else
            convertible.data[k] = run(v, convertible, renderer)
          end
        end
      end

      # Sets up a new context in which to eval Ruby coming from front matter.
      #
      # ruby_code - a string of code
      # convertible - the Document/Page/Layout with the Ruby front matter
      # renderer - the Renderer instance that's processing the document (optional)
      #
      # Returns the transformed output of the code
      def run(ruby_code, convertible, renderer)
        return unless ruby_code.is_a?(Rb)

        klass = Class.new
        obj = klass.new

        if convertible.is_a?(Layout)
          klass.attr_accessor :layout, :site, :data
          obj.layout = convertible
        else
          klass.attr_accessor :document, :page, :renderer, :site, :data
          obj.document = obj.page = convertible
          obj.renderer = renderer
        end
        obj.site = convertible.site
        obj.data = convertible.data

        # This is where the magic happens! DON'T BE EVIL!!! ;-)
        output = obj.instance_eval(ruby_code)
        output.is_a?(Hash) ? output.with_dot_access : output
      end
    end
  end
end
