class RubyLsp::Tapioca::RunGemRbiCheck

def cleanup_orphaned_rbis

def cleanup_orphaned_rbis
  untracked_files = git_ls_gem_rbis("--others", "--exclude-standard")
  deleted_files = git_ls_gem_rbis("--deleted")
  delete_files(untracked_files, "Deleted untracked RBIs")
  restore_files(deleted_files, "Restored deleted RBIs")
end

def delete_files(files, message)

def delete_files(files, message)
  files_to_remove = files.map { |file| File.join(project_path, file) }
  FileUtils.rm(files_to_remove)
  log_message("#{message}: #{files.join(", ")}") unless files.empty?
end

def execute_in_project_path(*parts, stdin: nil)

def execute_in_project_path(*parts, stdin: nil)
  options = { chdir: project_path }
  options[:stdin_data] = stdin if stdin
  stdout_and_stderr, _status = T.unsafe(Open3).capture2e(*parts, options)
  stdout_and_stderr
end

def execute_tapioca_gem_command(gems)

def execute_tapioca_gem_command(gems)
  Bundler.with_unbundled_env do
    stdout, stderr, status = T.unsafe(Open3).capture3(
      "bundle",
      "exec",
      "tapioca",
      "gem",
      "--lsp_addon",
      *gems,
      chdir: project_path,
    )
    log_message(stdout) unless stdout.empty?
    @stderr = stderr unless stderr.empty?
    @status = status
  end
end

def generate_gem_rbis

def generate_gem_rbis
  parser = Tapioca::LockfileDiffParser.new(@lockfile_diff)
  removed_gems = parser.removed_gems
  added_or_modified_gems = parser.added_or_modified_gems
  if added_or_modified_gems.any?
    log_message("Identified lockfile changes, attempting to generate gem RBIs...")
    execute_tapioca_gem_command(added_or_modified_gems)
  elsif removed_gems.any?
    remove_rbis(removed_gems)
  end
end

def git_ls_gem_rbis(*flags)

def git_ls_gem_rbis(*flags)
  flags = T.unsafe(["git", "ls-files", *flags, "sorbet/rbi/gems/"])
  execute_in_project_path(*flags)
    .lines
    .map(&:strip)
end

def git_repo?

def git_repo?
  _, status = Open3.capture2e("git", "rev-parse", "--is-inside-work-tree", chdir: project_path)
  status.success?
end

def initialize(project_path)

def initialize(project_path)
  @project_path = project_path
  @stdout = T.let("", String)
  @stderr = T.let("", String)
  @status = T.let(nil, T.nilable(Process::Status))
end

def lockfile

def lockfile
  @lockfile ||= T.let(Pathname(project_path).join("Gemfile.lock"), T.nilable(Pathname))
end

def lockfile_changed?

def lockfile_changed?
  !lockfile_diff.empty?
end

def lockfile_diff

def lockfile_diff
  @lockfile_diff ||= T.let(read_lockfile_diff, T.nilable(String))
end

def log_message(message)

def log_message(message)
  @stdout += "#{message}\n"
end

def read_lockfile_diff

def read_lockfile_diff
  return "" unless lockfile.exist?
  execute_in_project_path("git", "diff", lockfile.to_s).strip
end

def remove_rbis(gems)

def remove_rbis(gems)
  files = Dir.glob(
    "sorbet/rbi/gems/{#{gems.join(",")}}@*.rbi",
    base: project_path,
  )
  delete_files(files, "Removed RBIs for")
end

def restore_files(files, message)

def restore_files(files, message)
  execute_in_project_path("git", "checkout", "--pathspec-from-file=-", stdin: files.join("\n"))
  log_message("#{message}: #{files.join(", ")}") unless files.empty?
end

def run

def run
  return log_message("Not a git repository") unless git_repo?
  cleanup_orphaned_rbis
  if lockfile_changed?
    generate_gem_rbis
  end
end