class Geet::Github::PR

def self.create(title, description, head, api_interface, base, draft: false)

def self.create(title, description, head, api_interface, base, draft: false)
  api_path = 'pulls'
  if api_interface.upstream?
    authenticated_user = Geet::Github::User.authenticated(api_interface).username
    head = "#{authenticated_user}:#{head}"
  end
  request_data = {title:, body: description, head:, base:, draft:}
  response = T.cast(api_interface.send_request(api_path, data: request_data), T::Hash[String, T.untyped])
  number = T.cast(response.fetch('number'), Integer)
  title = T.cast(response.fetch('title'), String)
  link = T.cast(response.fetch('html_url'), String)
  node_id = T.cast(response['node_id'], T.nilable(String))
  new(number, api_interface, title, link, node_id:)
end

def self.list(api_interface, milestone: nil, assignee: nil, owner: nil, head: nil, &type_filter)

def self.list(api_interface, milestone: nil, assignee: nil, owner: nil, head: nil, &type_filter)
  check_list_params!(milestone, assignee, head)
  if head
    api_path = 'pulls'
    # Technically, the upstream approach could be used for both, but it's actually good to have
    # both of them as reference.
    #
    # For upstream pulls, the owner is the authenticated user, otherwise, the repository owner.
    #
    response = if api_interface.upstream?
      unfiltered_response = T.cast(
        api_interface.send_request(api_path, multipage: true),
        T::Array[T::Hash[String, T.untyped]]
      )
      # VERY weird. From the docs, it's not clear if the user/org is required in the `head` parameter,
      # but:
      #
      # - if it isn't included (eg. `anything`), the parameter is ignored
      # - if it's included (eg. `saveriomiroddi:local_branch_name`), an empty resultset is returned.
      #
      # For this reason, we can't use that param, and have to filter manually.
      #
      unfiltered_response.select do |pr_data|
        pr_head = T.cast(pr_data.fetch('head'), T::Hash[String, T.untyped])
        label = T.cast(pr_head.fetch('label'), String)
        label == "#{owner}:#{head}"
      end
    else
      request_params = {head: "#{owner}:#{head}"}
      T.cast(
        api_interface.send_request(api_path, params: request_params, multipage: true),
        T::Array[T::Hash[String, T.untyped]]
      )
    end
    response.map do |pr_data|
      number = T.cast(pr_data.fetch('number'), Integer)
      title = T.cast(pr_data.fetch('title'), String)
      link = T.cast(pr_data.fetch('html_url'), String)
      new(number, api_interface, title, link)
    end
  else
    result = super(api_interface, milestone:, assignee:) do |issue_data|
      issue_data.key?('pull_request')
    end
    T.cast(result, T::Array[Geet::Github::PR])
  end
end

def check_list_params!(milestone, assignee, head)

def check_list_params!(milestone, assignee, head)
  if (milestone || assignee) && head
    raise "Head can't be specified with milestone or assignee!"
  end
end

def enable_automerge

def enable_automerge
  merge_method = fetch_available_merge_method
  query = <<~GRAPHQL
    mutation($pullRequestId: ID!, $mergeMethod: PullRequestMergeMethod!) {
      enablePullRequestAutoMerge(input: {pullRequestId: $pullRequestId, mergeMethod: $mergeMethod}) {
        pullRequest {
          id
          autoMergeRequest {
            enabledAt
            mergeMethod
          }
        }
      }
    }
  GRAPHQL
  variables = {pullRequestId: @node_id, mergeMethod: merge_method}
  @api_interface.send_graphql_request(query, variables:)
  merge_method
end

def fetch_available_merge_method

def fetch_available_merge_method
  query = <<~GRAPHQL
    query($owner: String!, $name: String!, $number: Int!) {
      repository(owner: $owner, name: $name) {
        mergeCommitAllowed
        squashMergeAllowed
        rebaseMergeAllowed
        pullRequest(number: $number) {
          commits {
            totalCount
          }
        }
      }
    }
  GRAPHQL
  owner, name = T.must(@api_interface.repository_path).split('/')
  response = @api_interface.send_graphql_request(query, variables: {owner:, name:, number:})
  repo_data = response['repository'].transform_keys(&:to_sym)
  commit_count = repo_data[:pullRequest]['commits']['totalCount']
  if commit_count == 1 && repo_data[:squashMergeAllowed]
    'SQUASH'
  elsif repo_data[:mergeCommitAllowed]
    'MERGE'
  elsif repo_data[:squashMergeAllowed]
    'SQUASH'
  elsif repo_data[:rebaseMergeAllowed]
    'REBASE'
  else
    raise 'No merge methods are allowed on this repository'
  end
end

def initialize(number, api_interface, title, link, node_id: nil)

def initialize(number, api_interface, title, link, node_id: nil)
  super(number, api_interface, title, link)
  @node_id = node_id
end

def merge(merge_method: nil)

def merge(merge_method: nil)
  api_path = "pulls/#{number}/merge"
  request_data = {merge_method:} if merge_method
  @api_interface.send_request(api_path, http_method: :put, data: request_data)
end

def request_review(reviewers)

def request_review(reviewers)
  api_path = "pulls/#{number}/requested_reviewers"
  request_data = {reviewers:}
  @api_interface.send_request(api_path, data: request_data)
end