# Copyright (c) 2020 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details.
# frozen_string_literal: true

# rubocop:disable Style/MethodMissingSuper, Style/MissingRespondToMissing
module Contrast
  # A wrapper build around the Common Agent Configuration project to allow for
  # access of the values contained in its parent_configuration_spec.yaml
  class CommonAgentConfiguration
    # The CAC spec, deserialized to a hash.

    SPEC       = 'spec'
    NODES      = 'nodes'
    PROPERTIES = 'properties'

    def initialize hsh
      @hsh = hsh[SPEC]
    end

    # Used to indicate those sections of the configuration which have
    # references to other nodes or properties, allowing for the parsing of and
    # access to the nested configuration structure.
    module IsANode
      def children
        hsh[NODES]&.map { |raw_node| Node.new(raw_node) } || []
      end

      def properties
        hsh[PROPERTIES].map { |raw_property| Property.new(raw_property) }
      end

      def lookup *path
        # Path will be N args, representing a path of nodes.
        path.reduce(self) do |node, next_arg|
          # If we can travel to a node by that name, do that.
          candidate_node = node.children.find { |n| n.name == next_arg }
          next candidate_node if candidate_node

          # If there's a property, dereference that.
          candidate_property = node.properties.find { |n| n.name == next_arg }
          next candidate_property if candidate_property

          raise IndexError, "couldn't traverse path:\t#{ path.join(Contrast::Utils::ObjectShare::PERIOD) }"
        end
      end

      def method_missing method, *args, &block
        if args.any?
          lookup(method.to_s).public_send(args, block)
        else
          lookup(method.to_s)
        end
      rescue IndexError
        super(method, args, block)
      end
    end

    # Used to indicate those sections of the configuration which are for a
    # single property, allowing for the parsing of and access to the
    # information describing the property.
    module IsAProperty
      attr_reader :hsh

      def initialize hsh
        @hsh = hsh
      end

      %w[name default description required_languages display].each do |field|
        define_method(field) { hsh[field].dup }
      end
    end

    include IsANode
    include IsAProperty

    # A Property in the Common Agent Configuration
    class Property
      include IsAProperty
    end

    # A Node in the Common Agent Configuration
    class Node < Property
      include IsANode
    end
  end
end
# rubocop:enable Style/MethodMissingSuper, Style/MissingRespondToMissing