local servers = {
	ansiblels = {},
	arduino_language_server = {},
	astro = {},
	bashls = {},
	beancount = {},
	clangd = {},
	cssls = {},
	docker_compose_language_service = {},
	dockerls = {},
	emmet_ls = {},
	eslint = {},
	gopls = {},
	julials = {},
	jsonls = {},
	ltex = { autostart = false },
	lua_ls = {
		settings = {
			Lua = {
				diagnostics = { globals = { "vim" } },
				-- enable when working on neovim stuff. Takes *long* to load
				-- workspace = { library = vim.api.nvim_get_runtime_file("", true) },
				telemetry = { enable = false },
				hint = {
					enable = true,
					setType = true,
				},
			},
		},
	},
	marksman = {},
	basedpyright = {},
	ruff = {},
	serve_d = {},
	taplo = {},
	texlab = {},
	tinymist = {},
	ts_ls = {},
	yamlls = {},
}

return {
	-- lsp setup
	{
		"junnplus/lsp-setup.nvim",
		dependencies = {
			{
				"neovim/nvim-lspconfig",
				-- will sometimes not keep up with lsp changes if set to stable
				version = false,
			},
			{
				"williamboman/mason.nvim",
				cmd = {
					"Mason",
					"MasonInstall",
					"MasonUninstall",
					"MasonUninstallAll",
					"MasonLog",
					"MasonUpdate",
				},
				build = ":MasonUpdate",
				keys = {
					{ "<leader>vm", ":Mason<cr>", desc = "Mason" },
				},
			},
			{
				"williamboman/mason-lspconfig.nvim",
				cmd = { "LspInstall", "LspUninstall" },
			},
		},
		event = { "BufReadPost", "BufNewFile", "BufWritePre" },
		config = function()
			vim.diagnostic.config({ virtual_text = true })
			vim.fn.sign_define("DiagnosticSignError", { text = "✘", texthl = "DiagnosticSignError" })
			vim.fn.sign_define("DiagnosticSignWarn", { text = "", texthl = "DiagnosticSignWarn" })
			vim.fn.sign_define("DiagnosticSignInfo", { text = "", texthl = "DiagnosticSignInfo" })
			vim.fn.sign_define("DiagnosticSignHint", { text = "", texthl = "DiagnosticSignHint" })

			local lsp = require("lsp-setup")

			local function on_attach(_, bufnr)
				local map = vim.keymap.set
				map(
					"n",
					"[d",
					"<cmd>lua vim.diagnostic.goto_prev()<cr>",
					{ buffer = bufnr, desc = "Previous diagnostic" }
				)
				map("n", "]d", "<cmd>lua vim.diagnostic.goto_next()<cr>", { buffer = bufnr, desc = "Next diagnostic" })
				map(
					"n",
					"[D",
					"<cmd>lua vim.diagnostic.goto_prev({severity = vim.diagnostic.severity.ERROR})<cr>",
					{ buffer = bufnr, desc = "Previous error" }
				)
				map(
					"n",
					"]D",
					"<cmd>lua vim.diagnostic.goto_next({severity = vim.diagnostic.severity.ERROR})<cr>",
					{ buffer = bufnr, desc = "Next error" }
				)

				if require("core.util").is_available("which-key") then
					require("which-key").add({ "<localleader>l", group = "language" })
				end
				map(
					"n",
					"<localleader>ld",
					"<cmd>lua vim.diagnostic.open_float()<cr>",
					{ buffer = bufnr, desc = "Line diagnostics" }
				)
				map("n", "<localleader>li", function()
					vim.lsp.inlay_hint.enable(not vim.lsp.inlay_hint.is_enabled())
				end, { buffer = bufnr, desc = "Inlay hints" })
				map(
					"n",
					"<localleader>la",
					"<cmd>lua vim.lsp.buf.code_action()<cr>",
					{ buffer = bufnr, desc = "Codeactions" }
				)
				map(
					"n",
					"<localleader>ln",
					"<cmd>lua vim.lsp.buf.rename()<cr>",
					{ buffer = bufnr, desc = "Rename element" }
				)
				if vim.fn.exists(":Glance") then
					map("n", "<localleader>lr", "<cmd>Glance references<cr>", { buffer = bufnr, desc = "References" })
					map("n", "<localleader>lf", "<cmd>Glance definitions<cr>", { buffer = bufnr, desc = "Definition" })
					map(
						"n",
						"<localleader>lt",
						"<cmd>Glance type_definitions<cr>",
						{ buffer = bufnr, desc = "Type definition" }
					)
					map(
						"n",
						"<localleader>lm",
						"<cmd>Glance implementations<cr>",
						{ buffer = bufnr, desc = "Implementation" }
					)
				elseif vim.fn.exists(":Telescope") then
					map(
						"n",
						"<localleader>lr",
						"<cmd>Telescope lsp_references<cr>",
						{ buffer = bufnr, desc = "References" }
					)
					map(
						"n",
						"<localleader>lf",
						"<cmd>Telescope lsp_definitions<cr>",
						{ buffer = bufnr, desc = "Definition" }
					)
					map(
						"n",
						"<localleader>lt",
						"<cmd>Telescope lsp_type_definitions<cr>",
						{ buffer = bufnr, desc = "Type definition" }
					)
					map(
						"n",
						"<localleader>lm",
						"<cmd>Telescope lsp_implementations<cr>",
						{ buffer = bufnr, desc = "Implementation" }
					)
				else
					map(
						"n",
						"<localleader>lr",
						"<cmd>lua vim.lsp.buf.references()<cr>",
						{ buffer = bufnr, desc = "References" }
					)
					map(
						"n",
						"<localleader>lf",
						"<cmd>lua vim.lsp.buf.definition()<cr>",
						{ buffer = bufnr, desc = "Definition" }
					)
					map(
						"n",
						"<localleader>lt",
						"<cmd>lua vim.lsp.buf.type_definition()<cr>",
						{ buffer = bufnr, desc = "Type definition" }
					)
					map(
						"n",
						"<localleader>lm",
						"<cmd>lua vim.lsp.buf.implementation()<cr>",
						{ buffer = bufnr, desc = "Implementation" }
					)
				end
				map("n", "K", "<cmd>lua vim.lsp.buf.hover()<cr>", { buffer = bufnr, desc = "Hover definition" })
				map(
					"n",
					"<localleader>lc",
					"<cmd>lua vim.lsp.buf.declaration()<cr>",
					{ buffer = bufnr, desc = "Declaration" }
				)
				map(
					"n",
					"<localleader>ls",
					"<cmd>lua vim.lsp.buf.signature_help()<cr>",
					{ buffer = bufnr, desc = "Signature help" }
				)
				map("n", "<localleader>lo", function()
					vim.diagnostic.enable(not vim.diagnostic.is_enabled())
				end, { buffer = bufnr, desc = "Toggle Diagnostics" })
			end

			-- Display diagnostics as virtual text only if not in insert mode
			-- /r/neovim/comments/12inp4c/disable_diagnostics_virtual_text_when_in_insert/jqqifwk/
			vim.api.nvim_create_autocmd("InsertEnter", {
				callback = function()
					vim.diagnostic.config({ virtual_text = false })
				end,
			})
			vim.api.nvim_create_autocmd("InsertLeave", {
				callback = function()
					vim.diagnostic.config({ virtual_text = true })
				end,
			})

			lsp.setup({
				default_mappings = false,
				servers = servers,
				on_attach = on_attach,
				inlay_hints = {
					enabled = vim.fn.has("nvim-0.10") == true and true or false,
				},
			})

			local lspconfig = require("lspconfig")
			lspconfig.nushell.setup({})

			lspconfig.marksman.setup({
				filetypes = { "markdown", "quarto" },
				on_attach = function(client, bufnr)
					-- TODO: for some reason this stays true even after rootdir switch?
					if client.config.in_zk_notebook then
						local default_handler = vim.diagnostic.handlers.virtual_text
						vim.diagnostic.handlers.virtual_text = {
							show = function(namespace, bufnrr, diagnostics, opts)
								for i, diagnostic in ipairs(diagnostics) do
									if
										diagnostic.source
										== "Marksman" -- You need to check what the correct value should be here
									then
										table.remove(diagnostics, i)
									end
								end
								default_handler.show(namespace, bufnrr, diagnostics, opts)
							end,
							hide = function(...)
								default_handler.hide(...)
							end,
						}
						local default_handler = vim.diagnostic.handlers.signs
						vim.diagnostic.handlers.signs = {
							show = function(namespace, bufnrr, diagnostics, opts)
								for i, diagnostic in ipairs(diagnostics) do
									if
										diagnostic.source
										== "Marksman" -- You need to check what the correct value should be here
									then
										table.remove(diagnostics, i)
									end
								end
								default_handler.show(namespace, bufnrr, diagnostics, opts)
							end,
							hide = function(...)
								default_handler.hide(...)
							end,
						}
					end
					on_attach(client, bufnr)
				end,
				on_new_config = function(conf, new_root)
					if require("lspconfig.util").root_pattern(".zk")(new_root) then
						conf.in_zk_notebook = true
					else
						conf.in_zk_notebook = false
					end
				end,
			})

			local python_path
			-- ensure python virtualenv is determined automatically on lsp start
			lspconfig.basedpyright.setup({
				on_attach = function(client, bufnr)
					on_attach(client, bufnr)
					require("core.util").set_python_env()
					if python_path == nil then
						python_path, _ = vim.fn.expand(require("core.util").get_python_venv_bin(client.config.root_dir))
					end
					-- print(string.format("[PYTHON VENV]: %s", vim.inspect(python_path)))
					client.config.settings.python = {} or client.config.settings.python
					client.config.settings.python.pythonPath = python_path
				end,
				settings = {
					-- disable imports and linting since, we use ruff for that
					pyright = {
						disableOrganizeImports = true,
					},
					python = {
						analysis = {
							ignore = { "*" },
						},
					},
				},
			})
			lspconfig.ruff.setup({
				on_attach = function(client, bufnr)
					on_attach(client, bufnr)
					require("core.util").set_python_env()
					client.server_capabilities.hoverProvider = false -- we use pyright for hover info
					if python_path == nil then
						python_path, _ = vim.fn.expand(require("core.util").get_python_venv_bin(client.config.root_dir))
					end
					client.config.settings.python = {} or client.config.settings.python
					client.config.settings.python.pythonPath = python_path
				end,
			})

			-- set up arduino with the help of arduino.nvim plugin
			if require("core.util").is_available("arduino") then
				lspconfig.arduino_language_server.setup({
					on_new_config = require("arduino").on_new_config,
				})
			end

			-- attach ltex for fitting ft only when spell checking becomes enabled
			vim.api.nvim_create_autocmd("User", {
				pattern = "SpellEnable",
				callback = function()
					lspconfig.ltex.setup({
						on_attach = function(client, bufnr)
							on_attach(client, bufnr)
							if require("core.util").is_available("ltex_extra") then
								require("ltex_extra").setup()
							end
						end,
						settings = {
							ltex = {
								language = vim.opt.spelllang:get(),
							},
						},
					})
					vim.cmd("LspStart ltex")
				end,
			})
		end,
		keys = { { "<leader>vs", ":LspInfo<cr>", desc = "LspInfo" } },
	},
	-- pretty lsp 'peek' menus
	{
		"DNLHC/glance.nvim",
		opts = { border = { enable = true }, theme = { enable = true, mode = "auto" } },
		cmd = { "Glance" },
	},
}
