local opts = { silent = true }

-- ; as :
vim.keymap.set({'n', 'v'}, ';', ':')

-- leave insert mode via jj
vim.keymap.set('i', 'jj', '<ESC>', opts)

-- j/k with wraps
vim.keymap.set({'n', 'v'}, 'j', 'gj', opts)
vim.keymap.set({'n', 'v'}, 'k', 'gk', opts)

-- leave insert mode with <ESC><ESC>
vim.keymap.set('t', '<ESC><ESC>', '<C-\\><C-N>', opts)

-- select pasted text
vim.keymap.set('n', 'gp', '`[v`]', opts)

-- pickers
vim.keymap.set('n', '<C-P>', '<cmd>lua project_files()<CR>', opts)
vim.keymap.set('n', '<C-O>', '<cmd>Telescope buffers<CR>', opts)
vim.keymap.set('n', '<Leader>fg', '<cmd>lua commit_files()<CR>', opts)
vim.keymap.set('n', '<Leader>ff', '<cmd>Telescope live_grep<CR>', opts)
vim.keymap.set('n', '<Leader>fr', '<cmd>Telescope resume<CR>', opts)
vim.keymap.set('n', '<Leader>fp', '<cmd>Telescope pickers<CR>', opts)

-- jumplist navigation
vim.keymap.set('n', '[f', '<C-O>', opts)
vim.keymap.set('n', ']f', '<C-I>', opts)

-- quickfix navigation
vim.keymap.set('n', '[q', '<cmd>cprevious<CR>', opts)
vim.keymap.set('n', ']q', '<cmd>cnext<CR>', opts)

-- conflicts navigation
vim.keymap.set('n', '[x', '?^[<lt>=>]\\{7}<CR>', opts)
vim.keymap.set('n', ']x', '/^[<lt>=>]\\{7}<CR>', opts)

-- hunk navigation
vim.keymap.set('n', '[h', '<cmd>Gitsigns prev_hunk<CR>', opts)
vim.keymap.set('n', ']h', '<cmd>Gitsigns next_hunk<CR>', opts)

-- window navigation
vim.keymap.set('n', '[w', '<C-w>W', opts)
vim.keymap.set('n', ']w', '<C-w>w', opts)

-- cursor display
vim.keymap.set('n', '<Leader>c', '<cmd>set cursorline! cursorcolumn!<CR>', opts)

-- notification history
vim.keymap.set('n', '<Leader>n', require('mini.notify').show_history, opts)

-- git
-- open status in current split without special flag (so navigating away closes it)
-- https://github.com/tpope/vim-fugitive/issues/1296
vim.keymap.set('n', '<Leader>gs', '<cmd>Gedit :<CR>', opts)
vim.keymap.set('n', '<Leader>gb', '<cmd>Telescope git_branches<CR>', opts)
vim.keymap.set('n', '<Leader>gd', '<cmd>Gitsigns toggle_deleted<CR>', opts)

-- undotree
vim.keymap.set('n', '<Leader>ut', '<cmd>UndotreeToggle<CR>', opts)

-- opening terminals
vim.keymap.set('n', '<Leader>tv', '<cmd>vertical term $SHELL<CR>', opts)
vim.keymap.set('n', '<Leader>to', '<cmd>term $SHELL<CR>', opts)

-- delete buffer
vim.keymap.set('n', '<Leader>q', '<cmd>lua MiniBufremove.delete()<CR>', opts)
vim.keymap.set('n', '<Leader>Q', '<cmd>lua MiniBufremove.delete(0, true)<CR>', opts)

-- split/join
vim.keymap.set('n', '<Leader>s', '<cmd>TSJSplit<CR>', opts)
vim.keymap.set('n', '<Leader>j', '<cmd>TSJJoin<CR>', opts)

-- diagnostics
vim.keymap.set('n', '<space>e', '<cmd>lua vim.diagnostic.open_float()<CR>', opts)
vim.keymap.set('n', '[d', '<cmd>lua vim.diagnostic.goto_prev()<CR>', opts)
vim.keymap.set('n', ']d', '<cmd>lua vim.diagnostic.goto_next()<CR>', opts)
vim.keymap.set('n', '<space>q', '<cmd>lua vim.diagnostic.setqflist({ severity = { min = vim.diagnostic.severity.WARN } })<CR>', opts)

-- LSP-specific
vim.api.nvim_create_autocmd('LspAttach', {
  callback = function(args)
    local client = vim.lsp.get_client_by_id(args.data.client_id)

    if client.name == 'dprint' or client.name == 'eslint' then
      -- mappings should have been attached by typescript and re-attaching can
      -- overwrite the typescript specific overrides
      return
    end

    local opts = { silent = true, buffer = args.buf }

    vim.keymap.set('n', 'gD', '<cmd>lua vim.lsp.buf.declaration()<CR>', opts)
    vim.keymap.set('n', 'gd', '<cmd>Telescope lsp_definitions<CR>', opts)
    vim.keymap.set('n', 'K', '<cmd>lua vim.lsp.buf.hover()<CR>', opts)
    vim.keymap.set('n', 'gi', '<cmd>Telescope lsp_implementations<CR>', opts)
    vim.keymap.set({'n', 'i'}, '<C-k>', '<cmd>lua vim.lsp.buf.signature_help()<CR>', opts)
    vim.keymap.set('n', '<space>D', '<cmd>Telescope lsp_type_definitions<CR>', opts)
    vim.keymap.set('n', '<space>rn', '<cmd>lua vim.lsp.buf.rename()<CR>', opts)
    vim.keymap.set('n', '<space>ca', '<cmd>lua vim.lsp.buf.code_action()<CR>', opts)
    vim.keymap.set('v', '<space>ca', '<cmd>lua vim.lsp.buf.range_code_action()<CR>', opts)
    vim.keymap.set('n', 'gr', '<cmd>Telescope lsp_references<CR>', opts)
    vim.keymap.set('n', '<Leader>fs', '<cmd>Telescope lsp_document_symbols<CR>', opts)

    if client.name == 'tsserver' then
      -- exclude import statements from reference search (may have false positives)
      vim.keymap.set('n', 'gr', '<cmd>Telescope lsp_references default_text=!import\\ <CR>', opts)
      vim.keymap.set('n', 'gD', '<cmd>Telescope lsp_definitions<CR>', opts)
      vim.keymap.set('n', 'gd', '<cmd>TypescriptGoToSourceDefinition<CR>', opts)
    end
  end,
})