local lazypath = vim.fn.stdpath("data") .. "/lazy/lazy.nvim"
if not vim.loop.fs_stat(lazypath) then
  vim.fn.system({
    "git",
    "clone",
    "--filter=blob:none",
    "https://github.com/folke/lazy.nvim.git",
    "--branch=stable", -- latest stable release
    lazypath,
  })
end
vim.opt.rtp:prepend(lazypath)

-- run :GoBuild or :GoTestCompile based on the go file
local function build_go_files()
  if vim.endswith(vim.api.nvim_buf_get_name(0), "_test.go") then
    vim.cmd("GoTestCompile")
  else
    vim.cmd("GoBuild")
  end
end

-- Track session state globally for the noop provider
-- local noop_session_active = false
-- local noop_terminal_provider = {
--   setup = function(config)
--     -- Change to git root directory first
--     local git_root = vim.fn.system("git rev-parse --show-toplevel 2>/dev/null"):gsub("\n", "")
--     if vim.v.shell_error == 0 and git_root ~= "" then
--       vim.cmd("cd " .. git_root)
--     end
--     print("ClaudeCode session configured")
--   end,
--
--   open = function(cmd_string, env_table, effective_config, focus)
--     -- -- Clean up existing Claude Code websocket servers (Neovim processes only)
--     -- local claude_dir = vim.fn.expand("~/.claude/ide")
--     -- if vim.fn.isdirectory(claude_dir) == 1 then
--     --   for _, lock_file in ipairs(vim.fn.glob(claude_dir .. "/*.lock", false, true)) do
--     --     local port = vim.fn.fnamemodify(lock_file, ":t:r")
--     --     local pids = vim.fn.system("lsof -ti:" .. port .. " 2>/dev/null"):gsub("\n", " ")
--     --     
--     --     for pid in pids:gmatch("%S+") do
--     --       local cmd = vim.fn.system("ps -p " .. pid .. " -o comm= 2>/dev/null"):gsub("\n", "")
--     --       if cmd:match("nvim") then
--     --         vim.fn.system("kill -9 " .. pid .. " 2>/dev/null")
--     --         vim.fn.delete(lock_file)
--     --       end
--     --     end
--     --   end
--     -- end
--
--     -- Mark session as active when opening
--     noop_session_active = true
--   end,
--
--   close = function()
--     -- Mark session as inactive when closing
--     noop_session_active = false
--     print("ClaudeCode session closed")
--   end,
--
--   simple_toggle = function(cmd_string, env_table, effective_config)
--     -- Check if already active and print message
--     if noop_session_active then
--       print("ClaudeCode is already running, focusing")
--
--       -- Jump to the rightmost tmux pane (where Claude usually runs)
--       vim.fn.system("tmux select-pane -R")
--       return
--     end
--
--     -- Mark session as active when starting
--     noop_session_active = true
--     print("ClaudeCode session started")
--   end,
--
--   focus_toggle = function(cmd_string, env_table, effective_config)
--     print("Focused on Claude")
--     -- Jump to the rightmost tmux pane (where Claude usually runs)
--     vim.fn.system("tmux select-pane -R")
--   end,
--
--   get_active_bufnr = function()
--     -- Return nil since there's no terminal buffer
--     return nil
--   end,
--
--   is_available = function()
--     -- Always available since it does nothing
--     return true
--   end,
--
--   -- Optional function
--   toggle = function(cmd_string, env_table, effective_config)
--     -- Check if already active and print message
--     if noop_session_active then
--       print("ClaudeCode is already running")
--       return
--     end
--
--     -- Mark session as active when starting
--     noop_session_active = true
--     print("ClaudeCode session started")
--   end,
--
--   _get_terminal_for_test = function()
--     -- For testing only - return nil
--     return nil
--   end,
-- }

----------------
--- plugins ---
----------------
require("lazy").setup({

  -- colorscheme
  { 
    "ellisonleao/gruvbox.nvim", 
    priority = 1000, -- make sure to load this before all the other start plugins
    config = function ()
      require("gruvbox").setup({
        contrast = "hard"
      })
      vim.cmd([[colorscheme gruvbox]])
    end,
  },

  -- automatic dark mode
  -- requires: brew install cormacrelf/tap/dark-notify
  { 
    "cormacrelf/dark-notify",
    config = function ()
      require("dark_notify").run()
    end,
  },

  -- statusline
  { 
    "nvim-lualine/lualine.nvim",
    dependencies = { 'nvim-tree/nvim-web-devicons' },
    config = function ()
      require("lualine").setup({
        options = { theme = 'gruvbox' },
        sections = {
          lualine_c = {
            {
              'filename',
              file_status = true, -- displays file status (readonly status, modified status)
              path = 2 -- 0 = just filename, 1 = relative path, 2 = absolute path
            }
          }
        }
      })
    end,
  },

  -- you know the drill
  {
    "fatih/vim-go",
    config = function ()
      -- we disable most of these features because treesitter and nvim-lsp
      -- take care of it
      vim.g['go_gopls_enabled'] = 0
      vim.g['go_code_completion_enabled'] = 0
      vim.g['go_fmt_autosave'] = 0
      vim.g['go_imports_autosave'] = 0
      vim.g['go_mod_fmt_autosave'] = 0
      vim.g['go_doc_keywordprg_enabled'] = 0
      vim.g['go_def_mapping_enabled'] = 0
      vim.g['go_textobj_enabled'] = 0
      vim.g['go_list_type'] = 'quickfix'
    end,
  },

  -- Highlight, edit, and navigate code
  { 
    'nvim-treesitter/nvim-treesitter',
    dependencies = {
      'nvim-treesitter/nvim-treesitter-textobjects',
    },
    build = ":TSUpdate",
    config = function()
      require('nvim-treesitter.configs').setup({
        ensure_installed = {
          'go',
          'gomod',
          'proto',
          'lua',
          'vimdoc',
          'vim',
          'bash',
          'fish',
          'json',
          'markdown',
          'markdown_inline',
          'mermaid',
        },
        indent = { enable = true },
        incremental_selection = {
          enable = true,
          keymaps = {
            init_selection = "<space>", -- maps in normal mode to init the node/scope selection with space
            node_incremental = "<space>", -- increment to the upper named parent
            node_decremental = "<bs>", -- decrement to the previous node
            scope_incremental = "<tab>", -- increment to the upper scope (as defined in locals.scm)
          },
        },
        autopairs = {
          enable = true,
        },
        highlight = {
          enable = true,

          -- Disable slow treesitter highlight for large files
          disable = function(lang, buf)
            local max_filesize = 100 * 1024 -- 100 KB
            local ok, stats = pcall(vim.loop.fs_stat, vim.api.nvim_buf_get_name(buf))
            if ok and stats and stats.size > max_filesize then
              return true
            end
          end,

          -- Setting this to true will run `:h syntax` and tree-sitter at the same time.
          -- Set this to `true` if you depend on 'syntax' being enabled (like for indentation).
          -- Using this option may slow down your editor, and you may see some duplicate highlights.
          -- Instead of true it can also be a list of languages
          additional_vim_regex_highlighting = false,
        },
        textobjects = {
          select = {
            enable = true,
            lookahead = true, -- Automatically jump forward to textobj, similar to targets.vim
            keymaps = {
              -- You can use the capture groups defined in textobjects.scm
              ['aa'] = '@parameter.outer',
              ['ia'] = '@parameter.inner',
              ['af'] = '@function.outer',
              ['if'] = '@function.inner',
              ['ac'] = '@class.outer',
              ['ic'] = '@class.inner',
              ["iB"] = "@block.inner",
              ["aB"] = "@block.outer",
            },
          },
          move = {
            enable = true,
            set_jumps = true, -- whether to set jumps in the jumplist
            goto_next_start = {
              [']]'] = '@function.outer',
            },
            goto_next_end = {
              [']['] = '@function.outer',
            },
            goto_previous_start = {
              ['[['] = '@function.outer',
            },
            goto_previous_end = {
              ['[]'] = '@function.outer',
            },
          },
          swap = {
            enable = true,
            swap_next = {
              ['<leader>sn'] = '@parameter.inner',
            },
            swap_previous = {
              ['<leader>sp'] = '@parameter.inner',
            },
          },
        },
      })
    end,
  },

  -- search selection via *
  { 'bronson/vim-visual-star-search' },

  {
    'dinhhuy258/git.nvim',
    config = function ()
      require("git").setup()
    end,
  },

  -- file explorer
  {
    "nvim-tree/nvim-tree.lua",
    version = "*",
    dependencies = { 'nvim-tree/nvim-web-devicons' },
    config = function()
      require("nvim-tree").setup({
        sort_by = "case_sensitive",
        filters = {
          dotfiles = true,
        },
        on_attach = function(bufnr)
          local api = require('nvim-tree.api')

          local function opts(desc)
            return {
              desc = 'nvim-tree: ' .. desc,
              buffer = bufnr,
              noremap = true,
              silent = true,
              nowait = true,
            }
          end

          api.config.mappings.default_on_attach(bufnr)

          vim.keymap.set('n', 's', api.node.open.vertical, opts('Open: Vertical Split'))
          vim.keymap.set('n', 'i', api.node.open.horizontal, opts('Open: Horizontal Split'))
          vim.keymap.set('n', 'u', api.tree.change_root_to_parent, opts('Up'))
        end
      })
    end,
  },

  -- save my last cursor position
  {
    "ethanholz/nvim-lastplace",
    config = function()
      require("nvim-lastplace").setup({
        lastplace_ignore_buftype = {"quickfix", "nofile", "help"},
        lastplace_ignore_filetype = {"gitcommit", "gitrebase", "svn", "hgcommit"},
        lastplace_open_folds = true
      })
    end,
  },

  { 
    "AndrewRadev/splitjoin.vim"
  },

  {
    "windwp/nvim-autopairs",
    config = function() 
      local npairs = require("nvim-autopairs")
      npairs.setup {
        check_ts = true,
      }
      
      -- Remove backtick pairing
      local Rule = require('nvim-autopairs.rule')
      npairs.remove_rule('`')
    end
  },


  -- {
  --   "coder/claudecode.nvim",
  --   config = function() 
  --     require("claudecode").setup({
  --       terminal = {
  --         provider = noop_terminal_provider,
  --       },
  --     })
  --   end,
  --   lazy = false,
  --   opts = {
  --     terminal_cmd = "/Users/fatih/.local/bin/claude",
  --   },
  --   cmd = {
  --     "ClaudeCode",
  --     "ClaudeCodeFocus",
  --     "ClaudeCodeSelectModel",
  --     "ClaudeCodeAdd",
  --     "ClaudeCodeSend",
  --     "ClaudeCodeTreeAdd",
  --     "ClaudeCodeDiffAccept",
  --     "ClaudeCodeDiffDeny",
  --   },
  --   keys = {
  --     { "<leader>c", nil, desc = "AI/Claude Code" },
  --     { "<C-t>", "<cmd>ClaudeCode<cr>", desc = "Toggle Claude" },
  --     { "<leader>cf", "<cmd>ClaudeCodeFocus<cr>", desc = "Focus Claude" },
  --     { "<leader>cr", "<cmd>ClaudeCode --resume<cr>", desc = "Resume Claude" },
  --     { "<leader>cC", "<cmd>ClaudeCode --continue<cr>", desc = "Continue Claude" },
  --     { "<leader>cm", "<cmd>ClaudeCodeSelectModel<cr>", desc = "Select Claude model" },
  --     { "<leader>ca", "<cmd>ClaudeCodeAdd %<cr>", desc = "Add current buffer" },
  --     { "<leader>cs", "<cmd>ClaudeCodeSend<cr>", mode = "v", desc = "Send to Claude" },
  --     {
  --       "<leader>cs",
  --       "<cmd>ClaudeCodeTreeAdd<cr>",
  --       desc = "Add file",
  --       ft = { "NvimTree", "neo-tree", "oil" },
  --     },
  --     { "<leader>da", "<cmd>ClaudeCodeDiffAccept<cr>", desc = "Accept diff" },
  --     { "<leader>dd", "<cmd>ClaudeCodeDiffDeny<cr>", desc = "Deny diff" },
  --   }
  -- },

  {
    "sourcegraph/amp.nvim",
    branch = "main", 
    lazy = false,
    opts = { auto_start = true, log_level = "info" },
  },

  {
      'brianhuster/live-preview.nvim',
      dependencies = {
        'ibhagwan/fzf-lua',
  		  'folke/snacks.nvim',
      },
  },

  -- { -- Fuzzy Finder (files, lsp, etc)
  {
    "ibhagwan/fzf-lua",
    dependencies = { "nvim-tree/nvim-web-devicons" },
    opts = {},
    config = function() 
      require("fzf-lua").register_ui_select()
      require('fzf-lua').setup {
        oldfiles = {
           -- include current sessions in old_files mode
           include_current_session = true,
        },
        winopts = {
          -- split = "belowright 10new",
          backdrop = 100,
          border = "single",
          preview = {
            hidden = true,
            default = "bat",
            border = "rounded",
            title = false,
            layout = "vertical",
            horizontal = "right:50%",
          },
        },
        git = {
           files = {
             cwd_header = false,
             prompt        = '❯ ',
             cmd           = 'git ls-files --exclude-standard',
             multiprocess  = true,  -- run command in a separate process
             git_icons     = false, -- show git icons?
             file_icons    = false, -- show file icons (true|"devicons"|"mini")?
             color_icons   = false, -- colorize file|git icons
           },
        },
        files = {
          git_files = false,
          cwd_header = false,
          cwd_prompt = true,
          file_icons = false,
        }
      }

     vim.keymap.set("n", "<C-p>", require("fzf-lua").git_files, {})
     vim.keymap.set("n", "<C-b>", require("fzf-lua").files, {})
     vim.keymap.set("n", "<C-g>", require("fzf-lua").lsp_document_symbols, {})
    end
  },

  -- LSP Plugins
  {
    -- `lazydev` configures Lua LSP for your Neovim config, runtime and plugins
    -- used for completion, annotations and signatures of Neovim apis
    'folke/lazydev.nvim',
    ft = 'lua',
    opts = {
      library = {
        -- Load luvit types when the `vim.uv` word is found
        { path = 'luvit-meta/library', words = { 'vim%.uv' } },
      },
    },
  },

  { 'Bilal2453/luvit-meta', lazy = true },

  {
    'neovim/nvim-lspconfig',
    dependencies = {
      -- Automatically install LSPs and related tools to stdpath for Neovim
      { 'williamboman/mason.nvim', config = true }, -- NOTE: Must be loaded before dependants
      'williamboman/mason-lspconfig.nvim',
      'WhoIsSethDaniel/mason-tool-installer.nvim',

      -- Useful status updates for LSP.
      -- NOTE: `opts = {}` is the same as calling `require('fidget').setup({})`
      { 'j-hui/fidget.nvim', opts = {} },

      'saghen/blink.cmp',
    },
    config = function()
      local capabilities = require('blink.cmp').get_lsp_capabilities()

      local servers = {
        gopls = {
          capabilities = capabilities,
        },
        lua_ls = {
          capabilities = capabilities,
          settings = {
            Lua = {
              completion = {
                callSnippet = 'Replace',
              },
            },
          },
        },
      }

      -- Ensure the servers and tools above are installed
      --  To check the current status of installed tools and/or manually install
      --  other tools, you can run
      --    :Mason
      --
      --  You can press `g?` for help in this menu.
      require('mason').setup()

      -- You can add other tools here that you want Mason to install
      -- for you, so that they are available from within Neovim.
      local ensure_installed = vim.tbl_keys(servers or {})
      vim.list_extend(ensure_installed, {
        'stylua', -- Used to format Lua code
        'gofumpt', -- Used to format Lua code
      })
      require('mason-tool-installer').setup { ensure_installed = ensure_installed }

      require('mason-lspconfig').setup {
        handlers = {
          function(server_name)
            local server = servers[server_name] or {}
            -- This handles overriding only values explicitly passed
            -- by the server configuration above. Useful when disabling
            -- certain features of an LSP (for example, turning off formatting for ts_ls)
            server.capabilities = vim.tbl_deep_extend('force', {}, capabilities, server.capabilities or {})
            require('lspconfig')[server_name].setup(server)
          end,
        },
      }
    end,
   
  },

  { "rafamadriz/friendly-snippets" },

  -- autocompletion
  {
    'saghen/blink.cmp',
    dependencies = { 'rafamadriz/friendly-snippets' },
    version = '1.*',
    opts = {
      keymap = { 
        preset = 'enter',
        ["<Tab>"] = { "select_next", "snippet_forward", "fallback" },
        ["<S-Tab>"] = { "select_next", "snippet_forward", "fallback" },
      },
      appearance = {
        nerd_font_variant = 'mono'
      },
      completion = { 
        documentation = {
          auto_show = true,
          auto_show_delay_ms = 100,
        },
        menu = {
          border = 'rounded',
          draw = {
            treesitter = { 'lsp' },
          },
        },
        list = {
           selection = {
             preselect = false,
             auto_insert = true,
          },
        },
      },
      sources = {
        default = { 'lsp', 'path', 'snippets', 'buffer' },
      },
      fuzzy = { implementation = "prefer_rust_with_warning" }
    },
    opts_extend = { "sources.default" }
  },
})

----------------
--- SETTINGS ---
----------------

-- disable netrw at the very start of our init.lua, because we use nvim-tree
vim.g.loaded_netrw = 1
vim.g.loaded_netrwPlugin = 1

vim.opt.termguicolors = true -- Enable 24-bit RGB colors

vim.opt.number = true        -- Show line numbers
vim.opt.showmatch = true     -- Highlight matching parenthesis
vim.opt.splitright = true    -- Split windows right to the current windows
vim.opt.splitbelow = true    -- Split windows below to the current windows
vim.opt.autowrite = true     -- Automatically save before :next, :make etc.
vim.opt.autochdir = true     -- Change CWD when I open a file

vim.opt.mouse = 'a'                -- Enable mouse support
vim.opt.clipboard = 'unnamedplus'  -- Copy/paste to system clipboard
vim.opt.swapfile = false           -- Don't use swapfile
vim.opt.ignorecase = true          -- Search case insensitive...
vim.opt.smartcase = true           -- ... but not it begins with upper case 
vim.opt.completeopt = 'menuone,noinsert,noselect'  -- Autocomplete options

vim.opt.undofile = true
vim.opt.undodir = vim.fn.stdpath("data") .. "undo"

-- Indent Settings
-- I'm in the Spaces camp (sorry Tabs folks), so I'm using a combination of
-- settings to insert spaces all the time. 
vim.opt.expandtab = true  -- expand tabs into spaces
vim.opt.shiftwidth = 2    -- number of spaces to use for each step of indent.
vim.opt.tabstop = 2       -- number of spaces a TAB counts for
vim.opt.autoindent = true -- copy indent from current line when starting a new line
vim.opt.wrap = true

-- This comes first, because we have mappings that depend on leader
-- With a map leader it's possible to do extra key combinations
-- i.e: <leader>w saves the current file
vim.g.mapleader = ','

-- Fast saving
vim.keymap.set('n', '<Leader>w', ':write!<CR>')
vim.keymap.set('n', '<Leader>q', ':q!<CR>', { silent = true })

-- Some useful quickfix shortcuts for quickfix
vim.keymap.set('n', '<C-n>', '<cmd>cnext<CR>zz')
vim.keymap.set('n', '<C-m>', '<cmd>cprev<CR>zz')
vim.keymap.set('n', '<leader>a', '<cmd>cclose<CR>')

-- Exit on jj and jk
vim.keymap.set('n', 'j', 'gj')
vim.keymap.set('n', 'k', 'gk')

-- Exit on jj and jk
vim.keymap.set('i', 'jj', '<ESC>')
vim.keymap.set('i', 'jk', '<ESC>')

-- Copy current filepath to system clipboard (relative to git root, fallback to absolute path)
vim.keymap.set('n', '<Leader>e', function()
  local git_prefix = vim.fn.system('git rev-parse --show-prefix'):gsub('\n', '')
  local path
  if vim.v.shell_error == 0 then
    path = git_prefix .. vim.fn.expand('%')
  else
    path = vim.fn.expand('%:p')
  end
  vim.fn.setreg('+', path)
  print('Copied to clipboard: ' .. path)
end, { silent = true })

-- Remove search highlight
vim.keymap.set('n', '<Leader><space>', ':nohlsearch<CR>')

-- Search mappings: These will make it so that going to the next one in a
-- search will center on the line it's found in.
vim.keymap.set('n', 'n', 'nzzzv', {noremap = true})
vim.keymap.set('n', 'N', 'Nzzzv', {noremap = true})

-- Don't jump forward if I higlight and search for a word
local function stay_star()
  local sview = vim.fn.winsaveview()
  local args = string.format("keepjumps keeppatterns execute %q", "sil normal! *")
  vim.api.nvim_command(args)
  vim.fn.winrestview(sview)
end
vim.keymap.set('n', '*', stay_star, {noremap = true, silent = true})



-- We don't need this keymap, but here we are. If I do a ctrl-v and select
-- lines vertically, insert stuff, they get lost for all lines if we use
-- ctrl-c, but not if we use ESC. So just let's assume Ctrl-c is ESC.
vim.keymap.set('i', '<C-c>', '<ESC>')

-- If I visually select words and paste from clipboard, don't replace my
-- clipboard with the selected word, instead keep my old word in the
-- clipboard
vim.keymap.set("x", "p", "\"_dP")

-- rename the word under the cursor 
vim.keymap.set("n", "<leader>rw", [[:%s/\<<C-r><C-w>\>/<C-r><C-w>/gI<Left><Left><Left>]])

-- Better split switching
vim.keymap.set('', '<C-j>', '<C-W>j')
vim.keymap.set('', '<C-k>', '<C-W>k')
vim.keymap.set('', '<C-h>', '<C-W>h')
vim.keymap.set('', '<C-l>', '<C-W>l')

-- Terminal mode window switching
vim.keymap.set('t', '<C-h>', '<C-\\><C-N><C-w>h')
vim.keymap.set('t', '<C-j>', '<C-\\><C-N><C-w>j')
vim.keymap.set('t', '<C-k>', '<C-\\><C-N><C-w>k')
vim.keymap.set('t', '<C-l>', '<C-\\><C-N><C-w>l')

-- Visual linewise up and down by default (and use gj gk to go quicker)
vim.keymap.set('n', '<Up>', 'gk')
vim.keymap.set('n', '<Down>', 'gj')

-- Yanking a line should act like D and C
vim.keymap.set('n', 'Y', 'y$')

if vim.fn.getenv("TERM_PROGRAM") == "ghostty" then
  vim.opt.title = true
  vim.opt.titlestring = "%{getcwd()}/%{bufname()}"
end

-- Open help window in a vertical split to the right.
vim.api.nvim_create_autocmd("BufWinEnter", {
    group = vim.api.nvim_create_augroup("help_window_right", {}),
    pattern = { "*.txt" },
    callback = function()
        if vim.o.filetype == 'help' then vim.cmd.wincmd("L") end
    end
})

-- git.nvim
vim.keymap.set('n', '<leader>gb', '<CMD>lua require("git.blame").blame()<CR>')
vim.keymap.set('n', '<leader>go', "<CMD>lua require('git.browse').open(false)<CR>")
vim.keymap.set('x', '<leader>go', ":<C-u> lua require('git.browse').open(true)<CR>")

-- old habits 
vim.api.nvim_create_user_command("GBrowse", 'lua require("git.browse").open(true)<CR>', {
  range = true,
  bang = true,
  nargs = "*",
})

-- File-tree mappings
vim.keymap.set('n', '<leader>n', ':NvimTreeToggle<CR>', { noremap = true })
vim.keymap.set('n', '<leader>f', ':NvimTreeFindFileToggle!<CR>', { noremap = true })


-- vim-go
vim.keymap.set('n', '<leader>b', build_go_files)
vim.api.nvim_create_user_command("A", ":lua vim.api.nvim_call_function('go#alternate#Switch', {true, 'edit'})<CR>", {})
vim.api.nvim_create_user_command("AV", ":lua vim.api.nvim_call_function('go#alternate#Switch', {true, 'vsplit'})<CR>", {})
vim.api.nvim_create_user_command("AS", ":lua vim.api.nvim_call_function('go#alternate#Switch', {true, 'split'})<CR>", {})

-- Go uses gofmt, which uses tabs for indentation and spaces for aligment.
-- Hence override our indentation rules.
vim.api.nvim_create_autocmd('Filetype', {
  group = vim.api.nvim_create_augroup('setIndent', { clear = true }),
  pattern = { 'go' },
  command = 'setlocal noexpandtab tabstop=4 shiftwidth=4'
})

-- -- ClaudeCode mapping
-- vim.keymap.set('n', '<C-t>', ':ClaudeCode<CR>', { noremap = true, silent = true })
-- vim.keymap.set('n', '<leader>ca', '<cmd>ClaudeCodeAdd %<cr>', { desc = "Add current buffer" })
-- vim.keymap.set({'n', 'v'}, '<leader>cs', '<cmd>ClaudeCodeSend<cr>', { desc = "Send to Claude" })

-- Amp mapping
vim.keymap.set('n', '<leader>ab', '<cmd>AmpBuffer<cr>', { desc = "Create Amp buffer" })
vim.keymap.set('x', '<leader>ab', ":'<,'>AmpBuffer<CR>", { desc = "Create Amp buffer from selection" })
vim.keymap.set('n', '<leader>as', '<cmd>AmpSendBuffer<cr>', { desc = "Send buffer to Amp" })
vim.keymap.set('n', '<leader>am', '<cmd>AmpMessage %<cr>', { desc = "Send message to Amp" })
vim.keymap.set('x', '<leader>aa', ":'<,'>AmpAppendBuffer<CR>", { desc = "Append selection to Amp buffer" })

vim.api.nvim_create_user_command("AmpMessage", function(opts)
  local message = opts.args
  if message == "" then
    print("Please provide a message to send")
    return
  end
  
  local amp_message = require("amp.message")
  amp_message.send_message(message)
end, {
  nargs = "*",
  desc = "Send a message to Amp",
})

-- Open new scratch buffer for Amp prompts
vim.api.nvim_create_user_command("AmpBuffer", function(opts)
	local lines = {}
	-- Only get lines if we have a valid range
	local has_range = (opts.range > 0) and (opts.line2 > 0) and (opts.line2 >= opts.line1)
	if has_range then
		lines = vim.api.nvim_buf_get_lines(0, opts.line1 - 1, opts.line2, false)
	end
	
	-- Check if amp-scratch buffer already exists
	local existing_buf = nil
	for _, buf in ipairs(vim.api.nvim_list_bufs()) do
		if vim.api.nvim_buf_is_valid(buf) then
			local buf_name = vim.api.nvim_buf_get_name(buf)
			if buf_name:match("amp%-scratch$") then
				existing_buf = buf
				break
			end
		end
	end
	
	if existing_buf then
		-- Open existing buffer in a vertical split
		vim.cmd("vsplit")
		vim.api.nvim_win_set_buf(0, existing_buf)
		
		-- Only append new lines if we have a selection
		if #lines > 0 then
			local existing_lines = vim.api.nvim_buf_get_lines(existing_buf, 0, -1, false)
			if #existing_lines > 0 and existing_lines[#existing_lines] ~= "" then
				table.insert(existing_lines, "") -- Add blank line separator
			end
			for _, line in ipairs(lines) do
				table.insert(existing_lines, line)
			end
			vim.api.nvim_buf_set_lines(existing_buf, 0, -1, false, existing_lines)
		end
		-- If no selection, just open the existing buffer without modifying it
	else
		-- Create new buffer
		vim.cmd("vsplit")
		local buf = vim.api.nvim_create_buf(false, true)
		vim.api.nvim_win_set_buf(0, buf)
		vim.bo[buf].buftype = "nofile"
		vim.bo[buf].bufhidden = "hide"
		vim.bo[buf].swapfile = false
		vim.api.nvim_buf_set_name(buf, "amp-scratch")
		
		-- Populate buffer with selected lines
		if #lines > 0 then
			vim.api.nvim_buf_set_lines(buf, 0, -1, false, lines)
		end
	end
end, {
	nargs = 0,
	desc = "Open scratch buffer for Amp prompts",
	range = true,
})

-- Send entire buffer contents and close the buffer
vim.api.nvim_create_user_command("AmpSendBuffer", function(opts)
	local buf = vim.api.nvim_get_current_buf()
	local buf_name = vim.api.nvim_buf_get_name(buf)
	
	-- Check if we're in an amp-scratch buffer
	if not buf_name:match("amp%-scratch$") then
		print("AmpSendBuffer can only be used in amp-scratch buffers")
		return
	end
	
	local lines = vim.api.nvim_buf_get_lines(buf, 0, -1, false)
	local content = table.concat(lines, "\n")

	-- Check if content is empty
	if content:match("^%s*$") then
		print("Buffer is empty, nothing to send")
		return
	end

	-- Check if Amp server is running
	local amp = require("amp")
	if not amp.state.server then
		print("Amp server is not running - start it first with :AmpStart")
		return
	end

	-- Check if there are actually connected clients
	local server_status = amp.state.server.get_status and amp.state.server.get_status()
	if not server_status or server_status.client_count == 0 then
		print("No Amp clients connected")
		return
	end

	-- Store buffer info before attempting send (failsafe)
	local should_close_buffer = false
	local amp_message = require("amp.message")
	
	-- Send message and check return value
	local success = amp_message.send_message(content)
	
	if success then
		should_close_buffer = true
		print("Message sent to Amp")
	else
		print("Failed to send to Amp - connection failed")
	end
	
	-- Only close buffer if we explicitly marked it as safe to close
	if should_close_buffer then
		vim.api.nvim_buf_set_lines(buf, 0, -1, false, {})
		vim.api.nvim_buf_delete(buf, { force = true })
	end
end, {
	nargs = "?",
	desc = "Send current buffer contents to Amp",
})

-- Append selected lines to amp-scratch buffer in @filename#L12-46 format
vim.api.nvim_create_user_command("AmpAppendBuffer", function(opts)
  local bufname = vim.api.nvim_buf_get_name(0)
  if bufname == "" then
    print("Current buffer has no filename")
    return
  end

  local relative_path = vim.fn.fnamemodify(bufname, ":.")
  local ref = "@" .. relative_path
  if opts.line1 ~= opts.line2 then
    ref = ref .. "#L" .. opts.line1 .. "-" .. opts.line2
  elseif opts.line1 > 1 then
    ref = ref .. "#L" .. opts.line1
  end

  -- Find amp-scratch buffer
  local scratch_buf = nil
  for _, buf in ipairs(vim.api.nvim_list_bufs()) do
    local buf_name = vim.api.nvim_buf_get_name(buf)
    if buf_name:match("amp%-scratch$") then
      scratch_buf = buf
      break
    end
  end
  
  if not scratch_buf then
    print("No amp-scratch buffer found")
    return
  end
  
  -- Get current lines in scratch buffer and append the reference to the last line
  local scratch_lines = vim.api.nvim_buf_get_lines(scratch_buf, 0, -1, false)
  if #scratch_lines == 0 then
    -- Empty buffer, just add the reference
    table.insert(scratch_lines, ref)
  else
    -- Append to the last line with a space
    scratch_lines[#scratch_lines] = scratch_lines[#scratch_lines] .. " " .. ref
  end
  vim.api.nvim_buf_set_lines(scratch_buf, 0, -1, false, scratch_lines)
  
  print("Appended " .. ref .. " to amp-scratch buffer")
end, {
  range = true,
  desc = "Add file reference (with selection) to amp-scratch buffer",
})

-- The cleanup and git root logic is now handled in the open function above


-- automatically resize all vim buffers if I resize the terminal window
vim.api.nvim_command('autocmd VimResized * wincmd =')

-- https://github.com/neovim/neovim/issues/21771
local exitgroup = vim.api.nvim_create_augroup('setDir', { clear = true })
vim.api.nvim_create_autocmd('DirChanged', {
  group = exitgroup,
  pattern = { '*' },
  command = [[call chansend(v:stderr, printf("\033]7;file://%s\033\\", v:event.cwd))]],
})

vim.api.nvim_create_autocmd('VimLeave', {
  group = exitgroup,
  pattern = { '*' },
  command = [[call chansend(v:stderr, "\033]7;\033\\")]],
})


-- put quickfix window always to the bottom
local qfgroup = vim.api.nvim_create_augroup('changeQuickfix', { clear = true })
vim.api.nvim_create_autocmd('FileType', {
  pattern = 'qf',
  group = qfgroup,
  command = 'wincmd J',
})

vim.api.nvim_create_autocmd('FileType', {
  pattern = 'qf',
  group = qfgroup,
  command = 'setlocal wrap',
})

-- Highlight when yanking (copying) text
--  Try it with `yap` in normal mode
--  See `:help vim.highlight.on_yank()`
vim.api.nvim_create_autocmd('TextYankPost', {
  desc = 'Highlight when yanking (copying) text',
  group = vim.api.nvim_create_augroup('kickstart-highlight-yank', { clear = true }),
  callback = function()
    vim.highlight.on_yank()
  end,
})

-- disable diagnostics, I didn't like them
vim.lsp.handlers["textDocument/publishDiagnostics"] = function() end

-- Run gofmt/gofmpt, import packages automatically on save
vim.api.nvim_create_autocmd('BufWritePre', {
  group = vim.api.nvim_create_augroup('setGoFormatting', { clear = true }),
  pattern = '*.go',
  callback = function()
    local params = vim.lsp.util.make_range_params()
    params.context = { only = { "source.organizeImports" } }
    local result = vim.lsp.buf_request_sync(0, "textDocument/codeAction", params, 2000)
    for _, res in pairs(result or {}) do
      for _, r in pairs(res.result or {}) do
        if r.edit then
          vim.lsp.util.apply_workspace_edit(r.edit, "utf-16")
        else
          vim.lsp.buf.execute_command(r.command)
        end
      end
    end

    vim.lsp.buf.format()
  end
})

-- Use LspAttach autocommand to only map the following keys
-- after the language server attaches to the current buffer
vim.api.nvim_create_autocmd('LspAttach', {
  group = vim.api.nvim_create_augroup('UserLspConfig', {}),
  callback = function(ev)
    -- Buffer local mappings.
    -- See `:help vim.lsp.*` for documentation on any of the below functions
    local opts = { buffer = ev.buf }

    vim.keymap.set('n', 'gd', "<cmd>lua vim.lsp.buf.definition()<CR>", opts)
    vim.keymap.set('n', '<leader>v', "<cmd>vsplit | lua vim.lsp.buf.definition()<CR>", opts)
    vim.keymap.set('n', '<leader>s', "<cmd>belowright split | lua vim.lsp.buf.definition()<CR>", opts)

    vim.keymap.set('n', 'gr', vim.lsp.buf.references, opts)
    vim.keymap.set('n', 'gD', vim.lsp.buf.declaration, opts)
    vim.keymap.set('n', 'gi', vim.lsp.buf.implementation, opts)
    vim.keymap.set('n', '<leader>cl', vim.lsp.codelens.run, opts)
    vim.keymap.set('n', '<leader>rn', vim.lsp.buf.rename, opts)
  end,
})
