# frozen_string_literal: true

module Litany

  # This mixin can be included into any resource in order to make it a Stack.  Stacks are top level constructs inside of Litany and CloudFormation.
  # A stack represents your deployable unit of infrastructure.
  module Stack
    def compile_stack
      config_segments = [{ AWSTemplateFormatVersion: '2010-09-09' }]
      all_resources.select { |resource| resource.is_a?(Resource) }.each { |resource| config_segments << resource.compile }
      config_segments.reduce(:deep_merge!)
    end

    # Finalizes the stack, ensuring that it is ready for compilation.
    # @return [void]
    def finalize_stack
      raise "A stack must have a reference to an :environment, :environments, or a :vpc. #{self.class.inspect} does not." unless respond_to?(:vpc) || respond_to?(:environment) || respond_to?(:environments)
      raise "If you declare :only_in references you must have a :vpc reference. #{self.class.inspect} does not." if respond_to?(:only_ins) && !respond_to?(:vpc)

      if respond_to?(:environments)
        environment(default_environment) unless set_environments?
      elsif respond_to?(:environment)
        environment(default_environment) unless set_environment?
      end
    end

    # Tests if this stack is in the active environment.
    # @return [Boolean]
    def in_active_environment?
      target_environments.include?(active_environment)
    end

    # Tests if this stack is targeted against multiple environments
    # @return [Boolean]
    def multi_region?
      target_environments.count > 1
    end

    # Returns a self-memoized list of targeted environments.  Calling this and then adding to a list of environments is considered a invalid process.
    # @todo Protect against altering the list of targeted environments after first calling this.
    # @return [Array<Environment>]
    def target_environments
      return @target_environments if instance_variable_defined?(:@target_environments)

      @target_environments ||= environments if respond_to?(:environments)
      @target_environments ||= [environment] if respond_to?(:environment)
      @target_environments ||= vpc.environments if respond_to?(:vpc)

      @target_environments &= only_ins if respond_to?(:only_ins) && set_only_ins? # Take the intersection!

      @target_environments
    end

    # Runs validation on all resources in this stack.
    # @return [Boolean]
    def validate_stack
      all_resources.collect(&:run_validation).all?
    end

    # Returns a list of this resource and all of it's children.
    # @return [Array<MetaResource>]
    private def all_resources
      [self] + project.gather_child_resources(self)
    end
  end
end
