local function select_next(fallback)
    if require("cmp").visible() then
        if require'snippy'.can_expand_or_advance() then
            require("cmp").select_next_item({ behavior = require("cmp").SelectBehavior.Select })
        else
            require("cmp").select_next_item({ behavior = require("cmp").SelectBehavior.Insert })
        end
    elseif require'snippy'.can_expand_or_advance() then
        require'snippy'.expand_or_advance()
    else
        fallback()
    end
end

local function select_previous(fallback)
    if require("cmp").visible() then
        if require'snippy'.can_expand_or_advance() then
            require("cmp").select_prev_item({ behavior = require("cmp").SelectBehavior.Select })
        else
            require("cmp").select_prev_item({ behavior = require("cmp").SelectBehavior.Insert })
        end
    elseif require'snippy'.can_jump(-1) then
        require'snippy'.previous()
    else
        fallback()
    end
end

return {
    "hrsh7th/nvim-cmp",
    dependencies = {
        'hrsh7th/cmp-buffer',
        'hrsh7th/cmp-cmdline',
        'hrsh7th/cmp-nvim-lsp',
        'hrsh7th/cmp-path',
        'honza/vim-snippets',
        'dcampos/cmp-snippy',
        'neovim/nvim-lspconfig',
        'williamboman/mason-lspconfig.nvim',
        {'dcampos/nvim-snippy', opts = {
            mappings = {
                is = {
                    ['<leader><Tab>'] = 'expand_or_advance',
                    ['<leader><S-Tab>'] = 'previous',
                },
            },
        }},
    },
    keys = {
        {"<leader>n", vim.diagnostic.goto_next},
        {"<leader>p", vim.diagnostic.goto_prev},
        {"<leader>f", vim.lsp.buf.code_action},
        {"gd", vim.lsp.buf.definition},
        {"gr", vim.lsp.buf.references},
        {"<space>", vim.lsp.buf.hover},
        {"<leader>rn", vim.lsp.buf.rename},
    },
    lazy = false,
    config = function ()
        local cmp = require("cmp")
        cmp.setup({
            snippet = { expand = function(args) require('snippy').expand_snippet(args.body) end },
            window = {
                completion = cmp.config.window.bordered(),
                documentation = cmp.config.window.bordered(),
            },
            mapping = cmp.mapping.preset.insert({
                ['<CR>'] = cmp.mapping.confirm({ select = false }), -- Accept currently selected item. Set `select` to `false` to only confirm explicitly selected items.
                ["<Tab>"] = cmp.mapping(select_next, { "i", "s" }),
                ["<C-J>"] = cmp.mapping(select_next, { "i", "s" }),
                ["<S-Tab>"] = cmp.mapping(select_previous, { "i", "s" }),
                ["<C-K>"] = cmp.mapping(select_previous, { "i", "s" }),
            }),
            sources = cmp.config.sources({ { name = 'nvim_lsp' }, { name = 'path' }, { name = 'snippy' }, }, { { name = 'buffer' } })
        })

        -- Set configuration for specific filetype.
        cmp.setup.filetype('gitcommit', {
            sources = cmp.config.sources({
                { name = 'cmp_git' }, -- You can specify the `cmp_git` source if you were installed it.
            }, {
                { name = 'buffer' },
            })
        })

        -- Use buffer source for `/` (if you enabled `native_menu`, this won't work anymore).
        cmp.setup.cmdline('/', {
            mapping = cmp.mapping.preset.cmdline(),
            sources = {
                { name = 'buffer' }
            }
        })

        -- Use cmdline & path source for ':' (if you enabled `native_menu`, this won't work anymore).
        cmp.setup.cmdline(':', {
            mapping = cmp.mapping.preset.cmdline(),
            sources = cmp.config.sources({
                { name = 'path' }
            }, {
                { name = 'cmdline' }
            })
        })

        local cmp_completion = require('nvim-autopairs.completion.cmp')
        cmp.event:on('confirm_done',cmp_completion.on_confirm_done())

        -- Setup lspconfig.
        local capabilities = require('cmp_nvim_lsp').default_capabilities(vim.lsp.protocol.make_client_capabilities())

        require('lspconfig').pyright.setup {
            capabilities = capabilities,
            settings = {
                python = {
                    analysis = {
                        typeCheckingMode = "off"
                    }
                }
            }
        }

        require('lspconfig').texlab.setup {
            capabilities = capabilities
        }

        require('lspconfig').nixd.setup {
            capabilities = capabilities
        }

        require('lspconfig').clangd.setup {
            capabilities = capabilities,
            -- root_dir = function()
            --     return require('lspconfig').util.root_pattern({'.clang-format', 'build/', 'compile_flags.txt'})
            -- end,
            cmd = {
                "clangd",
                "--background-index",
                "--clang-tidy",
                "-j=8",
                "--clang-tidy-checks=*",
                "--all-scopes-completion",
                "--completion-style=bundled",
                "--cross-file-rename",
                "--completion-style=detailed",
                "--header-insertion-decorators",
                "--header-insertion=iwyu",
                "--pch-storage=memory"
            }
        }

        require('lspconfig').bashls.setup {
            capabilities = capabilities
        }

        require('lspconfig').csharp_ls.setup {
            capabilities = capabilities
        }

        require('lspconfig').typst_lsp.setup {
            capabilities = capabilities
        }

        require('lspconfig').cmake.setup {
            capabilities = capabilities
        }

        require('lspconfig').jsonls.setup {
            capabilities = capabilities
        }

        require('lspconfig').tsserver.setup {
            capabilities = capabilities
        }

        require('lspconfig').kotlin_language_server.setup {
            capabilities = capabilities
        }

        require('lspconfig').vuels.setup {
            capabilities = capabilities
        }

        require('lspconfig').dockerls.setup {
            capabilities = capabilities
        }

        require('lspconfig').vimls.setup {
            capabilities = capabilities
        }

        require('lspconfig').html.setup {
            capabilities = capabilities
        }

        require('lspconfig').emmet_ls.setup({
            capabilities = capabilities,
            filetypes = { "css", "eruby", "html", "javascript", "javascriptreact", "less", "sass", "scss", "svelte", "pug", "typescriptreact", "vue" },
            init_options = {
                html = {
                    options = {
                        ["bem.enabled"] = true,
                    },
                },
            }
        })

        require('lspconfig').yamlls.setup {
            capabilities = capabilities
        }

        require('lspconfig').cssls.setup {
            capabilities = capabilities
        }

        require('lspconfig').jdtls.setup {
            capabilities = capabilities
        }

        require('lspconfig').rust_analyzer.setup {
            capabilities = capabilities,
        }

        require('lspconfig').gopls.setup {
            capabilities = capabilities
        }

        require('lspconfig').ruff_lsp.setup {
            capabilities = capabilities
        }

        require('lspconfig').lua_ls.setup {
            settings = {
                Lua = {
                    runtime = {
                        -- Tell the language server which version of Lua you're using (most likely LuaJIT in the case of Neovim)
                        version = 'LuaJIT',
                    },
                    diagnostics = {
                        -- Get the language server to recognize the `vim` global
                        globals = {'vim', 'use', 'awesome', 'client', 'root'},
                    },
                    workspace = {
                        -- Make the server aware of Neovim runtime files
                        library = {
                            ['/usr/share/nvim/runtime/lua'] = true,
                            ['/usr/share/nvim/runtime/lua/lsp'] = true,
                            ['/usr/share/awesome/lib'] = true
                        }
                    },
                    -- Do not send telemetry data containing a randomized but unique identifier
                    telemetry = {
                        enable = false,
                    },
                },
            },
        }

        local filetypes = { "bibtex", "gitcommit", "markdown", "org", "tex", "restructuredtext", "rsweave", "latex", "quarto", "rmd", "context", "html", "xhtml", "typst", "mail" }
        require('lspconfig').ltex.setup {
            capabilities = capabilities,
            on_attach = function(_, _)
                require("ltex_extra").setup{
                    load_langs = {"nl-BE", "en-US", "en-GB"},
                    init_check = true,
                }
            end,
            settings = {
                ltex = {
                    enabled = filetypes,
                    language = "en-GB"
                },
            },
            filetypes = filetypes
        }

        vim.diagnostic.config({
            virtual_text = false
        })

        -- Show line diagnostics automatically in hover window
        vim.cmd("autocmd CursorHold,CursorHoldI * lua vim.diagnostic.open_float(nil, {focus=false})")
    end,
}
