local lsp = require('lsp-zero')
local luasnip = require("luasnip")

lsp.preset('recommended')

lsp.ensure_installed({
    'lua_ls',
    'tsserver',
    'eslint',
    'gopls',
    'intelephense',
    'jsonls',
    'volar',
})

local has_words_before = function()
    local line, col = unpack(vim.api.nvim_win_get_cursor(0))
    return col ~= 0 and vim.api.nvim_buf_get_lines(0, line - 1, line, true)[1]:sub(col, col):match "%s" == nil
end

local cmp = require('cmp')

-- Use buffer source for `/`
cmp.setup.cmdline("/", {
    enabled = false
})

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

-- local cmp_select = {behavior = cmp.SelectBehavior.Select}
local cmp_mappings = lsp.defaults.cmp_mappings({
    ["<C-k>"] = cmp.mapping(cmp.mapping.select_prev_item(), { "i", "c" }),
    ["<C-j>"] = cmp.mapping(cmp.mapping.select_next_item(), { "i", "c" }),
    ["<C-b>"] = cmp.mapping(cmp.mapping.scroll_docs(-4), { "i", "c" }),
    ["<C-f>"] = cmp.mapping(cmp.mapping.scroll_docs(4), { "i", "c" }),
    ["<C-Space>"] = cmp.mapping(cmp.mapping.complete(), { "i", "c" }),
    ["<C-e>"] = cmp.mapping { i = cmp.mapping.close(), c = cmp.mapping.close() },
    ["<CR>"] = cmp.mapping {
        i = cmp.mapping.confirm { behavior = cmp.ConfirmBehavior.Replace, select = false },
        c = function(fallback)
            if cmp.visible() then
                cmp.mapping.confirm { behavior = cmp.ConfirmBehavior.Replace, select = false }
            else
                fallback()
            end
        end,
    },
    ["<Tab>"] = cmp.mapping(
    function(fallback)
        if cmp.visible() then
            cmp.select_next_item()
        elseif luasnip.expand_or_jumpable() then
            luasnip.expand_or_jump()
        elseif has_words_before() then
            cmp.complete()
        else
            fallback()
        end
    end,
    {
        "i",
        "s",
    }),
    ["<S-Tab>"] = cmp.mapping(function(fallback)
        if cmp.visible() then
            cmp.select_prev_item()
        elseif luasnip.jumpable(-1) then
            luasnip.jump(-1)
        else
            fallback()
        end
    end,
    {
        "i",
        "s",
    }),
})

lsp.setup_nvim_cmp({
    mapping = cmp_mappings,
    completion = {
        completeopt = "menu,menuone",
    },
    window = {
        completion = {
            side_padding = 1,
            winhighlight = "NormalFloat:NormalFloat,FloatBorder:TelescopeBorder",
            scrollbar = false,
        },
        documentation = {
            border = { "╭", "─", "╮", "│", "╯", "─", "╰", "│" },
            winhighlight = "NormalFloat:NormalFloat,FloatBorder:TelescopeBorder",
        },
        snippet = {
            expand = function(args)
                luasnip.lsp_expand(args.body)
            end,
        },
    },
})

lsp.on_attach(function(client, bufnr)
    local opts = {buffer = bufnr, remap = false}

    -- Key mappings
    vim.keymap.set("n", "K", vim.lsp.buf.hover, opts)

    vim.keymap.set("n", "[d", vim.diagnostic.goto_prev, opts)
    vim.keymap.set("n", "]d", vim.diagnostic.goto_next, opts)
    vim.keymap.set("n", "[e", function () vim.diagnostic.goto_prev({severity = vim.diagnostic.severity.ERROR}) end, opts)
    vim.keymap.set("n", "]e", function () vim.diagnostic.goto_next({severity = vim.diagnostic.severity.ERROR}) end, opts)

    vim.keymap.set("n", "gd", function() vim.lsp.buf.definition() end, opts)

    vim.keymap.set("n", "<leader>vws", function() vim.lsp.buf.workspace_symbol() end, opts)
    vim.keymap.set("n", "<leader>vd", function() vim.diagnostic.open_float() end, opts)

    vim.keymap.set("n", "<leader>rn", vim.lsp.buf.rename, opts)
    vim.keymap.set("n", "<leader>K", vim.lsp.buf.code_action, opts)
    vim.keymap.set("n", "<leader>of", vim.diagnostic.open_float, opts)

    vim.keymap.set("n", "gh", vim.lsp.buf.signature_help, opts)
end)

lsp.setup()
