module Agents
  class SlackAgent < Agent
    DEFAULT_USERNAME = 'Huginn'
    ALLOWED_PARAMS = ['channel', 'username', 'unfurl_links', 'attachments', 'blocks']

    can_dry_run!
    cannot_be_scheduled!
    cannot_create_events!
    no_bulk_receive!

    gem_dependency_check { defined?(Slack) }

    description <<~MD
      The Slack Agent lets you receive events and send notifications to [Slack](https://slack.com/).

      #{'## Include `slack-notifier` in your Gemfile to use this Agent!' if dependencies_missing?}

      To get started, you will first need to configure an incoming webhook.

      - Go to `https://my.slack.com/services/new/incoming-webhook`, choose a default channel and add the integration.

      Your webhook URL will look like: `https://hooks.slack.com/services/some/random/characters`

      Once the webhook has been configured, it can be used to post to other channels or direct to team members. To send a private message to team member, use their @username as the channel. Messages can be formatted using [Liquid](https://github.com/huginn/huginn/wiki/Formatting-Events-using-Liquid).

      Finally, you can set a custom icon for this webhook in `icon`, either as [emoji](http://www.emoji-cheat-sheet.com) or an URL to an image. Leaving this field blank will use the default icon for a webhook.
    MD

    def default_options
      {
        'webhook_url' => 'https://hooks.slack.com/services/...',
        'channel' => '#general',
        'username' => DEFAULT_USERNAME,
        'message' => "Hey there, It's Huginn",
        'icon' => '',
      }
    end

    def validate_options
      unless options['webhook_url'].present? ||
          (options['auth_token'].present? && options['team_name'].present?)  # compatibility
        errors.add(:base, "webhook_url is required")
      end

      errors.add(:base, "channel is required") unless options['channel'].present?
    end

    def working?
      received_event_without_error?
    end

    def webhook_url
      case
      when url = interpolated[:webhook_url].presence
        url
      when (team = interpolated[:team_name].presence) && (token = interpolated[:auth_token])
        webhook = interpolated[:webhook].presence || 'incoming-webhook'
        # old style webhook URL
        "https://#{Rack::Utils.escape_path(team)}.slack.com/services/hooks/#{Rack::Utils.escape_path(webhook)}?token=#{Rack::Utils.escape(token)}"
      end
    end

    def username
      interpolated[:username].presence || DEFAULT_USERNAME
    end

    def slack_notifier
      @slack_notifier ||= Slack::Notifier.new(webhook_url, username:)
    end

    def filter_options(opts)
      opts.select { |key, _value| ALLOWED_PARAMS.include? key }.symbolize_keys
    end

    def receive(incoming_events)
      incoming_events.each do |event|
        opts = interpolated(event)
        slack_opts = filter_options(opts)
        if opts[:icon].present?
          if /^:/.match(opts[:icon])
            slack_opts[:icon_emoji] = opts[:icon]
          else
            slack_opts[:icon_url] = opts[:icon]
          end
        end
        slack_notifier.ping opts[:message], slack_opts
      end
    end
  end
end
