local M = {}

function M.luasnip()
    local types = require('luasnip.util.types')

    require('luasnip').config.set_config {
        updateevents = 'TextChanged, TextChangedI',
        history = true,
        ext_opts = {
            [types.choiceNode] = {
                active = { virt_text = { { '●', 'LuaSnipChoice' } } },
            },
            [types.insertNode] = {
                active = { virt_text = { { '●', 'LuaSnipInsert' } } },
            },
        },
    }

    -- Loading snippets from 'friendly-snippets'
    require('luasnip.loaders.from_vscode').lazy_load()
end

function M.cmp()
    local cmp = require('cmp')
    local luasnip = require('luasnip')
    local neogen = require('neogen')
    local border_chars = require('user.util').border_styles[vim.g.border_style]

    local lspkind_icons = {
        Text = '',
        Method = '',
        Function = '',
        Constructor = '',
        Field = 'ﰠ',
        Variable = '',
        Class = 'ﴯ',
        Interface = '',
        Module = '',
        Property = 'ﰠ',
        Unit = '塞',
        Value = '',
        Enum = '',
        Keyword = '',
        Snippet = '',
        Color = '',
        File = '',
        Reference = '',
        Folder = '',
        EnumMember = '',
        Constant = '',
        Struct = '',
        Event = '',
        Operator = '',
        TypeParameter = '',
    }

    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

    cmp.setup {
        -- view = {entries = 'native'},
        formatting = {
            format = function(entry, vim_item)
                vim_item.kind = string.format('%s %s', lspkind_icons[vim_item.kind], vim_item.kind)

                vim_item.menu = ({
                    luasnip = '[SNIP]',
                    nvim_lsp = '[LSP]',
                    nvim_lua = '[NVIM]',
                    conjure = '[CJ]',
                    orgmode = '[ORG]',
                    fish = '[FISH]',
                    path = '[PATH]',
                    buffer = '[BUF]',
                    calc = '[CALC]',
                })[entry.source.name]

                return vim_item
            end,
        },
        documentation = {
            border = border_chars,
            winhighlight = 'NormalFloat:NormalFloat,FloatBorder:FloatBorder',
        },
        mapping = {
            ['<CR>'] = cmp.mapping.confirm {
                behavior = cmp.ConfirmBehavior.Replace,
                select = true,
            },
            ['<C-Space>'] = cmp.mapping.complete(),
            ['<C-p>'] = cmp.mapping.select_prev_item { behavior = cmp.SelectBehavior.Select },
            ['<C-n>'] = cmp.mapping.select_next_item { behavior = cmp.SelectBehavior.Select },
            ['<C-b>'] = cmp.mapping.scroll_docs(-4),
            ['<C-f>'] = cmp.mapping.scroll_docs(4),
            ['<C-c>'] = cmp.mapping(function(fallback)
                if cmp.visible() then
                    cmp.close()
                else
                    fallback()
                end
            end),
            -- Cycle through annotations
            ['<C-j>'] = cmp.mapping(function(fallback)
                if neogen.jumpable() then
                    neogen.jump_next()
                else
                    fallback()
                end
            end, { 'i', 's' }),
            ['<C-k>'] = cmp.mapping(function(fallback)
                if neogen.jumpable(-1) then
                    neogen.jump_prev()
                else
                    fallback()
                end
            end, { 'i', 's' }),
            -- Change choice nodes for luasnip
            ['<C-s>'] = cmp.mapping(function(fallback)
                if luasnip.choice_active() then
                    luasnip.change_choice(1)
                else
                    fallback()
                end
            end, { 'i', 's' }),
            -- supertab-like mapping
            ['<Tab>'] = cmp.mapping(function(fallback)
                if cmp.visible() then
                    cmp.select_next_item { behavior = cmp.SelectBehavior.Insert }
                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 { behavior = cmp.SelectBehavior.Insert }
                elseif luasnip.jumpable(-1) then
                    luasnip.jump(-1)
                else
                    fallback()
                end
            end, { 'i', 's' }),
        },
        snippet = {
            expand = function(args)
                luasnip.lsp_expand(args.body)
            end,
        },
        sources = {
            { name = 'luasnip' },
            { name = 'nvim_lsp' },
            { name = 'nvim_lua' },
            { name = 'conjure' },
            { name = 'orgmode' },
            { name = 'fish' },
            { name = 'path' },
            { name = 'buffer' },
            { name = 'calc' },
        },
    }
end

function M.autopairs()
    require('nvim-autopairs').setup {
        disable_filetype = { 'TelescopePrompt', 'spectre_panel' },
        disable_in_macro = false, -- disable when recording or executing a macro
        ignored_next_char = string.gsub([[ [%w%%%'%[%"%.] ]], '%s+', ''),
        enable_moveright = true,
        enable_afterquote = true, -- add bracket pairs after quote
        enable_check_bracket_line = true, -- check bracket in same line
        check_ts = true, -- use treesitter
        ts_config = {
            lua = { 'string', 'source' },
            javascript = { 'string', 'template_string' },
        },
        map_bs = true, -- map the <BS> key
        map_c_w = true,
        map_cr = true,
        fast_wrap = {
            map = '<A-e>',
            chars = { '{', '[', '(', '"', "'" },
            pattern = string.gsub([[ [%'%"%)%>%]%)%}%,] ]], '%s+', ''),
            offset = 0, -- Offset from pattern match
            end_key = '$',
            keys = 'qwertyuiopzxcvbnmasdfghjkl',
            check_comma = true,
            highlight = 'Search',
            highlight_grey = 'Comment',
        },
    }

    -- Setup for cmp
    local cmp_autopairs = require('nvim-autopairs.completion.cmp')
    local cmp = require('cmp')
    cmp.event:on('confirm_done', cmp_autopairs.on_confirm_done())
end

return M
