# frozen_string_literal: true

module Integrations
  class GitlabSlackApplication < BaseSlackNotification
    attribute :alert_events, default: false
    attribute :commit_events, default: false
    attribute :confidential_issues_events, default: false
    attribute :confidential_note_events, default: false
    attribute :deployment_events, default: false
    attribute :issues_events, default: false
    attribute :job_events, default: false
    attribute :merge_requests_events, default: false
    attribute :note_events, default: false
    attribute :pipeline_events, default: false
    attribute :push_events, default: false
    attribute :tag_push_events, default: false
    attribute :vulnerability_events, default: false
    attribute :wiki_page_events, default: false

    has_one :slack_integration, foreign_key: :integration_id, inverse_of: :integration
    delegate :bot_access_token, :bot_user_id, to: :slack_integration, allow_nil: true

    def update_active_status
      update(active: !!slack_integration)
    end

    def title
      s_('Integrations|GitLab for Slack app')
    end

    def description
      s_('Integrations|Enable slash commands and notifications for a Slack workspace.')
    end

    def self.to_param
      'gitlab_slack_application'
    end

    override :show_active_box?
    def show_active_box?
      false
    end

    override :test
    def test(_data)
      failures = test_notification_channels

      { success: failures.blank?, result: failures }
    end

    # The form fields of this integration are editable only after the Slack App installation
    # flow has been completed, which causes the integration to become activated/enabled.
    override :editable?
    def editable?
      activated?
    end

    override :fields
    def fields
      return [] unless editable?

      super
    end

    override :sections
    def sections
      return [] unless editable?

      [
        {
          type: SECTION_TYPE_TRIGGER,
          title: s_('Integrations|Trigger'),
          description: s_('Integrations|An event will be triggered when one of the following items happen.')
        },
        {
          type: SECTION_TYPE_CONFIGURATION,
          title: s_('Integrations|Notification settings'),
          description: s_('Integrations|Configure the scope of notifications.')
        }
      ]
    end

    override :configurable_events
    def configurable_events
      return [] unless editable?

      super
    end

    override :requires_webhook?
    def requires_webhook?
      false
    end

    def upgrade_needed?
      slack_integration.present? && slack_integration.upgrade_needed?
    end

    private

    override :notify
    def notify(message, opts)
      channels = Array(opts[:channel])
      return false if channels.empty?

      payload = {
        attachments: message.attachments,
        text: message.pretext,
        unfurl_links: false,
        unfurl_media: false
      }

      successes = channels.map do |channel|
        notify_slack_channel!(channel, payload)
      end

      successes.any?
    end

    def notify_slack_channel!(channel, payload)
      response = api_client.post(
        'chat.postMessage',
        payload.merge(channel: channel)
      )

      log_error('Slack API error when notifying', api_response: response.parsed_response) unless response['ok']

      response['ok']
    rescue *Gitlab::HTTP::HTTP_ERRORS => e
      Gitlab::ErrorTracking.track_and_raise_for_dev_exception(e,
        {
          integration_id: id,
          slack_integration_id: slack_integration.id
        }
      )

      false
    end

    def api_client
      @slack_api ||= ::Slack::API.new(slack_integration)
    end

    def test_notification_channels
      return if unique_channels.empty?
      return s_('Integrations|GitLab for Slack app must be reinstalled to enable notifications') unless bot_access_token

      test_payload = {
        text: 'Test',
        user: bot_user_id
      }

      not_found_channels = unique_channels.first(10).select do |channel|
        test_payload[:channel] = channel

        response = ::Slack::API.new(slack_integration).post('chat.postEphemeral', test_payload)
        response['error'] == 'channel_not_found'
      end

      return if not_found_channels.empty?

      format(
        s_(
          'Integrations|Unable to post to %{channel_list}, ' \
          'please add the GitLab Slack app to any private Slack channels'
        ),
        channel_list: not_found_channels.to_sentence
      )
    end

    override :metrics_key_prefix
    def metrics_key_prefix
      'i_integrations_gitlab_for_slack_app'
    end
  end
end
