# frozen_string_literal: true

module Nanoc
  module Core
    # Provides a context and a binding for use in filters such as the ERB and
    # Haml ones.
    class Context
      # Creates a new context based off the contents of the hash.
      #
      # Each pair in the hash will be converted to an instance variable and an
      # instance method. For example, passing the hash `{ :foo => 'bar' }` will
      # cause `@foo` to have the value `"bar"`, and the instance method `#foo`
      # to return the same value `"bar"`.
      #
      # @param [Hash] hash A list of key-value pairs to make available
      #
      # @example Defining a context and accessing values
      #
      #     context = Nanoc::Core::Context.new(
      #       :name     => 'Max Payne',
      #       :location => 'in a cheap motel'
      #     )
      #     context.instance_eval do
      #       "I am #{name} and I am hiding #{@location}."
      #     end
      #     # => "I am Max Payne and I am hiding in a cheap motel."
      def initialize(hash)
        hash.each_pair do |key, value|
          instance_variable_set("@#{key}", value)
        end
      end

      # Returns a binding for this instance.
      #
      # @return [Binding] A binding for this instance
      # rubocop:disable Naming/AccessorMethodName
      def get_binding
        binding
      end
      # rubocop:enable Naming/AccessorMethodName

      def method_missing(method, *args, &)
        ivar_name = "@#{method}"
        if instance_variable_defined?(ivar_name)
          instance_variable_get(ivar_name)
        else
          super
        end
      end

      def respond_to_missing?(method, include_all)
        ivar_name = "@#{method}"

        valid_ivar_name =
          if defined?(Contracts)
            ivar_name =~ /\A@[A-Za-z_]+\z/
          else
            true # probably good enough
          end

        (valid_ivar_name && instance_variable_defined?(ivar_name)) || super
      end

      def include(mod)
        metaclass = class << self; self; end
        metaclass.instance_eval { include(mod) }
      end
    end
  end
end
