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

require 'contrast/api/dtm.pb'
require 'contrast/utils/string_utils'
require 'contrast/components/logger'

module Contrast
  module Agent
    module Reporting
      # This is the new Library Discovery class which will include all the needed information for the new
      # reporting system to relay this information in the Application Update messages. These libraries are used by
      # TeamServer to construct the dependency information for the SCA feature. They represent those gems that are
      # included in the GemSpec, not necessarily those that have had files in them required.
      #
      # @attr_reader class_count [Integer] the number of files in the Gem which contain code.
      # @attr_reader external_date [Integer] the time, in ms, when the Gem was published. Required for reporting.
      # @attr_reader file [String] the name of the Gem. Required for reporting.
      # @attr_reader hash [String] the Sha256 of the Gem, matching its hash in RubyGems. Required for reporting.
      # @attr_reader internal_date [Integer] the time, in ms, when the Gem was published. Required for reporting.
      # @attr_accessor manifest [String] the YAML form of the Gem's specification.
      # @attr_reader tags [String] Inventory tags set by the user via configuration.
      # @attr_reader url [String] The homepage of the Gem.
      # @attr_reader version [String] The version of the Gem.
      class LibraryDiscovery
        include Contrast::Components::Logger::InstanceMethods

        StringUtils = Contrast::Utils::StringUtils

        # required attributes
        attr_reader :external_date, :file, :hash, :internal_date
        # optional attributes
        attr_reader :class_count, :tags, :url, :version
        attr_accessor :manifest

        def initialize digest, spec
          @file = StringUtils.force_utf8(spec.name) # rubocop:disable Security/Module/Name
          @hash = StringUtils.force_utf8(digest)
          @version     = StringUtils.force_utf8(spec.version)
          @manifest    = StringUtils.force_utf8(StringUtils.force_utf8(spec.to_yaml.to_s))
          @external_date = (spec.date.to_f * 1000.0).to_i
          @internal_date = @external_date
          @url         = StringUtils.force_utf8(spec.homepage)
          @class_count = Contrast::Utils::Sha256Builder.instance.files(spec.full_gem_path.to_s).length
          @tags = Contrast::INVENTORY.tags
        end

        # Convert the instance variables on the class, and other information, into the identifiers required for
        # TeamServer to process the JSON form of this message.
        #
        # @return [Hash]
        # @raise [ArgumentError]
        def to_controlled_hash
          begin
            validate
          rescue ArgumentError => e
            logger.error('LibraryDiscovery validation failed with: ', e)
            return
          end

          {
              classCount: class_count,
              externalDate: external_date,
              file: file,
              hash: hash,
              internalDate: internal_date,
              manifest: manifest,
              url: url,
              version: version,
              tags: tags
          }.compact
        end

        # Ensure the required fields are present.
        #
        # @raise [ArgumentError]
        def validate
          unless external_date
            raise(ArgumentError, "#{ self } did not have a proper external date. Unable to continue.")
          end
          unless internal_date
            raise(ArgumentError, "#{ self } did not have a proper internal date. Unable to continue.")
          end
          raise(ArgumentError, "#{ self } did not have a proper file. Unable to continue.") unless file
          raise(ArgumentError, "#{ self } did not have a proper hash. Unable to continue.") unless hash
        end
      end
    end
  end
end