local cmp = require("cmp")
local luasnip = require("luasnip")
require("luasnip.loaders.from_vscode").lazy_load()

cmp.setup({
  formatting = {
    format = require("lspkind").cmp_format({
      mode = "symbol", -- show only symbol annotations
      maxwidth = 50, -- prevent the popup from showing more than provided characters
      ellipsis_char = "...", -- when popup menu exceed maxwidth, the truncated part would show ellipsis_char instead
      symbol_map = {
        -- Copilot = "",
      },
    }),
  },
  snippet = {
    -- REQUIRED - you must specify a snippet engine
    expand = function(args)
      require("luasnip").lsp_expand(args.body)
    end,
  },
  mapping = cmp.mapping.preset.insert({
    ["<C-b>"] = cmp.mapping.scroll_docs(-4),
    ["<C-f>"] = cmp.mapping.scroll_docs(4),
    ["<C-Space>"] = cmp.mapping.complete(),
    ["<S-CR>"] = cmp.mapping.abort(),
    ["<CR>"] = cmp.mapping.confirm({ select = true }),
    ["<Tab>"] = cmp.mapping(function(fallback)
      if cmp.visible() then
        cmp.select_next_item({ behavior = cmp.SelectBehavior.Select })
      elseif luasnip.locally_jumpable(1) then
        luasnip.jump(1)
      else
        fallback()
      end
    end, { "i", "s" }),
    ["<S-Tab>"] = cmp.mapping(function(fallback)
      if cmp.visible() then
        cmp.select_prev_item({ behavior = cmp.SelectBehavior.Select })
      elseif luasnip.locally_jumpable(-1) then
        luasnip.jump(-1)
      else
        fallback()
      end
    end, { "i", "s" }),
    ["<C-n>"] = cmp.mapping(function(fallback)
      if luasnip.choice_active() then
        luasnip.change_choice(1)
      elseif luasnip.locally_jumpable(1) then
        luasnip.jump(1)
      else
        fallback()
      end
    end, { "i", "s" }),
    ["<C-p>"] = cmp.mapping(function(fallback)
      if luasnip.choice_active() then
        luasnip.change_choice(-1)
      elseif luasnip.locally_jumpable(-1) then
        luasnip.jump(-1)
      else
        fallback()
      end
    end, { "i", "s" }),
  }),
  sources = cmp.config.sources({
    { priority = 1, name = "async_path" },
    { priority = 1, name = "buffer" },
    { priority = 1, name = "spell" },
    { priority = 2, name = "nvim_lsp" },
    -- { priority = 3, name = "copilot" },
    { priority = 3, name = "nvim_lsp_signature_help" },
    { priority = 4, name = "luasnip" },
    { priority = 4, name = "vimtex" },
  }),
})

-- Set configuration for specific filetype.
cmp.setup.filetype("gitcommit", {
  sources = cmp.config.sources({
    { name = "buffer" },
  }),
})

-- Use buffer source for `/` and `?` (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 = "async_path" },
  }, {
    { name = "cmdline" },
  }),
  enabled = function()
    -- Set of commands where cmp will be disabled
    local disabled = {
      IncRename = true,
    }
    -- Get first word of cmdline
    local cmd = vim.fn.getcmdline():match("%S+")
    -- Return true if cmd isn't disabled
    -- else call/return cmp.close(), which returns false
    return not disabled[cmd] or cmp.close()
  end,
})
-- If you want insert `(` after select function or method item
local cmp_autopairs = require("nvim-autopairs.completion.cmp")
local handlers = require("nvim-autopairs.completion.handlers")

cmp.event:on(
  "confirm_done",
  cmp_autopairs.on_confirm_done({
    filetypes = {
      -- "*" is a alias to all filetypes
      ["*"] = {
        ["("] = {
          kind = {
            cmp.lsp.CompletionItemKind.Function,
            cmp.lsp.CompletionItemKind.Method,
          },
          handler = handlers["*"],
        },
      },
      -- Disable for functional languages
      haskell = false,
      nix = false,
    },
  })
)

local pickers = require("telescope.pickers")
local finders = require("telescope.finders")
local conf = require("telescope.config").values
local actions = require("telescope.actions")
local action_state = require("telescope.actions.state")

local all_sources = vim.deepcopy(cmp.get_config().sources)

local find = function(sources, name)
  for k, source in ipairs(sources) do
    if source.name == name then
      return k
    end
  end
  return nil
end

local is_active = function(name)
  local active_sources = cmp.get_config().sources
  local index = find(active_sources, name)
  return index ~= nil
end

local enable_source = function(name, force)
  if force or not is_active(name) then
    local source_index = find(all_sources, name)
    if source_index ~= nil then
      local active_sources = cmp.get_config().sources
      local source = all_sources[source_index]
      table.insert(active_sources, 1, source)
      cmp.setup({ sources = active_sources })
    end
  end
end

local disable_source = function(identifier)
  if type(identifier) == "string" then
    identifier = find(all_sources, identifier)
  end
  local active_sources = cmp.get_config().sources
  table.remove(active_sources, identifier)
end

local toggle_sources = function(name)
  local active_sources = cmp.get_config().sources
  local index = find(active_sources, name)
  if index ~= nil then
    disable_source(index)
  else
    enable_source(name, true)
  end
end

-- our picker function: sources
local sources_picker = function(opts)
  opts = opts or {}
  pickers
    .new(opts, {
      prompt_title = "sources",
      finder = finders.new_table({
        results = vim.tbl_map(function(source)
          return source.name
        end, all_sources),
        entry_maker = function(entry)
          return {
            value = entry,
            display = function(tbl)
              local name = tbl["ordinal"]
              local active = is_active(name)
              return string.format("%s %s", name, active and "✅" or "❌")
            end,
            ordinal = entry,
          }
        end,
      }),
      sorter = conf.generic_sorter(opts),
      attach_mappings = function(prompt_bufnr, _)
        actions.select_default:replace(function()
          actions.close(prompt_bufnr)
          local selection = action_state.get_selected_entry()
          toggle_sources(selection["value"])
        end)
        return true
      end,
    })
    :find()
end

-- autocommand for sources_picker
vim.api.nvim_create_user_command("CmpToggle", sources_picker, {})

-- disable sources by default
-- disable_source("codeium")