# frozen_string_literal: true

require "dry/core/equalizer"
require_relative "decorated_attributes"

module Dry
  class View
    # Provides a baseline environment across all the templates, parts and scopes
    # in a given rendering.
    #
    # @abstract Subclass this and add your own methods (along with a custom
    #   `#initialize` if you wish to inject dependencies)
    #
    # @api public
    class Context
      include Dry::Equalizer(:_options)
      include DecoratedAttributes

      attr_reader :_render_env, :_options

      # Returns a new instance of Context
      #
      # In subclasses, you should include an `**options` parameter and pass _all
      # arguments_ to `super`. This allows Context to make copies of itself
      # while preserving your dependencies.
      #
      # @example
      #   class MyContext < Dry::View::Context
      #     # Injected dependency
      #     attr_reader :assets
      #
      #     def initialize(assets:, **options)
      #       @assets = assets
      #       super
      #     end
      #   end
      #
      # @api public
      def initialize(render_env: nil, **options)
        @_render_env = render_env
        @_options = options
      end

      # @api private
      def for_render_env(render_env)
        return self if render_env == _render_env

        self.class.new(**_options.merge(render_env: render_env))
      end

      # Returns a copy of the Context with new options merged in.
      #
      # This may be useful to supply values for dependencies that are _optional_
      # when initializing your custom Context subclass.
      #
      # @example
      #   class MyContext < Dry::View::Context
      #     # Injected dependencies (request is optional)
      #     attr_reader :assets, :request
      #
      #     def initialize(assets:, request: nil, **options)
      #       @assets = assets
      #       @request = reuqest
      #       super
      #     end
      #   end
      #
      #   my_context = MyContext.new(assets: assets)
      #   my_context_with_request = my_context.with(request: request)
      #
      # @api public
      def with(**new_options)
        self.class.new(
          render_env: _render_env,
          **_options.merge(new_options)
        )
      end
    end
  end
end
