require "uri"
require "json"

module Backup
  module Notifier
    class Slack < Base
      ##
      # The incoming webhook url
      attr_accessor :webhook_url

      ##
      # The channel to send messages to
      attr_accessor :channel

      ##
      # The username to display along with the notification
      attr_accessor :username

      ##
      # The emoji icon to display along with the notification
      #
      # See http://www.emoji-cheat-sheet.com for a list of icons.
      #
      # Default: :floppy_disk:
      attr_accessor :icon_emoji

      ##
      # Array of statuses for which the log file should be attached.
      #
      # Available statuses are: `:success`, `:warning` and `:failure`.
      # Default: [:warning, :failure]
      attr_accessor :send_log_on

      def initialize(model, &block)
        super
        instance_eval(&block) if block_given?

        @send_log_on ||= [:warning, :failure]
        @icon_emoji  ||= ":floppy_disk:"
      end

      private

      ##
      # Notify the user of the backup operation results.
      #
      # `status` indicates one of the following:
      #
      # `:success`
      # : The backup completed successfully.
      # : Notification will be sent if `on_success` is `true`.
      #
      # `:warning`
      # : The backup completed successfully, but warnings were logged.
      # : Notification will be sent if `on_warning` or `on_success` is `true`.
      #
      # `:failure`
      # : The backup operation failed.
      # : Notification will be sent if `on_warning` or `on_success` is `true`.
      #
      def notify!(status)
        data = {
          text: message.call(model, status: status_data_for(status)),
          attachments: [attachment(status)]
        }
        [:channel, :username, :icon_emoji].each do |param|
          val = send(param)
          data.merge!(param => val) if val
        end

        options = {
          headers: { "Content-Type" => "application/x-www-form-urlencoded" },
          body: URI.encode_www_form(payload: JSON.dump(data))
        }
        options[:expects] = 200 # raise error if unsuccessful
        Excon.post(uri, options)
      end

      def attachment(status)
        {
          fallback: "#{title(status)} - Job: #{model.label} (#{model.trigger})",
          text: title(status),
          color: color(status),
          fields: [
            {
              title: "Job",
              value: "#{model.label} (#{model.trigger})",
              short: false
            },
            {
              title: "Started",
              value: model.started_at,
              short: true
            },
            {
              title: "Finished",
              value: model.finished_at,
              short: true
            },
            {
              title: "Duration",
              value: model.duration,
              short: true
            },
            {
              title: "Version",
              value: "Backup v#{Backup::VERSION}\nRuby: #{RUBY_DESCRIPTION}",
              short: false
            },
            log_field(status)
          ].compact
        }
      end

      def log_field(status)
        send_log = send_log_on.include?(status)
        return unless send_log

        {
          title: "Detailed Backup Log",
          value: Logger.messages.map(&:formatted_lines).flatten.join("\n"),
          short: false
        }
      end

      def color(status)
        case status
        when :success then "good"
        when :failure then "danger"
        when :warning then "warning"
        end
      end

      def title(status)
        case status
        when :success then "Backup Completed Successfully!"
        when :failure then "Backup Failed!"
        when :warning then "Backup Completed Successfully (with Warnings)!"
        end
      end

      def uri
        @uri ||= webhook_url
      end
    end
  end
end
