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

require 'json'
require 'net/http'
require 'contrast/components/logger'
require 'contrast/utils/net_http_base'
require 'contrast/api/communication/connection_status'
require 'contrast/agent/reporting/reporting_utilities/response_handler'
require 'contrast/agent/reporting/reporting_utilities/reporter_client_utils'
require 'contrast/agent/reporting/reporting_utilities/endpoints'
require 'contrast/agent/reporting/reporting_utilities/headers'

module Contrast
  module Agent
    module Reporting
      # This class creates a Net::HTTP client and initiates a connection to the provided result
      # @attr_reader headers [Contrast::Agent::Reporting::Headers]
      class ReporterClient < Contrast::Utils::NetHttpBase
        attr_reader :headers

        include Contrast::Agent::Reporting::Endpoints
        include Contrast::Agent::Reporting::ReporterClientUtils
        include Contrast::Components::Logger::InstanceMethods
        SERVICE_NAME = 'Reporter'
        def initialize
          @headers = Contrast::Agent::Reporting::Headers.new
          super()
        end

        # This method initializes the Net::HTTP client we'll need. it will validate
        # the connection and make the first request. If connection is valid and response
        # is available then the open connection is returned.
        #
        # @return [Net::HTTP, nil] Return open connection or nil
        def initialize_connection
          # for this client we would use proxy and custom certificate file if available
          super(SERVICE_NAME, Contrast::API.api_url, use_proxy: true, use_custom_cert: true)
        end

        # Start the client for first time and sent startup event
        #
        # @param connection [Net::HTTP] open connection
        def startup! connection
          return if status.startup_messages_sent?
          return unless Contrast::Agent::Reporter.enabled?

          send_agent_startup(connection)
        end

        # Check event type and send it to appropriate TS endpoint
        #
        # @param event [Contrast::Agent::Reporting::ReportingEvent] The event to send to TeamServer. Really a
        #   child of the ReportingEvent rather than a literal one.
        # @param connection [Net::HTTP] open connection
        # @param send_immediately [Boolean] flag for the logger
        # @return response [Net::HTTP::Response, nil] response from TS if no response
        def send_event event, connection, send_immediately: false
          return unless Contrast::Agent::Reporter.enabled?
          return unless connection

          log_send_event(event) if send_immediately
          request = build_request(event)
          response = connection.request(request)
          audit&.audit_event(event, response) if ::Contrast::API.request_audit_enable?
          process_settings_response(response)
          process_preflight_response(event, response, connection)
          response
        rescue StandardError => e
          handle_error(event, e)
        end

        def status
          @_status ||= Contrast::Api::Communication::ConnectionStatus.new
        end

        def response_handler
          @_response_handler ||= Contrast::Agent::Reporting::ResponseHandler.new
        end

        def sleep?
          response_handler.sleep?
        end

        def timeout
          response_handler.timeout
        end

        def mode
          response_handler.mode
        end

        def reset_mode
          response_handler.reset_mode
        end

        def wake_up
          response_handler.wake_up
        end
      end
    end
  end
end