{ pkgs, config, ... }:
let
  render-markdown = pkgs.vimUtils.buildVimPlugin {
    name = "render-markdown";
    src = pkgs.fetchFromGitHub {
      owner = "MeanderingProgrammer";
      repo = "markdown.nvim";
      rev = "78ef39530266b3a0736c48b46c3f5d1ab022c7db";
      hash = "sha256-mddnBvIrekHh60Ix6qIYAnv10Mu40LamGI47EXk9wSo=";
    };
  };
in
{
  programs.nixvim = {
    plugins = {
      ansiesc.enable = true;

      treesitter-textobjects = {
        enable = false;
        settings = {
          lsp_interop.enable = true;
          select = {
            enable = true;

            keymaps = {
              "ai" = {
                query = "@impl.outer";
              };
              "ii" = {
                query = "@impl.inner";
              };
              "af" = {
                query = "@function.outer";
              };
              "if" = {
                query = "@function.inner";
              };

              "ac" = {
                query = "@conditional.outer";
              };
              "ic" = {
                query = "@conditional.inner";
              };
              "al" = {
                query = "@loop.outer";
              };
              "il" = {
                query = "@loop.inner";
              };
            };
          };

          move = {
            enable = true;
            set_jumps = true;
            goto_next_start = {
              "]f" = {
                query = "@function.outer";
              };
              "]c" = {
                query = "@conditional.outer";
              };
              "]l" = {
                query = "@loop.outer";
              };
              "]i" = {
                query = "@impl.outer";
              };
            };
            goto_next_end = {
              "]F" = {
                query = "@function.outer";
              };
              "]C" = {
                query = "@conditional.outer";
              };
              "]L" = {
                query = "@loop.outer";
              };
              "]I" = {
                query = "@impl.outer";
              };
            };
            goto_previous_start = {
              "[f" = {
                query = "@function.outer";
              };
              "[c" = {
                query = "@conditional.outer";
              };
              "[l" = {
                query = "@loop.outer";
              };
              "[i" = {
                query = "@impl.outer";
              };
            };
            goto_previous_end = {
              "[F" = {
                query = "@function.outer";
              };
              "[C" = {
                query = "@conditional.outer";
              };
              "[L" = {
                query = "@loop.outer";
              };
              "[I" = {
                query = "@impl.outer";
              };
            };
          };
        };
      };

      treesitter-context = {
        enable = true;
        settings = {
          multiwindow = true;
          separator = "―";
          max_lines = 4;
          trim_scopes = "outer";
        };
      };

      treesitter = {
        enable = true;

        nixGrammars = false;
        nixvimInjections = false;

        settings = {
          indent.enable = true;

          ensure_installed = "all";
          ignore_install = [
            "wing"
            "brightscript"
          ];

          highlight.enable = true;

          incremental_selection = {
            enable = true;
            keymaps = {
              scope_incremental = "s";
              node_incremental = "+";
              node_decremental = "-";
            };
          };
        };
      };
      rainbow-delimiters.enable = true;
      vim-surround.enable = true;
      lsp-format.enable = true;
      fugitive.enable = true;
      lspkind.enable = true;
      crates.enable = true;
      fidget.enable = true;
      nvim-autopairs.enable = true;
      spider.enable = true;
      origami = {
        enable = true;
        settings.autofold.enabled = false;
      };

      cmp = {
        enable = true;
        autoEnableSources = true;

        settings = {
          completion.completeopt = "noselect";
          preselect = "None";

          sources =
            let
              s = name: { inherit name; };
            in
            [
              {
                name = "nvim_lsp";
                priority = 8;
              }
              # (s "nvim_lsp_signature_help")
              # (s "treesitter")
              (s "path")
              (s "luasnip")
              # (s "crates")
              # (s "git")
              # (s "calc")
              # (s "buffer")
            ];
          mapping =
            let
              next = ''
                function(fallback)
                  if cmp.visible() then
                    cmp.select_next_item()
                  else
                    fallback()
                  end
                end
              '';
              prev = ''
                function(fallback)
                  if cmp.visible() then
                    cmp.select_prev_item()
                  else
                    fallback()
                  end
                end
              '';
            in
            {
              "<C-Space>" = "cmp.mapping.complete()";
              "<CR>" = "cmp.mapping.confirm({ select = true })";
              "<C-d>" = ''
                function(fallback)
                   if cmp.visible() then 
                     if cmp.visible_docs() then
                       cmp.close_docs()
                     else
                       cmp.open_docs()
                     end
                   else
                      fallback()
                   end
                 end
              '';
              "<Tab>" = next;
              "<S-Tab>" = prev;
              "<Down>" = next;
              "<Up>" = prev;
            };
          snippet.expand = "function(args) require('luasnip').lsp_expand(args.body) end";
          sorting.comparators = [
            "offset"
            "exact"
            "score"
            "recently_used"
            "locality"
            "kind"
            "length"
            "order"
          ];
          window =
            let
              common_border = {
                border = "rounded";
                winhighlight = "Normal:Normal,FloatBorder:FloatBorder,CursorLine:Visual,Search:None";
              };
            in
            {
              completion = common_border;
              documentation = common_border;
            };
        };
      };

      image = {
        enable = true;
      };

      # dial.nvim

      which-key = {
        enable = true;
      };

      auto-session = {
        enable = false;
        settings = {
          auto_save_enabled = true;
          auto_restore_enabled = true;
          pre_save_cmds = [ "Neotree close" ];
          post_restore_cmds = [ "Neotree filesystem show" ];
        };
      };

      comment = {
        enable = true;

        settings = {
          sticky = true;
        };
      };

      neo-tree = {
        enable = false;
        settings = {
          close_if_last_window = true;
          enable_git_status = false;

          window = {
            position = "right";
            width = 30;
            # mappings = {
            #   "<bs>" = "navigate_up";
            #   "." = "set_root";
            #   "/" = "fuzzy_finder";
            #   "f" = "filter_on_submit";
            #   "h" = "show_help";
            # };
          };
          filesystem = {
            follow_current_file.enabled = true;
            filteredItems = {
              hide_hidden = false;
              hide_dotfiles = false;
              force_visible_in_empty_folder = true;
              hide_gitignored = false;
            };
          };

        };

      };

      nvim-lightbulb = {
        enable = true;
        settings = {
          autocmd = {
            enabled = true;
            updatetime = 200;
          };
          line = {
            enabled = true;
          };
          number = {
            enabled = true;
            hl = "LightBulbNumber";
          };
          float = {
            enabled = false;
            text = "💡";
          };
          sign = {
            enabled = true;
            text = "💡";
          };
          status_text = {
            enabled = false;
            text = "💡";
          };
        };
      };

      git-conflict = {
        enable = true;
        settings = {
          default_mappings = {
            both = "<leader>cb";
            none = "<leader>c0";
            ours = "<leader>co";
            prev = "]x";
            next = "[x";
            theirs = "<leader>ct";
          };
          disable_diagnostics = false;
          highlights = {
            current = "DiffText";
            incoming = "DiffAdd";
          };
          list_opener = "conflicts";
        };
      };
      gitsigns = {
        enable = true;

        settings = {
          current_line_blame = true;
          current_line_blame_opts = {
            virt_text = true;
            virt_text_pos = "eol";
          };
        };
      };

      lspsaga = {
        enable = true;
        settings = {
          lightbulb.enable = false;
          code_action.keys = {
            quit = "<Esc>";
          };
          symbol_in_winbar.enable = false;
          implement.enable = true;
        };
      };

      typst-vim = {
        enable = true;
        settings = {
          cmd = "${pkgs.typst}/bin/typst";
          conceal_math = 1;
          auto_close_toc = 1;
        };
      };

      lualine = {
        enable = true;
        settings.options.theme = "onedark";
      };

      rustaceanvim = {
        enable = true;

        settings = {
          rustAnalyzerPackage = null;
          auto_attach = true;
          server = {
            standalone = false;
            default_settings = {
              rust-analyzer = {
                inlayHints = {
                  lifetimeElisionHints.enable = "always";
                  # expressionAdjustmentHints = {
                  # enable = "always";
                  # mode = "prefer_postfix";
                  # };
                  discriminantHints.enable = "always";
                };
                # check = {
                # command = "+nightly clippy";
                # };
                cachePriming.enable = true;

                diagnostic = {
                  refreshSupport = true;
                };
              };
              # cargo = {
              #   buildScripts.enable = true;
              #   features = "all";
              #   runBuildScripts = true;
              #   loadOutDirsFromCheck = true;
              # };
              # checkOnSave = true;
              # check = {
              #   allFeatures = true;
              #   command = "clippy";
              #   extraArgs = [ "--no-deps" ];
              # };
              # procMacro = { enable = true; };
              # imports = {
              #   granularity = { group = "module"; };
              #   prefix = "self";
              # };
              # files = {
              #   excludeDirs =
              #     [ ".cargo" ".direnv" ".git" "node_modules" "target" ];
              # };
              #
              # inlayHints = {
              #   bindingModeHints.enable = true;
              #   closureStyle = "rust_analyzer";
              #   closureReturnTypeHints.enable = "always";
              #   discriminantHints.enable = "always";
              #   expressionAdjustmentHints.enable = "always";
              #   implicitDrops.enable = true;
              #   lifetimeElisionHints.enable = "always";
              #   rangeExclusiveHints.enable = true;
              #   reborrowHints.enable = "mutable";
              # };
              #
              # rustc.source = "discover";
              #
              options.diagnostics = {
                enable = true;
                styleLints.enable = true;
              };
            };
          };
        };
      };

      lsp = {
        enable = true;

        servers = {
          astro.enable = true;
          cssls.enable = true;
          nil_ls = {
            enable = true;
            settings = {
              formatting.command = [ "${(pkgs.lib.getExe pkgs.nixfmt)}" ];
            };
            extraOptions = {
              nix = {
                maxMemoryMB = 0;
                flake = {
                  autoArchive = true;
                  autoEvalInputs = true;
                  nixpkgsInputName = "nixpkgs";
                };
              };
            };
          };

          clangd = {
            enable = true;
            filetypes = [
              "c"
              "cpp"
              "objc"
              "objcpp"
            ];
          };
          eslint = {
            enable = true;
            filetypes = [
              "javascript"
              "javascriptreact"
              "typescript"
              "typescriptreact"
            ];
          };
          html = {
            enable = true;
            filetypes = [ "html" ];
          };
          jsonls = {
            enable = true;
            filetypes = [
              "json"
              "jsonc"
            ];
          };
          pyright = {
            enable = true;
            filetypes = [ "python" ];
          };
          bashls = {
            enable = true;

            filetypes = [
              "sh"
              "bash"
            ];
          };
          # ts_ls = {
          #   enable = true;
          #   filetypes =
          #     [ "javascript" "javascriptreact" "typescript" "typescriptreact" ];
          # };
          # marksman.enable = true;
          yamlls = {
            enable = true;
            filetypes = [ "yaml" ];
          };
        };

        inlayHints = true;

        keymaps = {
          lspBuf = {
            "<leader>;" = "format";
            "gh" = "hover";
          };
        };
      };

      # leap = {
      # enable = true;
      # };

      wilder = {
        enable = true;

        settings = {
          modes = [
            "/"
            ":"
            "?"
          ];
          enable_cmdline_enter = true;
          before_cursor = true;
          use_cmdlinechanged = true;

          next_key = "<Tab>";
          prev_key = "<S-Tab>";
          accept_key = "<Down>";
          reject_key = "<Up>";
        };

        options = {
          pipeline = config.lib.nixvim.mkRaw ''
            wilder.branch(
              wilder.cmdline_pipeline({
                language = 'python',
                fuzzy = 2,
              }),
              wilder.python_search_pipeline({
                pattern = wilder.python_fuzzy_pattern(),
                sorter = wilder.python_difflib_sorter(),
                engine = 're',
              }),
              wilder.substitute_pipeline({
                pipeline = wilder.python_search_pipeline({
                  skip_cmdtype_check = 1,
                  pattern = wilder.python_fuzzy_pattern({
                    start_at_boundary = 0,
                  }),
                }),
              }),
              {
                wilder.check(function(ctx, x) return x == "" end),
                wilder.history(),
              },
              wilder.python_file_finder_pipeline({
                file_command = {'${pkgs.ripgrep}/bin/rg', '--files'},
                dir_command = {'${pkgs.fd}/bin/fd', '-td'},
                filters = {'cpsm_filter'},
              })
            )
          '';

          renderer = config.lib.nixvim.mkRaw ''
            (function()
              local highlighters = {
                wilder.pcre2_highlighter(),
                -- wilder.lua_fzy_highlighter(),
              }

              local popupmenu_renderer = wilder.popupmenu_renderer(
                wilder.popupmenu_border_theme({
                  border = 'rounded',
                  empty_message = wilder.popupmenu_empty_message_with_spinner(),
                  highlighter = highlighters,
                  highlights = {
                    accent = wilder.make_hl('WilderAccent', 'Pmenu', {{a = 1}, {a = 1}, {foreground = '#f4468f'}}),
                  },
                  left = {
                    ' ',
                    wilder.popupmenu_devicons(),
                    wilder.popupmenu_buffer_flags({
                      flags = ' a + ',
                      icons = {['+'] = '', a = '', h = ''},
                    }),
                  },
                  right = {
                    ' ',
                    wilder.popupmenu_scrollbar(),
                  },
                })
              )

              local wildmenu_renderer = wilder.wildmenu_renderer({
                highlights = {
                  accent = wilder.make_hl('WilderAccent', 'Pmenu', {{a = 1}, {a = 1}, {foreground = '#f4468f'}}),
                },
                highlighter = highlighters,
                separator = ' · ',
                left = {' ', wilder.wildmenu_spinner(), ' '},
                right = {' ', wilder.wildmenu_index()},
              })

              return wilder.renderer_mux({
                [':'] = popupmenu_renderer,
                ['/'] = wildmenu_renderer,
                substitute = wildmenu_enderer,
              })
            end)()
          '';
        };
      };

      floaterm = {
        enable = true;
        settings = {
          opener = "edit";
          width = 0.8;
          height = 0.8;
        };
      };
      telescope = {
        enable = true;

        extensions = {
          ui-select.enable = true;
          fzf-native.enable = true;
          frecency.enable = true;
        };

        settings = {
          file_ignore_patterns = [
            "^.git/"
            "^.jj/"
            "^.vscode/"
            "^.idea/"
            "^build/"
            "^target/"
          ];
          defaults = {
            path_display = [ "smart" ];
            layout_strategy = "flex";
            layout_config = {
              width = 0.99;
              height = 0.99;
            };
          };
        };
      };

      # diagnostics
      trouble = {
        enable = true;
      };

      # for lsp/cmp inside markdown code blocks
      otter = {
        enable = true;
      };

      none-ls = {
        enable = true;
        sources = {
          formatting.nixpkgs_fmt.enable = true;
          code_actions.statix.enable = true;
          diagnostics = {
            statix.enable = true;
            deadnix.enable = true;
          };
        };
      };
      nix.enable = true;

      web-devicons.enable = true;
    };
    extraPlugins = with pkgs.vimPlugins; [
      telescope-ui-select-nvim
      telescope-fzf-native-nvim

      vim-suda
      render-markdown
      vim-astro
      nvim-web-devicons
      vim-visual-multi
      vim-gh-line
      neoconf-nvim
      vim-autoswap
      targets-vim

      # fzy-lua-native
      cpsm

      # jj
      vim-jjdescription

      hover-nvim

    ];

  };
}
