require 'drone/builds/version'
require 'open-uri'
require 'net/http'
require 'json'

module Drone
  class Builds
    attr_reader :token, :domain, :owner, :name, :job_id, :protocol

    MAX_LIMIT = 30
    DEFAULT_LIMIT = 10
    DEFAULT_JOB_ID = 1

    def initialize(token:, domain:, owner:, name:, job_id: nil, protocol: nil)
      @token = token
      @domain = domain
      @owner = owner
      @name = name
      @job_id = job_id || DEFAULT_JOB_ID
      @protocol = protocol || 'http'
    end

    def list(limit: nil, ref: nil, status: nil)
      json_api(builds_url).select do |b|
        (!ref || b['ref'] == ref) && (!status || b['status'] == status)
      end.first(limit || DEFAULT_LIMIT).reverse
    end

    def show(build_number, filter: nil, context: nil)
      text_api(logs_url(build_number)).lines.each do |l|
        yield l if show_line(l, filter: filter, context: context)
      end
    rescue OpenURI::HTTPError
      stream_api(stream_url(build_number)) do |l|
        yield l if show_line(l, filter: filter, context: context)
      end
    end

    def kill(build_number)
      delete_api(job_url(build_number))
    end

    def restart(build_number)
      post_api(build_url(build_number))
    end

    private

    def base_url
      "#{protocol}://#{domain}/api"
    end

    def builds_url
      "#{base_url}/repos/#{owner}/#{name}/builds"
    end

    def logs_url(build_number)
      "#{base_url}/repos/#{owner}/#{name}/logs/#{build_number}/#{job_id}"
    end

    def build_url(build_number)
      "#{base_url}/repos/#{owner}/#{name}/builds/#{build_number}"
    end

    def job_url(build_number)
      "#{base_url}/repos/#{owner}/#{name}/builds/#{build_number}/#{job_id}"
    end

    def stream_url(build_number)
      "#{base_url}/stream/#{owner}/#{name}/#{build_number}/#{job_id}"
    end

    def access_token_param
      "?access_token=#{token}"
    end

    def access_token_args
      "access_token=#{token}"
    end

    def access_token_hash
      { access_token: token }
    end

    def full_url(url)
      "#{url}#{access_token_param}"
    end

    def stream_api(url)
      uri = URI(full_url(url))

      Net::HTTP.start(uri.host, uri.port) do |http|
        http.request(Net::HTTP::Get.new(uri)) do |response|
          response.read_body do |chunk|
            yield clean_stream_data(chunk)
          end
        end
      end
    end

    def json_api(url)
      JSON.parse(open(full_url(url)).read)
    end

    def text_api(url)
      open(full_url(url)).read
    end

    def post_api(url)
      parse_response(Net::HTTP.new(URI(url).host).post(URI(url).path + access_token_param, ""))
    end

    def delete_api(url)
      parse_response(Net::HTTP.new(URI(url).host).delete(URI(url).path + access_token_param))
    end

    def clean_stream_data(data)
      lines = data.lines.grep(/^data:/).map { |l| l.gsub(/^data:/, '') }
      lines.map(&:strip).compact.join("\n")
    end

    def show_line(line, filter:, context:)
      return true if filter.nil?

      @count ||= 0

      if line.include?(filter)
        @count = context
        true
      elsif @count > 0
        @count -= 1
        true
      else
        false
      end
    end


    def parse_response(response)
      if response.code[0] == "2" # 200 / 202 / ?
        "#{response.code}: #{response.message}"
      else
        "#{response.code}: #{response.body}"
      end
    end
  end
end
