🍯 Glaze

-- lua/gpr_gh.lua
-- Async PR opener using `vim.system` + GitHub CLI.
-- 1) blame current line -> SHA
-- 2) read commit subject
-- 3) extract PR number "(#1234)"
-- 4) `gh pr view  --web`
local M = {}

-- Safe notify in fast-event contexts
local notify = vim.schedule_wrap(function(msg, level)
  vim.notify(msg, level or vim.log.levels.INFO, { title = "GPR" })
end)

local function run(cmd, opts, cb)
  opts = opts or {}
  opts.text = true
  vim.system(cmd, opts, function(obj)
    if obj.code ~= 0 then
      cb(nil, obj.stderr ~= "" and obj.stderr or obj.stdout)
    else
      cb(obj.stdout or "", nil)
    end
  end)
end

local function blame_sha_for_line(repo_cwd, cb)
  local file = vim.api.nvim_buf_get_name(0)
  if file == "" then return cb(nil, "Buffer has no filename") end

  -- Use Lua dirname to avoid vim.fn in fast context
  local file_dir = vim.fs.dirname(file) or repo_cwd
  local lnum = vim.api.nvim_win_get_cursor(0)[1]

  run({ "git", "blame", "-L", ("%d,%d"):format(lnum, lnum), "--porcelain", "--", file },
      { cwd = file_dir }, function(out, err)
    if err then return cb(nil, "git blame failed: " .. err) end
    local sha = out:match("^([0-9a-fA-F]+)")
    if not sha then return cb(nil, "Could not parse a commit for this line") end
    cb(sha, nil)
  end)
end

local function commit_subject(repo_cwd, sha, cb)
  run({ "git", "log", "-n", "1", "--format=%s", sha }, { cwd = repo_cwd }, function(out, err)
    if err then return cb(nil, "git log failed: " .. err) end
    cb(vim.trim(out), nil)
  end)
end

local function extract_pr_number(subject)
  return subject:match("%(#(%d+)%)")      -- Title (#1234)
      or subject:match("PR%s*#(%d+)")     -- PR #1234
      or subject:match("#(%d+)")          -- #1234 anywhere
end

function M.open_pr_for_line()
  if vim.fn.executable("gh") == 0 then
    return notify("GitHub CLI (gh) not found", vim.log.levels.ERROR)
  end

  -- Capture CWD *before* async (pure Lua; safe in fast events)
  local repo_cwd = vim.uv.cwd()  -- alias: vim.loop.cwd()

  blame_sha_for_line(repo_cwd, function(sha, e1)
    if not sha then return notify(e1, vim.log.levels.WARN) end

    commit_subject(repo_cwd, sha, function(subj, e2)
      if not subj then return notify(e2, vim.log.levels.WARN) end

      local pr = extract_pr_number(subj)
      if not pr then
        return notify("No PR number like (#1234) in: " .. subj, vim.log.levels.WARN)
      end

      -- Fire-and-forget: open PR in browser via gh
      run({ "gh", "pr", "view", pr, "--web" }, { cwd = repo_cwd }, function(_, err)
        if err and not err:match("^warning:") then
          notify("Failed to open PR #" .. pr .. ": " .. err, vim.log.levels.WARN)
        else
          notify("Opening PR #" .. pr)
        end
      end)
    end)
  end)
end

return M