local M = {}

local map = vim.keymap.set

function M.lsp(buffer, client)
  local opts = {
    buffer = buffer,
    silent = true,
  }

  map("n", "gd", vim.lsp.buf.definition, opts)
  map("n", "gD", vim.lsp.buf.declaration, opts)
  map("n", "gr", vim.lsp.buf.references, opts)
  map("n", "gi", vim.lsp.buf.implementation, opts)
  map("n", "K", vim.lsp.buf.hover, opts)
  map("n", "<leader>rn", vim.lsp.buf.rename, opts)
  map("n", "<leader>ca", vim.lsp.buf.code_action, opts)

  local function run_in_terminal(cmd)
    vim.cmd("botright split | terminal " .. cmd)
  end

  local function set_c_bindings()
    map("n", "<leader>hs", "<cmd>ClangdSwitchSourceHeader<cr>", opts)

    map("n", "<F5>", function()
      vim.cmd("make")
      vim.cmd("cwindow")
    end, opts)

    map("n", "<F6>", function()
      run_in_terminal("gmake test")
    end, opts)
  end

  local function set_python_bindings()
    map("n", "<F6>", function()
      run_in_terminal("pytest")
    end, opts)
  end

  local function set_npm_bindings()
    map("n", "<F5>", function()
      run_in_terminal("npm run build")
    end, opts)
    map("n", "<F6>", function()
      run_in_terminal("npm test")
    end, opts)
    map("n", "<F7>", function()
      run_in_terminal("npm start")
    end, opts)
  end

  if client then
    if client.name == "clangd" then
      set_c_bindings()
    elseif client.name == "basedpyright" or client.name == "pyright" or client.name == "pylsp" then
      set_python_bindings()
    elseif client.name == "tsserver" or client.name == "vtsls" or client.name == "typescript_tools" then
      set_npm_bindings()
    end
  end
end

function M.gitsigns()
  map("n", "<leader>tb", require("gitsigns").toggle_current_line_blame)
end

function M.global()
  local opts = {
    silent = true,
  }

  map("n", "ö", "}", opts)
  map("n", "ä", "{", opts)

  -- besseres Verhalten von <PageUp und <PageDown>
  -- https://vimrc-dissection.blogspot.com/2009/02/fixing-pageup-and-pagedown.html
  map("n", "<PageUp>", "1000<C-u>", opts)
  map("n", "<PageDown>", "1000<C-d>", opts)
  map("i", "<PageUp>", "<C-o>1000<C-u>", opts)
  map("i", "<PageDown>", "<C-o>1000<C-d>", opts)

  -- Verschiebe Zeilen mit Alt+j / Alt+k
  -- MacOS: Terminal → Einstellungen → Profile → Tastatur → "Option als Meta-Taste verwenden"
  map("n", "<M-j>", ":m .+1<CR>==", opts)
  map("n", "<M-k>", ":m .-2<CR>==", opts)
  map("i", "<M-j>", "<Esc>:m .+1<CR>==gi", opts)
  map("i", "<M-k>", "<Esc>:m .-2<CR>==gi", opts)
  map("x", "<M-j>", ":m '>+1<CR>gv=gv", opts)
  map("x", "<M-k>", ":m '<-2<CR>gv=gv", opts)

  -- besseres Verhalten in umbrochenen Texten
  map({ "n", "x", "o" }, "j", function()
    return vim.v.count == 0 and "gj" or "j"
  end, { expr = true, silent = true })

  map({ "n", "x", "o" }, "k", function()
    return vim.v.count == 0 and "gk" or "k"
  end, { expr = true, silent = true })

  -- Kombinationen mit Leader-Key
  map("n", "<leader>K", function()
    vim.diagnostic.open_float({ scope = "cursor", border = "rounded" })
  end, opts)

  map("n", "<leader>q", vim.diagnostic.setloclist, opts)

  map("n", "<leader>ts", function()
    vim.opt_local.spell = not vim.opt_local.spell:get()
  end, { desc = "Toggle spell checking" })

  map("n", "[d", function()
    vim.diagnostic.jump_prev({ wrap = false })
  end, opts)

  map("n", "]d", function()
    vim.diagnostic.jump_next({ wrap = false })
  end, opts)

  map("n", "[e", function()
    vim.diagnostic.jump_prev({
      severity = vim.diagnostic.severity.ERROR,
      wrap = false,
    })
  end, opts)

  map("n", "]e", function()
    vim.diagnostic.jump_next({
      severity = vim.diagnostic.severity.ERROR,
      wrap = false,
    })
  end, opts)
end

return M
