module A9n
  class Loader
    attr_reader :scope, :env, :local_file, :example_file, :struct

    COMMON_NAMESPACE = 'defaults'.freeze
    KNOWN_NAMESPACES = [COMMON_NAMESPACE, 'development', 'test', 'staging', 'production'].freeze

    def initialize(file_path, scope, env)
      @scope = scope
      @env = ::A9n::StringInquirer.new(env.to_s)
      @local_file = file_path
      @example_file = "#{file_path}.example"
    end

    def get
      struct || load
    end

    def load
      local_config    = self.class.load_yml(local_file, scope, env)
      example_config  = self.class.load_yml(example_file, scope, env)

      ensure_data_presence!(local_config, example_config)
      ensure_keys_presence!(local_config, example_config)

      @struct = A9n::Struct.new(local_config || example_config)
    end

    class << self
      def load_yml(file_path, scope, env)
        return nil unless File.exist?(file_path)

        yml = A9n::YamlLoader.load(file_path)

        if no_known_namespaces?(yml)
          prepare_hash(yml, scope).freeze
        else
          common_namespace = prepare_hash(yml[COMMON_NAMESPACE], scope)
          env_namespace    = prepare_hash(yml[env], scope)

          A9n::Hash.merge(common_namespace, env_namespace).freeze
        end
      end

      def prepare_hash(data, scope)
        return nil unless data.is_a?(::Hash)

        A9n::Hash.deep_prepare(data, scope).freeze
      end

      def no_known_namespaces?(yml)
        (yml.keys & KNOWN_NAMESPACES).empty?
      end
    end

    private

    def ensure_data_presence!(local, example)
      return unless local.nil?
      return unless example.nil?

      raise A9n::MissingConfigurationDataError, "Configuration data for *#{env}* env was not found in neither *#{example}* nor *#{local}*"
    end

    def ensure_keys_presence!(local, example)
      return if local.nil?
      return if example.nil?

      missing_keys = example.keys - local.keys

      return if missing_keys.empty?

      raise A9n::MissingConfigurationVariablesError, "Following variables are missing in #{local_file} file: #{missing_keys.join(',')}"
    end
  end
end