-- [[ Basic Keymaps ]]
--  See `:help vim.keymap.set()`

-- Clear highlights on search when pressing <Esc> in normal mode
--  See `:help hlsearch`
vim.keymap.set('n', '<Esc>', '<cmd>nohlsearch<CR><esc>')

-- Quickfix
vim.keymap.set('n', '[q', '<cmd>cprev<cr>', { desc = 'Previous quickfix' })
vim.keymap.set('n', ']q', '<cmd>cnext<cr>', { desc = 'Next quickfix' })

-- Exit terminal mode in the builtin terminal with a shortcut that is a bit easier
-- for people to discover. Otherwise, you normally need to press <C-\><C-n>, which
-- is not what someone will guess without a bit more experience.
--
-- NOTE: This won't work in all terminal emulators/tmux/etc. Try your own mapping
-- or just use <C-\><C-n> to exit terminal mode
vim.keymap.set('t', '<Esc><Esc>', '<C-\\><C-n>', { desc = 'Exit terminal mode' })

-- TIP: Disable arrow keys in normal mode
-- vim.keymap.set('n', '<left>', '<cmd>echo "Use h to move!!"<CR>')
-- vim.keymap.set('n', '<right>', '<cmd>echo "Use l to move!!"<CR>')
-- vim.keymap.set('n', '<up>', '<cmd>echo "Use k to move!!"<CR>')
-- vim.keymap.set('n', '<down>', '<cmd>echo "Use j to move!!"<CR>')

--  See `:help wincmd` for a list of all window commands
vim.keymap.set('n', '<C-h>', '<C-w><C-h>', { desc = 'Move focus to the left window' })
vim.keymap.set('n', '<C-l>', '<C-w><C-l>', { desc = 'Move focus to the right window' })
vim.keymap.set('n', '<C-j>', '<C-w><C-j>', { desc = 'Move focus to the lower window' })
vim.keymap.set('n', '<C-k>', '<C-w><C-k>', { desc = 'Move focus to the upper window' })

-- <leader>w
-- Some more convenient keymaps for split management
vim.keymap.set('n', '<leader>wv', '<C-w>v', { desc = 'Window split vertically' })
vim.keymap.set('n', '<leader>w<Bslash>', '<C-w>v', { desc = 'Window split vertically' })
vim.keymap.set('n', '<leader>w|', '<C-w>v', { desc = 'Window split vertically' })
vim.keymap.set('n', '<leader>wh', '<C-w>s', { desc = 'Window split horizontally' })
vim.keymap.set('n', '<leader>w-', '<C-w>s', { desc = 'Window split horizontally' })
vim.keymap.set('n', '<leader>we', '<C-w>=', { desc = 'Make Window splits equal size' })
vim.keymap.set('n', '<leader>w=', '<C-w>=', { desc = 'Make Window splits equal size' })
vim.keymap.set('n', '<leader>wq', '<cmd>close<CR>', { desc = 'Quit window' })
vim.keymap.set('n', '<leader>wo', '<C-w>o', { desc = 'Close other windows' })
vim.keymap.set('n', '<leader>wm', '<C-w>o', { desc = 'Maximize' })
vim.keymap.set('n', '<leader>wH', '<C-w>H', { desc = 'Move window left' })
vim.keymap.set('n', '<leader>wL', '<C-w>L', { desc = 'Move window right' })
vim.keymap.set('n', '<leader>wJ', '<C-w>J', { desc = 'Move window down' })
vim.keymap.set('n', '<leader>wK', '<C-w>K', { desc = 'Move window up' })
vim.keymap.set('n', '<leader>wn', '<cmd>vnew<CR>', { desc = 'Window vsplit with new buffer' })
vim.keymap.set('n', '<leader>wfh', '<cmd>set winfixheight<CR>', { desc = 'Window fix height' })
vim.keymap.set('n', '<leader>wfw', '<cmd>set winfixwidth<CR>', { desc = 'Window fix width' })

local function toggle_split()
  local buf = vim.api.nvim_get_current_buf()
  local pos = vim.api.nvim_win_get_cursor(0)
  local height_pct = vim.api.nvim_win_get_height(0) / vim.o.lines
  local width_pct = vim.api.nvim_win_get_width(0) / vim.o.columns

  vim.api.nvim_win_close(0, false)

  -- Decide which split to use based on dimensions of current window
  if height_pct > width_pct then
    vim.cmd('split')
  else
    vim.cmd('vsplit')
  end

  -- Restore buffer and cursor
  vim.api.nvim_set_current_buf(buf)
  vim.api.nvim_win_set_cursor(0, pos)
end

vim.keymap.set('n', '<leader>w<space>', toggle_split, { noremap = true, silent = true })

-- blackhole single x
vim.keymap.set('n', 'x', '"_x')

-- Put change into the blackhole register
vim.keymap.set('n', 'c', '"_c')

-- X blackhole delete
vim.keymap.set({ 'n', 'v' }, 'X', '"_d')

-- Map jk as alternate escape sequence
vim.keymap.set({ 'i', 'c' }, 'jk', '<Esc>', { desc = 'Exit insert / cmd mode with jk' })

-- Sloppy aliases for accidental capital commands
-- in 0.10, could use vim.keymap.set("ca", ...)
vim.api.nvim_create_user_command('Qa', 'qa', {})
vim.api.nvim_create_user_command('Q', 'q', {})
vim.api.nvim_create_user_command('W', 'w', {})
vim.api.nvim_create_user_command('X', 'x', {})

-- Remap q to Q so I'm not accidentally recording macros all the time
vim.keymap.set('n', 'q', '<nop>')
vim.keymap.set('n', 'Q', 'q', { desc = 'Record macro' })
vim.keymap.set('n', '<M-q>', 'Q', { desc = 'Replay last register' })

-- Disable the command line window
vim.keymap.set('n', 'q:', ':q')

-- And now really, really disable it
-- https://www.reddit.com/r/neovim/comments/15bvtr4/what_is_that_command_line_mode_where_i_see_the/
vim.api.nvim_create_autocmd('CmdWinEnter', {
  group = vim.api.nvim_create_augroup('CWE', { clear = true }),
  pattern = '*',
  callback = function() vim.api.nvim_feedkeys(vim.api.nvim_replace_termcodes(':q<CR>:', true, false, true), 'm', false) end,
})

-- Unamp g? (don't need rot-13)
vim.keymap.set({ 'n', 'x' }, 'g?', '<nop>')

-- Borrowed from LazyVim

-- better up/down
vim.keymap.set({ 'n', 'x' }, 'j', "v:count == 0 ? 'gj' : 'j'", { desc = 'Down', expr = true, silent = true })
vim.keymap.set({ 'n', 'x' }, '<Down>', "v:count == 0 ? 'gj' : 'j'", { desc = 'Down', expr = true, silent = true })
vim.keymap.set({ 'n', 'x' }, 'k', "v:count == 0 ? 'gk' : 'k'", { desc = 'Up', expr = true, silent = true })
vim.keymap.set({ 'n', 'x' }, '<Up>', "v:count == 0 ? 'gk' : 'k'", { desc = 'Up', expr = true, silent = true })

-- Change windows (most likely overridden by tmux plugin)
vim.keymap.set('n', '<C-h>', '<C-w>h', { desc = 'Go to Left Window', remap = true })
vim.keymap.set('n', '<C-j>', '<C-w>j', { desc = 'Go to Lower Window', remap = true })
vim.keymap.set('n', '<C-k>', '<C-w>k', { desc = 'Go to Upper Window', remap = true })
vim.keymap.set('n', '<C-l>', '<C-w>l', { desc = 'Go to Right Window', remap = true })

-- Resize window using <ctrl> arrow keys (mostly likely overridden by tmux plugin)
vim.keymap.set('n', '<M-k>', '<cmd>resize +2<cr>', { desc = 'Increase Window Height' })
vim.keymap.set('n', '<M-j>', '<cmd>resize -2<cr>', { desc = 'Decrease Window Height' })
vim.keymap.set('n', '<M-h>', '<cmd>vertical resize -2<cr>', { desc = 'Decrease Window Width' })
vim.keymap.set('n', '<M-l>', '<cmd>vertical resize +2<cr>', { desc = 'Increase Window Width' })

-- Moving lines
-- vim.keymap.set('v', '<A-Down>', ":m '>+1<cr>gv=gv", { desc = 'Move Down' })
-- vim.keymap.set('v', '<A-Up>', ":m '<-2<cr>gv=gv", { desc = 'Move Up' })

-- https://github.com/mhinz/vim-galore#saner-behavior-of-n-and-n
vim.keymap.set('n', 'n', "'Nn'[v:searchforward].'zv'", { expr = true, desc = 'Next Search Result' })
vim.keymap.set('x', 'n', "'Nn'[v:searchforward]", { expr = true, desc = 'Next Search Result' })
vim.keymap.set('o', 'n', "'Nn'[v:searchforward]", { expr = true, desc = 'Next Search Result' })
vim.keymap.set('n', 'N', "'nN'[v:searchforward].'zv'", { expr = true, desc = 'Prev Search Result' })
vim.keymap.set('x', 'N', "'nN'[v:searchforward]", { expr = true, desc = 'Prev Search Result' })
vim.keymap.set('o', 'N', "'nN'[v:searchforward]", { expr = true, desc = 'Prev Search Result' })

-- -- Next/prev tabs
vim.keymap.set('n', '<leader><tab>l', '<cmd>tablast<cr>', { desc = 'Last Tab' })
vim.keymap.set('n', '<leader><tab>o', '<cmd>tabonly<cr>', { desc = 'Close Other Tabs' })
vim.keymap.set('n', '<leader><tab>f', '<cmd>tabfirst<cr>', { desc = 'First Tab' })
vim.keymap.set('n', '<leader><tab>n', '<cmd>tabnew<cr>', { desc = 'New Tab' })
vim.keymap.set('n', '<leader><tab>]', '<cmd>tabnext<cr>', { desc = 'Next Tab' })
vim.keymap.set('n', '<leader><tab><tab>', '<cmd>tabnext<cr>', { desc = 'Next Tab' })
vim.keymap.set('n', '<leader><tab><S-tab>', '<cmd>tabprev<cr>', { desc = 'Previous Tab' })
vim.keymap.set('n', '<leader><tab>d', '<cmd>tabclose<cr>', { desc = 'Close Tab' })
vim.keymap.set('n', '<leader><tab>q', '<cmd>tabclose<cr>', { desc = 'Close Tab' })
vim.keymap.set('n', '<leader><tab>[', '<cmd>tabprevious<cr>', { desc = 'Previous Tab' })

-- Add undo break-points
vim.keymap.set('i', ',', ',<c-g>u')
vim.keymap.set('i', '.', '.<c-g>u')
vim.keymap.set('i', ';', ';<c-g>u')

-- Quickly go to the start/end of the line while in insert mode.
vim.keymap.set('i', '<C-a>', '<C-o>I', { desc = 'Go to the start of the line' })
vim.keymap.set('i', '<C-e>', '<C-o>A', { desc = 'Go to the end of the line' })

-- Poor man's vim-rsi:
vim.keymap.set('c', '<C-a>', '<Home>')
vim.keymap.set('c', '<C-e>', '<End>')

-- better indenting
vim.keymap.set('v', '<', '<gv')
vim.keymap.set('v', '>', '>gv')

-- save file
vim.keymap.set({ 'x', 'n', 's' }, '<C-s>', '<cmd>w<cr>', { desc = 'Save File' })

-- save file with undo point in insert mode
vim.keymap.set('i', '<C-s>', '<c-g>u<cmd>w<cr><esc>', { desc = 'Save File' })

-- diagnostic
local diagnostic_goto = function(next, severity)
  if vim.fn.has('nvim-0.11') == 1 then
    local count = next and 1 or -1
    severity = severity and vim.diagnostic.severity[severity] or nil
    return function() vim.diagnostic.jump({ severity = severity, count = count }) end
  else
    ---@diagnostic disable-next-line: deprecated
    local go = next and vim.diagnostic.goto_next or vim.diagnostic.goto_prev
    severity = severity and vim.diagnostic.severity[severity] or nil
    return function() go({ severity = severity }) end
  end
end
-- Diagnostic keymaps
vim.keymap.set('n', '<leader>cd', function() vim.diagnostic.open_float({ border = 'rounded' }) end, { desc = 'Diagnostic popup' })
vim.keymap.set('n', ']d', diagnostic_goto(true), { desc = 'Next Diagnostic' })
vim.keymap.set('n', '[d', diagnostic_goto(false), { desc = 'Prev Diagnostic' })
vim.keymap.set('n', ']e', diagnostic_goto(true, 'ERROR'), { desc = 'Next Error' })
vim.keymap.set('n', '[e', diagnostic_goto(false, 'ERROR'), { desc = 'Prev Error' })
vim.keymap.set('n', ']w', diagnostic_goto(true, 'WARN'), { desc = 'Next Warning' })
vim.keymap.set('n', '[w', diagnostic_goto(false, 'WARN'), { desc = 'Prev Warning' })

-- End LazyVim

-- Old comment keymap
vim.keymap.set('n', '<bslash>x', 'gcc', { desc = 'Comment toggle', remap = true })
vim.keymap.set({ 'v', 'x' }, '<bslash>x', 'gc', { desc = 'Comment toggle', remap = true })

-- map page up/down to ctrl-u/d
vim.keymap.set({ 'n', 'v', 'x' }, '<pageup>', '<c-u>')
vim.keymap.set({ 'n', 'v', 'x' }, '<pagedown>', '<c-d>')

-- map shift up/down to just up/down so it doesn't scroll jarringly
vim.keymap.set({ 'n', 'v', 'x' }, '<S-Up>', '<Up>')
vim.keymap.set({ 'n', 'v', 'x' }, '<S-Down>', '<Down>')

-- Undo all changes since last save
vim.keymap.set('n', '<S-u>', '<cmd>undo<CR>', { desc = 'Undo' })
vim.keymap.set('n', '<M-u>', '<cmd>earlier 1f<CR>', { desc = 'Undo to last saved' })
vim.keymap.set('n', '<M-r>', '<cmd>later 1f<CR>', { desc = 'Redo to last saved' })

-- Tools
vim.keymap.set('n', '<leader>l', '<cmd>Lazy<CR>', { desc = 'Lazy' })
vim.keymap.set('n', '<leader>cm', '<cmd>Mason<CR>', { desc = 'Mason' })

-- Next/prev buffer
vim.keymap.set('n', '[b', '<cmd>:bprevious<CR>', { desc = 'Previous buffer' })
vim.keymap.set('n', ']b', '<cmd>:bnext<CR>', { desc = 'Next buffer' })

-- <leader>b
vim.keymap.set('n', '<leader>bd', '<cmd>bd<CR>', { desc = 'Close buffer and window' })
vim.keymap.set('n', '<leader>bD', '<cmd>bd!<CR>', { desc = 'Force close buffer and window' })
vim.keymap.set('n', '<leader>bn', '<cmd>ene<CR>', { desc = 'New buffer and window' })
vim.keymap.set('n', '<leader>bv', '<cmd>vnew<CR>', { desc = 'New buffer in vsplit' })

-- Shortcut for surrounding a word (inner) with a '
vim.keymap.set('n', 'S', '<nop>') -- Don't keep S mapping
vim.keymap.set('n', 'Sq', "Siw'", { desc = "Wrap word with '", remap = true })
vim.keymap.set('n', 'Sp', 'SiW(', { desc = 'Wrap word with ()', remap = true })
vim.keymap.set('n', 'Sb', 'Saw}', { desc = 'Wrap word with {}', remap = true })

-- Swap to alternate buffer, less work that ctrl-6
vim.keymap.set('n', '<leader>,', '<C-6>', { desc = 'Alt buffer' })

-- quick replace of current word
vim.keymap.set({ 'n', 'v', 'x' }, '<leader>r', [[:%s/\<<C-r><C-w>\>/<C-r><C-w>/gI<Left><Left><Left>]], { desc = 'Replace current word' })

-- select last pasted lines
vim.keymap.set('n', 'gp', "'[V']", { desc = 'Select pasted lines' })
vim.keymap.set('n', 'vp', "'[V']", { desc = 'Select pasted lines' })

vim.keymap.set('n', '<leader>vh', '<cmd>nohl<CR>', { desc = 'Clear highlights' })
vim.keymap.set('n', '<leader>vr', vim.cmd.checktime, { desc = 'Refresh files' })

vim.keymap.set('n', '<leader>cI', '<cmd>InspectTree<cr>', { desc = 'Inspect TS Tree' })

vim.keymap.set('n', '<leader>ci', '<cmd>Inspect<cr>', { desc = 'Inspect' })

-- vim.keymap.set('n', '<leader>cI', function()
--   local function display_lsp_info(client, _)
--     if not client then return end
--     -- Create a temporary buffer to show the configuration
--     local buf = vim.api.nvim_create_buf(false, true)
--     vim.api.nvim_open_win(buf, true, {
--       relative = 'editor',
--       width = math.floor(vim.o.columns * 0.75),
--       height = math.floor(vim.o.lines * 0.90),
--       col = math.floor(vim.o.columns * 0.125),
--       row = math.floor(vim.o.lines * 0.05),
--       style = 'minimal',
--       border = 'rounded',
--       title = ' ' .. (client.name:gsub('^%l', string.upper)) .. ': LSP Configuration ',
--       title_pos = 'center',
--     })
--
--     local lines = {}
--     table.insert(lines, 'Client: ' .. client.name)
--     table.insert(lines, 'ID: ' .. client.id)
--     table.insert(lines, '')
--     table.insert(lines, 'Configuration:')
--
--     local config_lines = vim.split(vim.inspect(client.config), '\n')
--     vim.list_extend(lines, config_lines)
--
--     -- Set the lines in the buffer
--     vim.api.nvim_buf_set_lines(buf, 0, -1, false, lines)
--
--     -- Set buffer options
--     vim.bo[buf].modifiable = false
--     vim.bo[buf].filetype = 'lua'
--     vim.bo[buf].bh = 'delete'
--     vim.diagnostic.enable(false, { bufnr = buf })
--
--     vim.api.nvim_buf_set_keymap(buf, 'n', 'q', ':q<CR>', { silent = true })
--   end
--
--   local clients = vim.lsp.get_clients()
--
--   if #clients == 1 then
--     display_lsp_info(clients[1])
--   else
--     vim.ui.select(clients, {
--       prompt = 'Pick language server: ',
--       format_item = function(item) return item.name end,
--     }, display_lsp_info)
--   end
-- end, { desc = 'Inspect LSP' })

-- Duplicate and comment out
vim.keymap.set('n', 'yc', 'yy<cmd>normal gcc<CR>p')

local function cycle_diff_algorithm()
  local algorithms = { 'myers', 'minimal', 'patience', 'histogram' }

  local function index_of(list, value)
    for i, v in ipairs(list) do
      if v == value then return i end
    end
    return nil -- not found
  end

  local algo_index = index_of(algorithms, vim.o.diffopt:match('algorithm:([^,]+)'))

  if not algo_index then
    vim.notify("Can't find current algorithm in vim.o.diffopt")
    return
  end

  local new_algo = algorithms[(algo_index % #algorithms) + 1]
  vim.opt.diffopt:remove('algorithm:' .. algorithms[algo_index])
  vim.opt.diffopt:append('algorithm:' .. new_algo)
  vim.notify('Algorithm set too: ' .. new_algo)
end

vim.keymap.set('n', '<leader>cDa', cycle_diff_algorithm, {
  desc = 'Cycle diffopt algorithm (myers/minimal/patience/histogram)',
})

vim.keymap.set('n', '<leader>cDl', function()
  local linematch = vim.o.diffopt:match('(linematch:%d+)')
  if linematch then
    vim.opt.diffopt:remove(linematch)
    vim.notify(linematch .. ' removed')
  else
    linematch = 'linematch:40'
    vim.opt.diffopt:append(linematch)
    vim.notify(linematch .. ' added')
  end
end, { desc = 'Toggle linematch' })

-- Debugging key
vim.keymap.set('n', '<Bslash>d', function() end, { desc = 'debugging function' })

-- vim: ts=2 sts=2 sw=2 et
