local api = vim.api
local ag = api.nvim_create_augroup('sops', {})

local function path_from_uri(uri)
  return string.sub(uri, string.len('sops://') + 1)
end

local function write_buf(buf)
  local file = path_from_uri(api.nvim_buf_get_name(buf))
  local cmd = vim.system({
    'sh',
    '-c',
    vim.fn.printf(
      'sops encrypt --filename-override %s --output %s <(cat)', -- ugly work around /dev/stdout not existing in vim.system()
      vim.fn.shellescape(file),
      vim.fn.shellescape(file)),
  }, {
    stdin = api.nvim_buf_get_lines(buf, 0, -1, true),
  })

  local res = cmd:wait()

  if res.code > 0 then
    vim.notify('Could not encrypt ' .. vim.fn.fnamemodify(file, ':p:.') .. ': ' .. res.stderr, vim.log.levels.WARN)
    return
  end

  api.nvim_set_option_value('modified', false, { buf = buf })
  vim.notify('Saved encrypted file ' .. vim.fn.fnamemodify(file, ':p:.'), vim.log.levels.INFO)
end


-- @param buf number
local function setup_buffer(buf)
  if api.nvim_get_option_value('buftype', { buf = buf }) == 'acwrite' then
    -- was here already
    return
  end
  api.nvim_set_option_value('buftype', 'acwrite', { buf = buf })
  api.nvim_set_option_value('swapfile', false, { buf = buf }) -- encrypted data shouldn't be laying around....

  api.nvim_create_autocmd('BufWriteCmd', {
    group = ag,
    buffer = buf,
    callback = function(ev)
      write_buf(ev.buf)
    end,
  })
end

-- @param file string
-- @return string
local function load_buf(buf)
  local file = path_from_uri(api.nvim_buf_get_name(buf))
  local res = vim.system({ 'sops', 'decrypt', file }, { text = true }):wait()

  if res.code > 0 then
    vim.notify('Could not decrypt ' .. file .. ': ' .. res.stderr, vim.log.levels.WARN)
    return
  end

  vim.cmd.doautocmd({"BufReadPre", file})
  api.nvim_buf_set_lines(buf, 0, -1, true, vim.split(res.stdout, '\n'))
  setup_buffer(buf)
  vim.notify('Decrypted file ' .. vim.fn.fnamemodify(file, ':p:.'), vim.log.levels.INFO)

  vim.cmd.doautocmd({"BufReadPost", file})
end

api.nvim_create_autocmd('BufReadCmd', {
  group = ag,
  pattern = 'sops://*',
  callback = function(ev)
    local buf = ev.buf
    load_buf(buf)
  end,
})

api.nvim_create_user_command('SopsEdit', function(opts)
  local fargs = opts.fargs
  local file = nil
  local args = { args = {}, bang = opts.bang }
  if #fargs == 0 then
    file = api.nvim_buf_get_name(0)
    if string.sub(file, 0, string.len('sops://')) ~= 'sops://' then
      args.args = { 'sops://' .. file }
    end
  else
    file = fargs[1]
    if string.sub(file, 0, string.len('sops://')) ~= 'sops://' then
      file = 'sops://' .. vim.fn.fnamemodify(file, ':p')
    end
    args.args = { file }
  end

  vim.cmd.edit(args)
end, {
  bang = true,
  complete = 'file',
  nargs = '?',
})
