if vim.g.did_load_autocommands_plugin then
  return
end
vim.g.did_load_autocommands_plugin = true

local api = vim.api
local wk = require('which-key')

local tempdirgroup = api.nvim_create_augroup('tempdir', { clear = true })
-- Do not set undofile for files in /tmp
api.nvim_create_autocmd('BufWritePre', {
  pattern = '/tmp/*',
  group = tempdirgroup,
  callback = function()
    vim.cmd.setlocal('noundofile')
  end,
})

-- Disable spell checking in terminal buffers
local nospell_group = api.nvim_create_augroup('nospell', { clear = true })
api.nvim_create_autocmd('TermOpen', {
  group = nospell_group,
  callback = function()
    vim.wo[0].spell = false
  end,
})

-- LSP
local keymap = vim.keymap

local function preview_location_callback(_, result)
  if result == nil or vim.tbl_isempty(result) then
    return nil
  end
  local buf, _ = vim.lsp.util.preview_location(result[1])
  if buf then
    local cur_buf = vim.api.nvim_get_current_buf()
    vim.bo[buf].filetype = vim.bo[cur_buf].filetype
  end
end

local function peek_definition()
  local params = vim.lsp.util.make_position_params()
  return vim.lsp.buf_request(0, 'textDocument/definition', params, preview_location_callback)
end

local function peek_type_definition()
  local params = vim.lsp.util.make_position_params()
  return vim.lsp.buf_request(0, 'textDocument/typeDefinition', params, preview_location_callback)
end

--- Don't create a comment string when hitting <Enter> on a comment line
vim.api.nvim_create_autocmd('BufEnter', {
  group = vim.api.nvim_create_augroup('DisableNewLineAutoCommentString', {}),
  callback = function()
    vim.opt.formatoptions = vim.opt.formatoptions - { 'c', 'r', 'o' }
  end,
})

vim.api.nvim_create_autocmd('LspAttach', {
  group = vim.api.nvim_create_augroup('UserLspConfig', {}),
  callback = function(ev)
    local bufnr = ev.buf
    local client = vim.lsp.get_client_by_id(ev.data.client_id)

    -- Attach plugins
    -- require('nvim-navic').attach(client, bufnr)

    vim.cmd.setlocal('signcolumn=yes')
    vim.bo[bufnr].bufhidden = 'hide'

    -- Enable completion triggered by <c-x><c-o>
    vim.bo[bufnr].omnifunc = 'v:lua.vim.lsp.omnifunc'
    local function desc(description)
      return { noremap = true, silent = true, buffer = bufnr, desc = description }
    end

    -- Set name for the <space>l prefix
    keymap.set('n', 'gD', vim.lsp.buf.declaration, { desc = 'lsp [g]o to [D]eclaration' })
    keymap.set('n', 'gd', vim.lsp.buf.definition, { desc = 'lsp [g]o to [d]efinition' })
    keymap.set('n', 'K', vim.lsp.buf.hover, { desc = '[lsp] hover' })
    keymap.set('n', 'gi', vim.lsp.buf.implementation, { desc = 'lsp [g]o to [i]mplementation' })
    keymap.set('n', '<leader>ls', vim.lsp.buf.signature_help, { desc = '[l]sp [s]ignature help' })
    keymap.set('n', '<M-CR>', vim.lsp.buf.code_action, { desc = '[lsp] code action' })
    keymap.set('n', '<leader>ll', vim.lsp.codelens.run, { desc = '[l]sp run code [l]ens' })
    keymap.set('n', 'gr', vim.lsp.buf.references, { desc = 'lsp [g]et [r]eferences' })

    wk.add({
      { "<leader>l", group = "[l]sp" },
      { "<leader>lR", "<cmd>vim.lsp.codelens.refresh<cr>", desc = "[l]sp code lenses [R]efresh" },
      { "<leader>ld", "<cmd>vim.lsp.buf.document_symbol<cr>", desc = "[l]sp [d]ocument symbol" },
      { "<leader>lF", function() vim.lsp.buf.format { async = true } end, desc = "[l]sp [F]ormat buffer" },
      { "<leader>lg", group = "[g]o to" },
      { "<leader>lgt", "<cmd>vim.lsp.buf.type_definition<cr>", desc = "[l]sp [g]o to [t]ype definition" },
      { "<leader>lp", group = "[p]eek" },
      { "<leader>lpd", peek_definition, desc = "[l]sp [p]eek [d]efinition" },
      { "<leader>lpt", peek_type_definition, desc = "[l]sp [p]eek [t]ype definition" },
      { "<leader>lr", "<cmd>vim.lsp.buf.rename<cr>", desc = "[l]sp [r]ename" },
      { "<leader>lw", group = "[w]orkspace" },
      { "<leader>lwa", "<cmd>vim.lsp.buf.add_workspace_folder<cr>", desc = "[l]sp add [w]orksp[a]ce folder" },
      { "<leader>lwl", function() vim.print(vim.lsp.buf.list_workspace_folders()) end, desc = "[l]sp [w]orkspace folders [l]ist" },
      { "<leader>lwq", "<cmd>vim.lsp.buf.workspace_symbol<cr>", desc = "[l]sp [w]orkspace symbol [q]" },
      { "<leader>lwr", "<cmd>vim.lsp.buf.remove_workspace_folder<cr>", desc = "[l]sp [w]orkspace folder [r]emove" },
    })
    if client ~= nil and client.server_capabilities.inlayHintProvider then
      -- keymap.set('n', '<space>lh', function()
      wk.add({
        "<leader>lh",
        function()
          local current_setting = vim.lsp.inlay_hint.is_enabled(bufnr)
            vim.lsp.inlay_hint.enable(bufnr, not current_setting)
        end, desc = '[l]sp toggle inlay [h]ints'
      })
    end
    -- TODO: InlayHint setting not working - the code below is the original one
    -- if client.server_capabilities.inlayHintProvider then
    --   keymap.set('n', '<space>lh', function()
    --     local current_setting = vim.lsp.inlay_hint.is_enabled(bufnr)
    --     vim.lsp.inlay_hint.enable(bufnr, not current_setting)
    --   end, desc('[l]sp toggle inlay [h]ints'))
    -- end

    -- Auto-refresh code lenses
    if not client then
      return
    end
    local function buf_refresh_codeLens()
      vim.schedule(function()
        if client.server_capabilities.codeLensProvider then
          vim.lsp.codelens.refresh()
          return
        end
      end)
    end
    local group = api.nvim_create_augroup(string.format('lsp-%s-%s', bufnr, client.id), {})
    if client.server_capabilities.codeLensProvider then
      vim.api.nvim_create_autocmd({ 'InsertLeave', 'BufWritePost', 'TextChanged' }, {
        group = group,
        callback = buf_refresh_codeLens,
        buffer = bufnr,
      })
      buf_refresh_codeLens()
    end
  end,
})

-- More examples, disabled by default

-- Toggle between relative/absolute line numbers
-- Show relative line numbers in the current buffer,
-- absolute line numbers in inactive buffers
-- local numbertoggle = api.nvim_create_augroup('numbertoggle', { clear = true })
-- api.nvim_create_autocmd({ 'BufEnter', 'FocusGained', 'InsertLeave', 'CmdlineLeave', 'WinEnter' }, {
--   pattern = '*',
--   group = numbertoggle,
--   callback = function()
--     if vim.o.nu and vim.api.nvim_get_mode().mode ~= 'i' then
--       vim.opt.relativenumber = true
--     end
--   end,
-- })
-- api.nvim_create_autocmd({ 'BufLeave', 'FocusLost', 'InsertEnter', 'CmdlineEnter', 'WinLeave' }, {
--   pattern = '*',
--   group = numbertoggle,
--   callback = function()
--     if vim.o.nu then
--       vim.opt.relativenumber = false
--       vim.cmd.redraw()
--     end
--   end,
-- })