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

require 'contrast/agent/telemetry/base'
require 'contrast/utils/telemetry_identifier'
require 'contrast/config/env_variables'

module Contrast
  module Utils
    # Tools for supporting the Telemetry feature
    module Telemetry
      DIR = '/ect/contrast/ruby-agent/'.cs__freeze
      FILE = '.telemetry'.cs__freeze
      CURRENT_DIR = Dir.pwd.cs__freeze
      CONFIG_DIR = CURRENT_DIR + '/config/contrast/'.cs__freeze
      MESSAGE = {
          disclaimer:
            "\n===================================================================================================" \
            "\n\nThe [Contrast Security] [Ruby Agent] " \
            "collects usage data in order to help us improve compatibility\n" \
            "and security coverage. The data is anonymous and does not contain application data. It is collected\n" \
            "by Contrast and is never shared. You can opt-out of telemetry by setting the\n" \
            "'CONTRAST_AGENT_TELEMETRY_OPTOUT' environment variable to '1' or 'true'.\n" \
            'Read more about [Contrast Security] [Ruby Agent] telemetry: ' \
            "https://docs.contrastsecurity.com/en/ruby-telemetry.html \n\n" \
            "===================================================================================================\n\n"
      }.cs__freeze

      # Here we create the .telemetry file. If the file exist we do nothing.
      #
      # @return[Boolean, nil] true if file is created, false if file already exist
      # and nil if Telemetry is disabled or on unsupported OS.
      def self.create_telemetry_file
        write_mark_file(DIR, FILE, CONFIG_DIR)
      end

      # The telemetry disclaimer message, set if needed.
      #
      # @return [String]
      def self.disclaimer
        @_disclaimer = MESSAGE[:disclaimer]
      end

      # Determine if telemetry has been explicitly disabled by opt out or has been left running.
      #
      # @return [Boolean]
      def self.exceptions_enabled?
        @_exceptions_enabled = telemetry_exceptions_enabled? if @_exceptions_enabled.nil?
        @_exceptions_enabled
      end

      class << self
        include Contrast::Config::EnvVariables

        private

        # Create the mark file
        #
        # @param dir [String] Directory in which the file is to be created
        # @param file [String] filename of the mark file
        # @param config_dir [String] path for the config folder in working directory
        # @return[Boolean, nil] true if file is created, false if file already exist
        # and nil if Telemetry is disabled or on unsupported OS.
        def write_mark_file dir, file, config_dir
          return unless Contrast::Agent::Telemetry::Base.enabled?
          return if Contrast::Utils::OS.windows?

          @dir = dir
          # After macOS Catalina, you can no longer store files or data in the read-only system volume,
          # nor can we write to the "root" directory ( / ). This results in Errno::EROFS exception.
          # So for the MacOS we would use the config directory of the instrumented application.
          @dir = config_dir if Contrast::Utils::OS.mac?
          return false if File.file?(@dir + file)
          return true if touch_marker(@dir, file)

          # If we don't have permission to write to root directory use config instead
          touch_marker(config_dir, file)
        end

        # Touches .telemetry file
        #
        # @return[Boolean] true if success, false if fails
        def touch_marker dir, file
          FileUtils.mkdir_p(dir) unless Dir.exist?(dir)
          FileUtils.touch(dir + file)
          File.file?(dir + file)
        rescue StandardError => _e
          false
        end

        # Determine if telemetry has been explicitly disabled by opt out or has been left running.
        #
        # @return [Boolean]
        def telemetry_exceptions_enabled?
          opts_out_telemetry = return_value(:telemetry_opt_outs).to_s
          return false if opts_out_telemetry.casecmp?('true') || opts_out_telemetry == '1'

          true
        end
      end
    end
  end
end