module ExternalAPI
  # Contains methods for utilizing services outside of Smoca.
  # Has a general GET and POST method, as well as methods for the following services:
  # - Slack
  # - Jenkins

  # @param url - Type String. The URL to get data from
  # @param ssl - Type Boolean. Whether to use SSL or not
  # @param auth - Type Hash. Format: {[:username] [:password]}. False for no basic auth
  # @return Net::HTTP object of the result
  def rest_get(url, ssl, auth)

    uri = URI.parse(url) # TODO: Would love to set token in a header instead
    http = Net::HTTP.new(uri.host, uri.port)

    if ssl == true
      http.use_ssl = true
      http.verify_mode = OpenSSL::SSL::VERIFY_NONE
    end

    request = Net::HTTP::Get.new(uri.request_uri)

    if auth != false
      request.basic_auth(auth[:username], auth[:password])
    end

    return http.request(request)
  end

  # @param url - Type String. The URL to POST data to
  # @param ssl - Type Boolean. Whether to use SSL or not
  # @param data - Type Hash. The data you want to send to the endpoint
  # @return Net::HTTP object of the result
  def rest_post(url, ssl, data)
    uri = URI.parse(url)
    http = Net::HTTP.new(uri.host, uri.port)

    if ssl == true
      http.use_ssl = true
      http.verify_mode = OpenSSL::SSL::VERIFY_NONE
    end

    request = Net::HTTP::Post.new(uri.request_uri)
    request.set_form_data(data)

    return http.request(request)
  end

  module Notifications
    module Slack

      include ExternalAPI

      # @param name - Type String. A Slack Display Name
      # @param ldap Boolean Whether to search with an LDAP username or Not
      # @return - Hash containing :id, and :first_name if available.
      def get_slack_user(name, ldap)
        response = rest_get("https://twitch.slack.com/api/users.list?token=#{@slack_token}", true, false)

        if response.code.to_i == 200
          response = JSON.parse(response.body)

          if response['ok'] == true

            if ldap
              # Some users ldap does not mention the strategy we're using of (ldap_username)@email.tv
              # This searches a list to see if the initiator matches one of those users
              override_id = search_override_users(name)
              return {:id => override_id} unless override_id.nil?

              key = search_by_ldap(name, response)
            else
              key = search_by_name(name, response)
            end

            if key
              return {:id => key['id'], :first_name => key['profile']['first_name']}
            else
              return nil
            end

          else # Bad response from Slack. Different from bad code. Possibly invalid api token.
            raise Exception.new("Failed to connect to Slack API. Response: #{response['ok']} - #{response['error']}")
          end

        else
          raise Exception.new("Failed to connect to Slack API. Response Code: #{response.code}")
        end

        return nil # Return nil if it didn't find anything
      end

      # Search by full name through Slack
      # @param name String The name to search
      # @param body Hash The Slack user list response
      # @return The user hash that matched
      # @return [NilClass] If no user was found, returns nil
      def search_by_name(name, body)
        body['members'].each do |key|
          next if key['real_name'].nil? || key['real_name'].empty? || key['name'].match(/\S*_old\z/) # Skip if user didn't set a real name

          if key['real_name'].downcase == name.downcase
            # Compare Slack's Real Name to the Name Passed In. Not Case Sensitive.
            return key
          end
        end

        return nil # If it got here, it didn't find anything
      end

      # Search by LDAP username through Slack in the sceme of (ldap_user)@email.tv
      # @param name String The name to search
      # @param body Hash The Slack user list response
      # @return The user hash that matched
      def search_by_ldap(name, body)
        body['members'].each do |key|
          email = key['profile']['email']

          # Move onto the next user if their email is empty, their name has "_old" within it, or they are deleted
          # Note: Users have _old when their SSO migration wasn't working, so IT basically threw these accounts out.
          next if email.nil? || email.empty? || key['name'].match(/\S*_old\z/) || key['deleted'] == true

          regex = /(\S*)@(justin.tv|twitch.tv)/ # Look for (ldap_username)@justin.tv or (ldap_username)@twitch.tv
          eval = regex.match(email)

          if eval.nil?
            # The email doesn't have a domain of @justin.tv or @twitch.tv
            # Example: @pivotal.io
            # Temporarily skip until we can provide support for these users
            puts "[WARN] Came across a non-standard email: #{email}"
            next
          else
            email_name = eval[1]
          end

          return key if email_name.downcase == name.downcase
        end

        return nil
      end

      # @return Array A list of users who do not have the convention of (ldap_username)@email.tv
      def overide_ldap_list
        override_users = []

        override_users << {'ldap' => 'mbollier',          'slack_id' => 'U03C7EY0Y'}
        override_users << {'ldap' => 'hassaan-markhiani', 'slack_id' => 'U03SWDZL7'}
        override_users << {'ldap' => 'tiffany-huang',     'slack_id' => 'U03SWEBCK'}
        override_users << {'ldap' => 'xangold',           'slack_id' => 'U03T6UVF9'}
        override_users << {'ldap' => 'jos',               'slack_id' => 'U03T6MDNZ'}
        override_users << {'ldap' => 'dlu',               'slack_id' => 'U03SWE1BP'}
        override_users << {'ldap' => 'mixonic',           'slack_id' => 'U0MP9NBRD'}
        override_users << {'ldap' => 'rwjblue',           'slack_id' => 'U0J6METJP'}
        override_users << {'ldap' => 'achou',             'slack_id' => 'U0TFV834H'}
        override_users << {'ldap' => 'seph',              'slack_id' => 'U0FLSJVBP'}
        override_users << {'ldap' => 'bantic',            'slack_id' => 'U0NMGECQ6'}
        override_users << {'ldap' => 'ben-swartz',        'slack_id' => 'U03SZ7P1T'}
        override_users << {'ldap' => 'devtools',          'slack_id' => false} # devtools is a deploy bot. No slack username.
        override_users << {'ldap' => 'hank',              'slack_id' => 'U0W0K8TED'}
        override_users << {'ldap' => 'Kai-Hayashi',       'slack_id' => 'U03SZ0HE3'}
        override_users << {'ldap' => 'abrown',            'slack_id' => 'U1W7UTPL4'}
        override_users << {'ldap' => 'jamesjia',          'slack_id' => 'U2K8CU1NY'}
        override_users << {'ldap' => 'toph',              'slack_id' => 'U0AAURCP9'}
        override_users << {'ldap' => 'tiffache',          'slack_id' => 'U1LMDAJPN'}
        override_users << {'ldap' => 'cbest',             'slack_id' => 'U0CPK83S6'}
        override_users << {'ldap' => 'jday',              'slack_id' => 'U0HM6THL7'}

        return override_users
      end

      # Searches through the override list for a matching user
      # @param name String Name of ldap user to search for
      # @return String Slack ID of the user
      def search_override_users(name)
        users = overide_ldap_list

        users.each do |x|
          if x['ldap'] == name
            puts "WARNING: Overriding Slack ID for user #{name} with ID: #{x['slack_id']}"
            return x['slack_id']
          end
        end

        return nil # Found nothing
      end

      # @param id - Type String. The ID of the Slack member you wish to open a DM message with
      # @return String of the Direct Message ID that can then be sent a message to
      def open_dm(id)
        # To DM, Slack must open an IM with the user, which returns a unique ID
        # https://api.slack.com/methods/im.open
        data = {'token' => @slack_token, 'user' => id}
        open_dm = rest_post('https://slack.com/api/im.open', true, data)

        open_dm = JSON.parse(open_dm.body)

        if open_dm['ok'] == true
          puts "Notice: Successfully opened Slack direct message to ID: #{id}"
          return open_dm['channel']['id']
        else # The Direct Message could not be opened with the user
          message = "_SMOCA failed to open a DM to Slack. Slack responded: #{open_dm['error']}._\n"\
    "Slack User ID: #{id}\n"
          puts message
          send_slack_message(@automation_room, attachments, message)
        end
      end


      # @param id - Type String. The Channel or DM ID to send a message to
      # @param attachments - Type String. A predefined set of attachments to send through Slack. Recommend using construct_attachments()
      # @param pretext - Type String. A message to display before the attachments
      # @return Boolean of True if it was sent successfully
      def send_slack_message(id, attachments, pretext)
        data = {'token' => @slack_token,
                'channel' => id,
                'username' => 'smoca',
                'as_user' => 'smoca',
                'attachments' => attachments}

        if pretext != false
          data['text'] = pretext
        end

        slack_send = rest_post('https://slack.com/api/chat.postMessage', true, data)
        slack_send = JSON.parse(slack_send.body)

        if slack_send['ok'] == true
          puts "Notice: Successfully sent slack message to DM ID: #{id}"
          return true
        else # The Direct Message could be opened with the user, but not sent
          message = "_SMOCA failed to deliver a message to Slack. Slack responded: #{slack_send['error']}._\n"\
      "_Slack Channel ID: #{id}_\n"
          raise Exception.new(message)
        end
      end

      # @param build - Type Hash. Jenkins Build results from get_build_info(). Analyzes the returned hash
      # @return String of JSON Attachments that Slack accepts
      def construct_attachments(build)
        if build['result'] == 'SUCCESS'
          emoji = ':jira:'
          color = 'good'
        elsif build['result'] == 'FAILURE'
          emoji = ':sob:'
          color = 'danger'
        else
          emoji = ''
          color = 'warning'
        end

        if build['jobName'] == 'qa-smoca'
          # TODO: Query Gnosis to see if the record exists when we expand to more builds
          # Blocker currently is not running this script inside Manta to install the necessary gems
          url = "https://smoca.internal.justin.tv/job/#{build['jobName']}/#{build['number']}"
        else
          url = build['url']
        end

        fields = [
          {:title => 'Build', :value => "<#{url}|#{build['displayName']}>", :short => true},
          # Note, value in special format to make linkable. Should clean up on future refactor.
          {:title => 'Result', :value => build['result'], :short => true},
          {:title => 'Environment', :value => build['environment'], :short => true},
          # Note: ENV['SLACK_USER_OVERRIDE'], if it exists, will override initiator user name
          {:title => 'Initiator', :value => ENV['SLACK_USER_OVERRIDE'] || build['initiatedBy'], :short => true}
        ]

        attachments = [{:fallback => "The result of your automation test is: #{build['result']} #{emoji}",
                        :color => color,
                        :title_link => url,
                        :fields => fields}]

        json_attach = JSON.generate(attachments)

        return json_attach
      end
    end
  end
end
