require 'net/http'
require 'net/https'
require 'uri'
require 'json'
require './class/data_writer'
require 'date'

class JenkinsConnection
  def initialize(jenkins_job)
    raise 'Error. Must provide Jenkins Username and Token for API Access' unless ENV['JENKINS_USER'] && ENV['JENKINS_TOKEN']

    @jenkins_username = ENV['JENKINS_USER']
    @jenkins_token = ENV['JENKINS_TOKEN']
    @jenkins_job = jenkins_job
    @jenkins_job_url = "http://jenkins-master-0.prod.us-west2.justin.tv/job/#{jenkins_job}"
    @jenkins_job_api_url = "#{@jenkins_job_url}/api/json"

    @auth = {:username => @jenkins_username, :password => @jenkins_token}
  end

  def get_last_build_number
    response = rest_get(@jenkins_job_api_url)

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

      return body['lastBuild']['number']
    end
  end

  def get_build_info(build_number)
    api_url = "#{@jenkins_job_url}/#{build_number}/api/json"
    timestamp_url = "#{@jenkins_job_url}/#{build_number}/buildTimestamp"

    response = rest_get(api_url)
    timestamp = rest_get(timestamp_url)

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

      # Jenkins returns a hash with nested hashes and arrays. The order varies per project
      # Need to loop through all nests until we find the right thing
      body.each_key do |key|
        if key == 'actions'
          body[key].each do |actions|
            if actions.kind_of?(Hash)

              # Analyze Parameters
              if actions.has_key?'parameters'
                actions['parameters'].each do |params|

                  # Environment
                  if params['name'] == 'ENVIRONMENT'
                    @environment = params['value']
                  end

                  # GITHUB_CREATOR / LDAP Username (available if deploy tool initated it)
                  if params['name'] == 'GITHUB_CREATOR'
                    @ldap = params['value']
                  end

                  if params['name'] == 'TRIGGER_SMOCA'
                    @trigger_smoca = params['value']
                  end
                end
              end

              # Get initator name (available if manually initiated through Jenkins)
              if actions.has_key?('causes')
                actions['causes'].each do |causes|
                  if causes.has_key?('userName')
                    @initiator = causes['userName']
                  end
                end
              end
            end
          end
        end
      end

      # If it is a deploy job with GITHUB_CREATOR, use that. If not, it'll use who manually triggered the job.
      if @ldap
        initiated_by = @ldap
      elsif @initiator
        initiated_by = @initiator
      end

      stamp = DateTime.strptime(timestamp.body, "%m/%d/%y %l:%M %p")
      date = stamp.strftime("%-m/%-d/%Y")
      time = stamp.strftime("%l:%M %p")

      return {'buildNumber' => build_number, 'buildName' => body['fullDisplayName'], 'buildResult' => body['result'],
          'initiatedBy' => initiated_by, 'date' => date, 'time' => time, 'environment' => @environment, 'smoca_triggered' => @trigger_smoca}


    else
      regex = /<title>.*<\/title>/
      error = response.body.to_s.match regex
      puts error
      raise Exception.new("Invalid token for Jenkins. Jenkins returned #{response.code.to_i}")
    end
  end

  def compile_build_data(amount_of_builds, file_name)
    csv = DataWriter.new(file_name)
    builds = Array.new

    for i in 0..(amount_of_builds - 1)
      build_number = get_last_build_number - i

      if csv.build_exists?(build_number)
        data = csv.build_exists?(build_number)
      else
        data = get_build_info(build_number)
        csv.save_data(data)
        sleep(2) # Rate limiting on the API
      end

      builds << data

      if i == 0
        print 'Collecting Data.'
      else
        print '.'
      end
    end

    return builds
  end

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

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

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

    return http.request(request)
  end
end
