{ pkgs, config, lib, ... }:

let
  inherit (config.lib.stylix.scheme) scheme;
  inherit (lib.strings) toLower;
in
{
  programs.bat.enable = true;

  editorconfig = {
    enable = true;

    settings = {
      "*" = {
        charset = "utf-8";
        end_of_line = "lf";
        insert_final_newline = true;
        indent_size = 2;
        indent_style = "space";
        trim_trailing_whitespace = false;
      };

      "Makefile" = {
        indent_style = "tab";
        indent_size = 4;
      };

      "*.html" = {
        indent_style = "tab";
        indent_size = 4;
      };

      "*.rs" = {
        indent_style = "space";
        indent_size = 4;
      };
    };
  };

  programs.neovim = {
    enable = true;
    defaultEditor = true;

    extraPackages = with pkgs; [
      nodePackages.vls
      nodePackages.typescript-language-server
      nodePackages."@astrojs/language-server"
      nodePackages."@prisma/language-server"
      lua-language-server
      emmet-language-server
      crystalline
      markdown-oxide
      tailwindcss-language-server
      vscode-langservers-extracted
      rust-analyzer
      texlab
      nil
      universal-ctags
    ];

    extraConfig = /* vim */ ''
      filetype plugin indent on
      set undofile
      set spell
      set number
      set linebreak
      set clipboard=unnamedplus
      set fileencoding=utf-8         " Ensure that we always save files as utf-8
      set fileencodings=utf-8,sjis   " Automatically open shiftjis files with their proper encoding
      set spelllang=en_us,cjk        " Don't show errors for CJK characters
      set noshowmode                 " Disable the --MODE-- text (enable if not using the status line)
      set mouse=a
      set ignorecase                 " By default use case-insensitive search (combine with smartcase)
      set smartcase                  " Make search case-sensitive when using capital letters
      set scrolloff=1                " The minimal number of rows to show when scrolling up/down
      set sidescrolloff=5            " The minimal number of columns to show when scrolling left/right
      set tabstop=4                  " Show a tab character as 4 spaces
      set softtabstop=0              " Edit soft tabs as if they're regular spaces
      set shiftwidth=4               " Make autoindent appear as 4 spaces

      set foldmethod=indent
      set foldlevelstart=99

      map <MiddleMouse> <Nop>
      imap <MiddleMouse> <Nop>
      map <2-MiddleMouse> <Nop>
      imap <2-MiddleMouse> <Nop>
      map <3-MiddleMouse> <Nop>
      imap <3-MiddleMouse> <Nop>
      map <4-MiddleMouse> <Nop>
      imap <4-MiddleMouse> <Nop>

      highlight Search ctermbg=240 ctermfg=255
      highlight IncSearch ctermbg=255 ctermfg=240

      let mapleader = ' '
      nnoremap <silent> <leader>e :set nu!<CR>
      nnoremap <silent> <leader>o :GitBlameToggle<CR>
      nnoremap <silent> <leader>a :NvimTreeFocus<CR>
      nnoremap <silent> <leader>d :bp\|bd #<CR>
      nnoremap <silent> <leader>f :Files<CR>
      nnoremap <silent> <leader>g :set hlsearch!<CR>
      nnoremap <silent> <leader>j :Buffers<CR>
      nnoremap <silent> <leader>l :Rg<CR>
      nnoremap <silent> <leader>; :NvimTreeToggle<CR>
      nnoremap <silent> <leader>b :Vista!!<CR>
      vnoremap <C-s> y:silent !notify-send -t 4000 "成果" "$(tango '<C-r>0')"<CR>:<Esc>

      autocmd BufNewFile,BufRead *.ecr setlocal syntax=html
      autocmd BufWritePre,FileWritePre * silent! call mkdir(expand('<afile>:p:h'), 'p')
      autocmd VimEnter * silent! :cd `git rev-parse --show-toplevel`

      tnoremap <C-space> <C-\><C-n>
    '';

    plugins = with pkgs.vimPlugins; [
      {
        plugin = nvim-tree-lua;
        type = "lua";
        config = /* lua */ ''
          require("nvim-tree").setup()

          vim.api.nvim_create_autocmd({"QuitPre"}, {
            callback = function()
              vim.cmd("NvimTreeClose")
            end
          })

          local function open_nvim_tree(data)
            local real_file = vim.fn.filereadable(data.file) == 1
            local no_name = data.file == "" and vim.bo[data.buf].buftype == ""

            if not real_file and not no_name then
              return
            end

            require("nvim-tree.api").tree.toggle({ focus = false, find_file = true })
          end

          vim.api.nvim_create_autocmd({ "VimEnter" }, { callback = open_nvim_tree })
        '';
      }
      {
        plugin = indent-blankline-nvim;
        type = "lua";
        config = /* lua */ ''
          require("ibl").setup()
        '';
      }
      {
        plugin = gitsigns-nvim;
        type = "lua";
        config = /* lua */ ''
          require('gitsigns').setup()
        '';
      }
      {
        plugin = nvim-web-devicons;
        type = "lua";
      }
      {
        plugin = nvim-scrollbar;
        type = "lua";
        config = /* lua */ ''
          require("scrollbar").setup()
        '';
      }
      {
        plugin = nvim-lspconfig;
        type = "lua";
        config = /* lua */ ''
          local lspconfig = require('lspconfig')
          local capabilities = require("cmp_nvim_lsp").default_capabilities()

          lspconfig.denols.setup {
            capabilities = capabilities,
            root_dir = lspconfig.util.root_pattern("deno.json", "deno.jsonc"),
          }

          lspconfig.tsserver.setup {
            capabilities = capabilities,
            root_dir = lspconfig.util.root_pattern("package.json"),
            single_file_support = false
          }

          lspconfig.eslint.setup {
            capabilities = capabilities,
            on_attach = function(client, bufnr)
              vim.api.nvim_create_autocmd("BufWritePre", {
                buffer = bufnr,
                command = "EslintFixAll",
              })
            end
          }

          lspconfig.tailwindcss.setup {
            capabilities = capabilities,
            on_attach = function(client, bufnr)
              require("tailwindcss-colors").buf_attach(bufnr)
            end
          }

          vim.keymap.set('n', '<space>e', vim.diagnostic.open_float)
          vim.keymap.set('n', '[d', vim.diagnostic.goto_prev)
          vim.keymap.set('n', ']d', vim.diagnostic.goto_next)
          vim.keymap.set('n', '<space>q', vim.diagnostic.setloclist)

          vim.api.nvim_create_autocmd('LspAttach', {
            group = vim.api.nvim_create_augroup('UserLspConfig', {}),

            callback = function(ev)
              local opts = { buffer = ev.buf }

              vim.keymap.set('n', 'gD', vim.lsp.buf.declaration, opts)
              vim.keymap.set('n', 'gd', vim.lsp.buf.definition, opts)
              vim.keymap.set('n', 'K', vim.lsp.buf.hover, opts)
              vim.keymap.set('n', 'gi', vim.lsp.buf.implementation, opts)
              vim.keymap.set('n', '<C-k>', vim.lsp.buf.signature_help, opts)
              vim.keymap.set('n', '<space>D', vim.lsp.buf.type_definition, opts)
              vim.keymap.set('n', '<space>rn', vim.lsp.buf.rename, opts)
              vim.keymap.set({ 'n', 'v' }, '<space>ca', vim.lsp.buf.code_action, opts)
              vim.keymap.set('n', 'gr', vim.lsp.buf.references, opts)
            end,
          })

          vim.lsp.handlers["textDocument/hover"] = vim.lsp.with(
            vim.lsp.handlers.hover, { border = "single" }
          )

          vim.lsp.handlers["textDocument/signatureHelp"] = vim.lsp.with(
            vim.lsp.handlers.signature_help, { border = "single" }
          )

          vim.diagnostic.config {
            float = { border = "single" }
          }
        '';
      }
      {
        plugin = nvim-cmp;
        type = "lua";
        config = /* lua */ ''
          local capabilities = require("cmp_nvim_lsp").default_capabilities()
          local lspconfig = require('lspconfig')

          local servers = {
            'nil_ls',
            'rust_analyzer',
            'markdown_oxide',
            'lua_ls',
            'html',
            'clangd',
            'texlab',
            'crystalline',
            'prismals',
            'jsonls',
            'vuels',
            'sqlls',
            'emmet_language_server',
            'astro',
            'cssls',
          }

          for _, lsp in ipairs(servers) do
            lspconfig[lsp].setup {
              capabilities = capabilities,
            }
          end

          local luasnip = require('luasnip')
          local cmp = require('cmp')

          cmp.setup {
            snippet = {
              expand = function(args)
                luasnip.lsp_expand(args.body)
              end,
            },
            mapping = cmp.mapping.preset.insert({
              ['<C-u>'] = cmp.mapping.scroll_docs(-4),
              ['<C-d>'] = cmp.mapping.scroll_docs(4),
              ['<C-Space>'] = cmp.mapping.complete(),
              ['<CR>'] = cmp.mapping.confirm {
                behavior = cmp.ConfirmBehavior.Replace,
                select = true,
              },
              ['<Tab>'] = cmp.mapping(function(fallback)
                if cmp.visible() then
                  cmp.select_next_item()
                elseif luasnip.expand_or_jumpable() then
                  luasnip.expand_or_jump()
                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' }),
            }),
            sources = {
              { name = 'nvim_lsp' },
              { name = 'luasnip' },
            },
          }
        '';
      }
      cmp-nvim-lsp
      cmp_luasnip
      {
        plugin = luasnip;
        type = "lua";
        config = /* lua */ ''
          require("luasnip.loaders.from_vscode").lazy_load()
        '';
      }
      friendly-snippets
      {
        plugin = base16-nvim;
        type = "lua";
        config = "vim.cmd('colorscheme base16-${toLower scheme}')";
      }
      {
        plugin = lualine-nvim;
        type = "lua";
        config = /* lua */ ''
          local theme = require("lualine.themes.base16")
          theme.normal.b.bg = nil
          theme.normal.c.bg = nil
          theme.replace.b.bg = nil
          theme.insert.b.bg = nil
          theme.visual.b.bg = nil
          theme.inactive.a.bg = nil
          theme.inactive.b.bg = nil
          theme.inactive.c.bg = nil

          require('lualine').setup {
            options = {
              theme = theme,
              disabled_filetypes = {'NvimTree', 'tagbar'}
            },
            sections = { lualine_c = {'%f'} }
          }
        '';
      }
      {
        plugin = git-blame-nvim;
        type = "lua";
      }
      {
        plugin = comment-nvim;
        type = "lua";
        config = /* lua */ ''
          require('Comment').setup {
            pre_hook = require('ts_context_commentstring.integrations.comment_nvim').create_pre_hook()
          }
        '';
      }
      {
        plugin = nvim-autopairs;
        type = "lua";
        config = /* lua */ ''
          require("nvim-autopairs").setup()
        '';
      }
      {
        plugin = auto-save-nvim;
        type = "lua";
        config = /* lua */ ''
          require("auto-save").setup()
        '';
      }
      {
        plugin = vimtex;
        config = /* vim */ ''
          let g:vimtex_mappings_enabled = 0
          let g:vimtex_imaps_enabled = 0
          let g:vimtex_view_method = 'zathura'
          let g:vimtex_compiler_latexmk = {'out_dir': '/tmp/vimtex'}

          nnoremap <localleader>f <plug>(vimtex-view)
          nnoremap <localleader>g <plug>(vimtex-compile)
          nnoremap <localleader>d <plug>(vimtex-env-delete)
          nnoremap <localleader>c <plug>(vimtex-env-change)
        '';
      }
      {
        plugin = vim-markdown;
        config = /* vim */ ''
          let g:vim_markdown_folding_disabled = 1
          let g:vim_markdown_conceal = 0
          let g:vim_markdown_frontmatter = 1
          let g:vim_markdown_toml_frontmatter = 1
          let g:vim_markdown_json_frontmatter = 1
        '';
      }
      {
        plugin = nvim-ts-autotag;
        type = "lua";
        config = /* lua */ ''
          require('nvim-ts-autotag').setup()
        '';
      }
      {
        plugin = nvim-surround;
        type = "lua";
        config = /* lua */ ''
          require('nvim-surround').setup()
        '';
      }
      {
        plugin = tailwindcss-colors-nvim;
        type = "lua";
        config = /* lua */ ''
          require('tailwindcss-colors').setup()
        '';
      }
      {
        plugin = nvim-ts-context-commentstring;
        type = "lua";
        config = /* lua */ ''
          require('ts_context_commentstring').setup {
            enable_autocmd = false,
          }
        '';
      }
      {
        plugin = nvim-treesitter.withAllGrammars;
        type = "lua";
        config = /* lua */ ''
          require'nvim-treesitter.configs'.setup {
            highlight = {
              enable = true,
              disable = function(lang)
                return lang ~= "javascript"
                  and lang ~= "tsx"
                  and lang ~= "typescript"
                  and lang ~= "astro"
                  and lang ~= "css"
                  and lang ~= "glsl"
                  and lang ~= "nix"
                  and lang ~= "vue"
                  and lang ~= "pug"
                  and lang ~= "prisma"
              end,
              additional_vim_regex_highlighting = true,
            },
          }
        '';
      }
      {
        plugin = vista-vim;
        config = /* vim */ ''
          let g:vista_default_executive = 'nvim_lsp'
          let g:vista_executive_for = {
            \ 'rust': 'ctags',
            \ }

          autocmd QuitPre * silent! :Vista!
        '';
      }
      fzf-vim
      vim-caddyfile
      vim-graphql
      vim-javascript
      vim-jsx-pretty
      rust-vim
      yuck-vim
      neoformat
      vim-crystal
      vim-nix
      tagbar
      rainbow-delimiters-nvim
    ];
  };
}
