# frozen_string_literal: true

module Projects
  module Alerting
    class NotifyService < BaseService
      include Gitlab::Utils::StrongMemoize
      include ::IncidentManagement::Settings

      def execute(token)
        return bad_request unless valid_payload_size?
        return forbidden unless alerts_service_activated?
        return unauthorized unless valid_token?(token)

        process_alert
        return bad_request unless alert.persisted?

        process_incident_issues if process_issues?
        send_alert_email if send_email?

        ServiceResponse.success
      end

      private

      delegate :alerts_service, :alerts_service_activated?, to: :project

      def process_alert
        if alert.persisted?
          process_existing_alert
        else
          create_alert
        end
      end

      def process_existing_alert
        if incoming_payload.ends_at.present?
          process_resolved_alert
        else
          alert.register_new_event!
        end

        alert
      end

      def process_resolved_alert
        return unless auto_close_incident?

        if alert.resolve(incoming_payload.ends_at)
          close_issue(alert.issue)
        end

        alert
      end

      def close_issue(issue)
        return if issue.blank? || issue.closed?

        ::Issues::CloseService
          .new(project, User.alert_bot)
          .execute(issue, system_note: false)

        SystemNoteService.auto_resolve_prometheus_alert(issue, project, User.alert_bot) if issue.reset.closed?
      end

      def create_alert
        return unless alert.save

        alert.execute_services
        SystemNoteService.create_new_alert(
          alert,
          alert.monitoring_tool || 'Generic Alert Endpoint'
        )
      end

      def process_incident_issues
        return if alert.issue

        ::IncidentManagement::ProcessAlertWorker.perform_async(nil, nil, alert.id)
      end

      def send_alert_email
        notification_service
          .async
          .prometheus_alerts_fired(project, [alert.attributes])
      end

      def alert
        strong_memoize(:alert) do
          existing_alert || new_alert
        end
      end

      def existing_alert
        return unless incoming_payload.gitlab_fingerprint

        AlertManagement::Alert.not_resolved.for_fingerprint(project, incoming_payload.gitlab_fingerprint).first
      end

      def new_alert
        AlertManagement::Alert.new(**incoming_payload.alert_params, ended_at: nil)
      end

      def incoming_payload
        strong_memoize(:incoming_payload) do
          Gitlab::AlertManagement::Payload.parse(project, params.to_h)
        end
      end

      def valid_payload_size?
        Gitlab::Utils::DeepSize.new(params).valid?
      end

      def valid_token?(token)
        token == alerts_service.token
      end

      def bad_request
        ServiceResponse.error(message: 'Bad Request', http_status: :bad_request)
      end

      def unauthorized
        ServiceResponse.error(message: 'Unauthorized', http_status: :unauthorized)
      end

      def forbidden
        ServiceResponse.error(message: 'Forbidden', http_status: :forbidden)
      end
    end
  end
end
