# frozen_string_literal: true

require 'json'
require 'faraday'
require 'faraday_middleware'

GITHUB_ADDR = ENV['GITHUB_ADDR'].freeze
GITHUB_TOKEN = ENV['GITHUB_TOKEN'].freeze
GITHUB_USER = ENV['GITHUB_USER'].freeze

module CSITeamCity
  module Status
    def teamcity_fail(message, details: nil, hard_fail: true)
      puts "####teamcity[buildProblem description='#{message}']"
      if hard_fail
        abort details unless details.nil?
        exit 1
      end
    end

    def teamcity_success(message, force_exit: false)
      puts "##teamcity[buildStatus status='SUCCESS' text='#{message}']"
      exit if force_exit
    end

    def teamcity_message(message, success: true)
      puts "##teamcity[message text='#{message}']"
      success
    end
  end

  module GitHub
    class Server
      class << self
        def conn
          @conn ||= Faraday.new(url: GITHUB_ADDR) do |conn|
            conn.request :multipart
            conn.request :url_encoded
            conn.response :json, content_type: 'application/json'
            conn.adapter :net_http
            conn.headers = { 'Authorization' => "token #{GITHUB_TOKEN}" }
          end
        end
      end
    end

    class Comment
      include CSITeamCity::Status

      def initialize(body, repo:, on:)
        @body = body
        @repo = repo
        @on = on.tr('pr', '')

        teamcity_fail 'Commit comments not currently supported' unless pull_request?

        self
      end

      def create
        res = conn.post do |req|
          req.url "/api/v3/repos/#{@repo}/issues/#{@on}/comments"
          req.headers['Content-Type'] = 'application/json'
          req.body = { body: @body }.to_json
        end
        teamcity_fail "GitHub (POST) comment on #{@on} returned with code #{res.status}" unless res.status == 201
        self
      end

      def update(filter = '')
        res = conn.patch do |req|
          req.url "/api/v3/repos/#{@repo}/issues/comments/#{last_comment(filter)['id']}"
          req.headers['Content-Type'] = 'application/json'
          req.body = { body: @body }.to_json
        end
        teamcity_fail "GitHub (PATCH) comment #{last_comment(filter)['id']} returned with code #{res.status}" unless res.status == 200
        self
      end

      def update_or_create(filter = '')
        return update(filter) unless last_comment(filter).nil?
        create
      end

      def recreate(filter = '')
        purge(filter)
        create
      end

      private

      def conn
        @conn ||= CSITeamCity::GitHub::Server.conn
      end

      def pull_request?
        @on =~ /^[0-9]+$/
      end

      def commit?
        @on =~ /^[a-z0-9]$/ && !pull_request?
      end

      def purge(filter)
        # clean up old, outdated comments on pull requests
        teamcity_fail 'Comment method `purge` is only available on pull requests' unless pull_request?

        existing_comments(filter).each do |c|
          del_res = conn.delete "/api/v3/repos/#{@repo}/issues/comments/#{c['id']}"
          teamcity_fail "GitHub (DELETE) of comment #{c['id']} returned with code #{del_res.status}" if del_res.status != 204
        end
        self
      end

      def existing_comments(filter = '')
        # get all comments by our github user on pull requests
        teamcity_fail 'Comment method `existing_comments` is only available on pull requests' unless pull_request?

        @existing_comments ||= begin
          res = conn.head "/api/v3/repos/#{@repo}/issues/#{@on}/comments"
          page = res.headers['link']
          page = page.nil? ? 1 : page.split(',')[1].match(/page=([0-9]+)/i).captures[0].to_i
          pages = page

          exc = []

          while page >= 1
            # puts "Page #{page} of #{pages}"
            res = conn.get "/api/v3/repos/#{@repo}/issues/#{@on}/comments?page=#{page}"
            teamcity_fail "GitHub (GET) comments on #{@on} returned with code #{res.status}" if res.status != 200

            res.body.reverse_each do |c|
              # puts "USER: #{c['user']['login']}, ME: #{GITHUB_USER}, IS ME?: #{c['user']['login'] == GITHUB_USER}, BODY: #{c['body'].gsub("\n", " // ")[0..64]}, PASS FILTER?: #{c['body'].include?(filter)}"
              next unless c['user']['login'] == GITHUB_USER && c['body'].include?(filter)
              exc << c
            end
            page -= 1
          end
          exc
        end
      end

      def last_comment(filter = '')
        return nil if existing_comments(filter).empty?
        existing_comments(filter).first # we're already in reverse chron order
      end

      def clear_existing_comments
        @existing_comments = nil
      end
    end
  end
end
