# frozen_string_literal: true

module Litany
  class ResourceReference < Base

    attr_reader :source, :target, :attribute

    class << self
      def instances
        @instances ||= []
      end

      def new(*args, &block)
        instances << super(*args, &block)
        instances[-1]
      end
    end

    def initialize(source, target, attribute)
      @source = source
      @target = target
      @attribute = attribute
    end

    def finalize
      # target.output(attribute) if source.stack != target.stack
    end

    def to_json(*args)
      id_validators = [/[a-z]{1,4}-[0-9a-z]{8,17}/, /[A-Z0-9]{13,14}/, /^arn:aws:\w+?:(?:[\w-]+-\d)?:\d+:[\w-]+(?:\/[\w-]+)?$/]

      reference = case target
        when String
          unless id_validators.collect { |validator| validator.match(target) }.any? || target == '*'
            raise "Expected a String target to match one of `#{id_validators.inspect}`, received `#{target}`."
          end

          target
        else
          raise "You cannot pull a ref to target `#{target.inspect}`, must be a subclass of Resource." unless target.is_a?(Resource)

          output = target.class.outputs[attribute || :__ref__]

          if source.stack != target.stack
            { 'Fn::ImportValue': target.output_name(attribute) }
          elsif output&.complex?
            output.value_for(target)
          else
            attribute.nil? ? { Ref: target.resource_name } : { 'Fn::GetAtt': [target.resource_name, attribute] }
          end
      end

      reference.to_json(*args)
    end

    def to_s
      raise "This object shouldn't be turned into a string but instead ran through as a json object similar to a hash.  Raising because this is likely an error."
    end

    def inspect
      "Reference to resource: #{target.inspect} attribute: #{attribute}"
    end
  end
end

