-- ── Bootstrap lazy.nvim ───────────────────────────────────────────────────────
-- lazy.nvim is the plugin manager.  On first launch it clones itself into the
-- Neovim data directory (~/.local/share/nvim/lazy/lazy.nvim).
-- vim.uv is the preferred libuv binding in Neovim >= 0.10; vim.loop is the
-- legacy alias kept for compatibility with older releases.
local lazypath = vim.fn.stdpath("data") .. "/lazy/lazy.nvim"
if not (vim.uv or vim.loop).fs_stat(lazypath) then
    vim.fn.system({
        "git", "clone",
        "--filter=blob:none",   -- blobless clone: skips file blobs, fetching only on checkout
        "https://github.com/folke/lazy.nvim.git",
        "--branch=stable", lazypath,
    })
end
-- Prepend lazy's path so Neovim can find and load it before any other plugin.
vim.opt.rtp:prepend(lazypath)

-- ── Plugins ───────────────────────────────────────────────────────────────────
require("lazy").setup({
    -- CyberQueer colorscheme loaded from the local dotfiles tree (not from GitHub)
    -- so that apply-theme.sh changes are picked up without a plugin update.
    { dir = vim.fn.expand("~/Dotfiles/nvim/theme/cyberqueer.nvim") },
    "rktjmp/lush.nvim",            -- HSL-based colorscheme builder used by cyberqueer
    "tpope/vim-sensible",           -- Sensible Vim defaults (better backspace, history, etc.)
    "junegunn/goyo.vim",            -- Distraction-free writing mode (centers and pads the buffer)
    "arecarn/vim-crunch",           -- Evaluate math expressions inside the buffer
    "preservim/nerdtree",           -- File-tree sidebar navigator
    "ryanoasis/vim-devicons",       -- Nerd Font icons for NERDTree and airline
    -- fzf binary + Vim integration; build step runs fzf#install() to compile the binary
    { "junegunn/fzf", build = function() vim.fn["fzf#install"]() end },
    "junegunn/fzf.vim",             -- :Files, :Rg, :Buffers etc. powered by fzf
    "vim-airline/vim-airline",      -- Status/tabline with Powerline-style separators
    "vim-airline/vim-airline-themes", -- Theme library for vim-airline (cyberqueer theme used)
    "voldikss/vim-floaterm",        -- Floating terminal windows inside Neovim
    "rust-lang/rust.vim",           -- Rust syntax, fmt-on-save, and cargo integration
    "norcalli/nvim-colorizer.lua",  -- Inline hex/RGB color previews in CSS, config files, etc.
    -- CoC: full LSP client with completion, hover, diagnostics; pinned to "release" branch
    { "neoclide/coc.nvim", branch = "release" },
    -- vim-visual-multi: multiple-cursor editing (like Sublime Text ctrl+d)
    { "mg979/vim-visual-multi", branch = "master" },
    "SirVer/ultisnips",             -- Snippet engine (expand/jump triggers configured below)
    "honza/vim-snippets",           -- Community snippet library consumed by UltiSnips
    "mfussenegger/nvim-dap",        -- Debug Adapter Protocol client for step-through debugging
    "elihunter173/dirbuf.nvim",     -- Edit directory contents like a buffer (rename, delete, etc.)
    "tpope/vim-dadbod",             -- Database client (SQL queries against any DB URL)
    "kristijanhusak/vim-dadbod-ui", -- TUI for vim-dadbod (tree browser + result panes)
    "kristijanhusak/vim-dadbod-completion", -- CoC/native completion source for SQL via dadbod
    "nvim-mini/mini.icons",         -- Minimal icon provider used by dadbod-ui and others
    "tadmccorkle/markdown.nvim",    -- Markdown helpers: table formatting, checkboxes, etc.
    -- glow.nvim: render Markdown in a floating window via the `glow` CLI
    { "ellisonleao/glow.nvim", config = true },
    "itchyny/calendar.vim",         -- Interactive calendar; used in the PIM overlay (key "g")
    -- claude-code.nvim: Claude Code integration (opens/communicates with the `claude` CLI)
    {
        "greggh/claude-code.nvim",
        dependencies = { "nvim-lua/plenary.nvim" },
        config = function()
            require("claude-code").setup()
        end,
    },
}, {
    -- Use a bundled colorscheme during the initial lazy install so the UI is
    -- readable even before cyberqueer is downloaded and compiled.
    install = { colorscheme = { "habamax" } },
})

-- ── Colorscheme & UI ──────────────────────────────────────────────────────────
vim.cmd("colorscheme cyberqueer")

-- Tell airline to use Powerline-patched glyphs for segment separators.
vim.g.airline_powerline_fonts = 1
vim.g.airline_theme           = "cyberqueer"

-- Embed machine identity in the airline status bar (section_x = right side).
-- Useful when SSHed into multiple machines or running nested Neovim sessions.
local ipaddr   = vim.trim(vim.fn.system("hostname -i"))
local hostname = vim.trim(vim.fn.system("hostname -s"))
vim.g.airline_section_x = "IP:" .. ipaddr .. " DNS:" .. hostname

-- ── Providers ─────────────────────────────────────────────────────────────────
-- Disable unused remote plugin providers to suppress ":checkhealth" warnings
-- and avoid the startup penalty of probing for ruby/perl interpreters.
vim.g.loaded_ruby_provider = 0
vim.g.loaded_perl_provider = 0


-- ── Editor options ────────────────────────────────────────────────────────────
-- Enable filetype-specific plugins and indentation rules (e.g. Python 4-space).
vim.cmd("filetype plugin indent on")
vim.cmd("syntax enable")

vim.opt.number         = true   -- show absolute line numbers in the gutter
vim.opt.relativenumber = true   -- show relative numbers above/below for fast j/k jumps
vim.opt.cursorline     = true   -- highlight the entire line the cursor is on
vim.opt.cursorcolumn   = true   -- highlight the entire column the cursor is in
vim.opt.showmode       = false  -- airline already shows mode; suppress the redundant "-- INSERT --" message
vim.opt.shiftwidth     = 4      -- number of spaces for each indentation level (>> / <<)
vim.opt.scrolloff      = 5      -- keep at least 5 lines visible above/below the cursor
vim.opt.wrap           = false  -- disable line wrapping (horizontal scroll instead)
vim.opt.incsearch      = true   -- show matches incrementally as you type the search pattern
vim.opt.ignorecase     = true   -- case-insensitive search by default
vim.opt.smartcase      = true   -- override ignorecase when the pattern contains uppercase
vim.opt.showcmd        = true   -- show the partial command being typed in the status line
vim.opt.showmatch      = true   -- briefly jump to the matching bracket when one is typed
vim.opt.hlsearch       = true   -- highlight all search matches (clear with :noh)
vim.opt.history        = 1000   -- keep 1000 entries in command and search history
vim.opt.wildmenu       = true   -- show a completion menu for : commands
vim.opt.wildmode       = "list:longest" -- first tab lists completions, second completes to longest common prefix

-- ── Keymaps ───────────────────────────────────────────────────────────────────
-- window navigation (normal mode)
-- Use Ctrl+hjkl to move between splits instead of the two-key <C-w> prefix.
-- <C-l> cycles to the next window (wraps); others jump directionally.
vim.keymap.set("n", "<C-l>", "<C-w>w")
vim.keymap.set("n", "<C-h>", "<C-w>h")
vim.keymap.set("n", "<C-j>", "<C-w>j")
vim.keymap.set("n", "<C-k>", "<C-w>k")

-- window navigation from terminal-insert mode (exit insert, then move)
-- <C-\><C-n> exits terminal-insert mode first; without it the control sequence
-- is sent to the shell process instead of being handled by Neovim.
vim.keymap.set("t", "<C-h>", "<C-\\><C-n><C-w>h",  { silent = true })
vim.keymap.set("t", "<C-j>", "<C-\\><C-n><C-w>j",  { silent = true })
vim.keymap.set("t", "<C-k>", "<C-\\><C-n><C-w>k",  { silent = true })
vim.keymap.set("t", "<C-l>", "<C-\\><C-n><C-w>w",  { silent = true })

-- auto-enter insert mode when focusing a terminal buffer (skip floaterm)
-- Without this, focusing a terminal buffer requires pressing 'i' manually.
-- Floaterm manages its own mode, so we exclude it to avoid conflicting with
-- its internal state machine.
vim.api.nvim_create_autocmd("BufEnter", {
    pattern  = "term://*",
    callback = function()
        if vim.bo.filetype ~= "floaterm" then
            vim.cmd("startinsert")
        end
    end,
})

-- calendar.vim steals <C-hjkl> for month nav; restore window movement
-- The plugin sets buffer-local mappings on FileType calendar, so we override
-- them with { buffer = true } mappings of our own that fire after the plugin's.
vim.api.nvim_create_autocmd("FileType", {
    pattern = "calendar",
    callback = function()
        local o = { buffer = true, silent = true }
        vim.keymap.set("n", "<C-l>", "<C-w>w", o)
        vim.keymap.set("n", "<C-h>", "<C-w>h", o)
        vim.keymap.set("n", "<C-j>", "<C-w>j", o)
        vim.keymap.set("n", "<C-k>", "<C-w>k", o)
    end,
})

-- quick actions: single-key shortcuts for frequently used commands
-- t: open a new floating terminal via vim-floaterm
vim.keymap.set("n", "t", ":FloatermNew<CR>",    { silent = true })
-- e: toggle the NERDTree sidebar; <C-W>l immediately moves focus back to the
--    editing window so the cursor doesn't land in the tree
vim.keymap.set("n", "e", ":NERDTreeToggle<CR><C-W>l", { silent = true })
-- s: toggle the vim-dadbod-ui database browser
vim.keymap.set("n", "s", ":DBUIToggle<CR>",     { silent = true })
-- q: try to save-quit (wq); fall back to force-quit (q) for unsaved/read-only buffers
vim.keymap.set("n", "q", function()
  local ok = pcall(vim.cmd, "wq")
  if not ok then vim.cmd("q") end
end, { silent = true })

-- insert mode completion: Tab triggers Neovim's built-in keyword completion (<C-N>)
-- S-Tab reverts to a literal Tab character so indentation still works normally.
vim.keymap.set("i", "<TAB>",   "<C-N>")
vim.keymap.set("i", "<S-TAB>", "<TAB>")

-- sudo save: :w!! pipes the buffer through sudo tee, bypassing a read-only
-- file system permission.  The '%' is the current file path.
vim.cmd("ca w!! w !sudo tee '%'")

-- :Vb is a convenience alias for entering visual-block mode without pressing
-- Ctrl+v, which conflicts with paste in some terminal emulators.
vim.cmd("command! Vb normal! <C-v>")

-- ── UltiSnips ─────────────────────────────────────────────────────────────────
vim.g.UltiSnipsExpandTrigger      = "<C-tab>"
vim.g.UltiSnipsJumpForwardTrigger = "<c-b>"
vim.g.UltiSnipsJumpBackwardTrigger = "<c-z>"
vim.g.UltiSnipsEditSplit           = "vertical"

-- ── CoC ───────────────────────────────────────────────────────────────────────
vim.g.coc_global_extensions = {
    "coc-snippets", "coc-powershell", "coc-sh", "coc-omnisharp",
    "coc-clangd", "coc-json", "coc-css", "coc-git", "coc-pyright", "coc-sql",
}

vim.g.coc_snippet_next = "<c-j>"
vim.g.coc_snippet_prev = "<c-k>"

vim.keymap.set("i", "<C-l>", "<Plug>(coc-snippets-expand)",      { remap = true })
vim.keymap.set("v", "<C-j>", "<Plug>(coc-snippets-select)",      { remap = true })
vim.keymap.set("i", "<C-j>", "<Plug>(coc-snippets-expand-jump)", { remap = true })
vim.keymap.set("x", "<leader>x", "<Plug>(coc-convert-snippet)",  { remap = true })

-- tab/s-tab navigate CoC pum, else fall through
vim.keymap.set("i", "<Tab>", function()
    return vim.fn["coc#pum#visible"]() == 1 and vim.fn["coc#pum#next"](1) or "<Tab>"
end, { expr = true, silent = true })

vim.keymap.set("i", "<S-Tab>", function()
    return vim.fn["coc#pum#visible"]() == 1 and vim.fn["coc#pum#prev"](1) or "<S-Tab>"
end, { expr = true, silent = true })

-- CR confirms CoC selection
vim.keymap.set("i", "<CR>", function()
    return vim.fn["coc#pum#visible"]() == 1 and vim.fn["coc#pum#confirm"]() or "<CR>"
end, { expr = true, silent = true })

-- ── PIM floating windows (sideward T: left column overlay) ───────────────────
local function _pim_scratch(label, err)
    vim.cmd("enew")
    vim.bo.buftype   = "nofile"
    vim.bo.buflisted = false
    vim.api.nvim_buf_set_lines(0, 0, -1, false, { "[" .. label .. " unavailable]", "", err or "" })
end

local function _pim_close_win(win)
    if not vim.api.nvim_win_is_valid(win) then return end
    local buf = vim.api.nvim_win_get_buf(win)
    vim.api.nvim_win_close(win, true)
    if vim.api.nvim_buf_is_valid(buf) and vim.bo[buf].buftype == "terminal" then
        pcall(vim.api.nvim_buf_delete, buf, { force = true })
    end
end

local function _pim_float(row, col, height, width, border)
    local buf = vim.api.nvim_create_buf(false, true)
    local win = vim.api.nvim_open_win(buf, true, {
        relative = "editor",
        row      = row,
        col      = col,
        height   = math.max(1, height),
        width    = math.max(1, width),
        style    = "minimal",
        border   = border or "none",
        zindex   = 50,
    })
    vim.api.nvim_set_option_value("winhighlight", "Normal:Normal,NormalNC:Normal", { win = win })
    return win
end

-- n/g/f: individual centered floating windows
local _solo = {}

local function toggle_solo(key, cmd, label)
    local win = _solo[key]
    if win and vim.api.nvim_win_is_valid(win) then
        _pim_close_win(win)
        _solo[key] = nil
        return
    end
    local H = vim.o.lines - 2
    local W = vim.o.columns
    local h = math.max(1, math.floor(H * 0.85))
    local w = math.max(1, math.floor(W * 0.85))
    -- centre accounting for the 1-cell rounded border on each side
    local r = math.max(0, math.floor((H - h - 2) / 2))
    local c = math.max(0, math.floor((W - w - 2) / 2))
    local win_id = _pim_float(r, c, h, w, "rounded")
    local ok, err = pcall(vim.cmd, cmd)
    if not ok then _pim_scratch(label, err) end
    _solo[key] = win_id
end

vim.keymap.set("n", "n", function() toggle_solo("n", "terminal alot",  "alot")         end, { silent = true })
vim.keymap.set("n", "g", function() toggle_solo("g", "terminal khal interactive", "khal") end, { silent = true })
vim.keymap.set("n", "f", function() toggle_solo("f", "terminal abook", "abook")        end, { silent = true })

-- r: sideward-T overlay — left column (bar of the T) with three stacked panes,
--    document remains visible to the right (the stem of the T)
local _pim_wins = {}

local function toggle_pim()
    if #_pim_wins > 0 and vim.api.nvim_win_is_valid(_pim_wins[1]) then
        for _, w in ipairs(_pim_wins) do _pim_close_win(w) end
        _pim_wins = {}
        return
    end

    -- full-screen: alot left, abook top-right, calendar bottom-right
    local H       = vim.o.lines - 2
    local W       = vim.o.columns
    local right_w = math.max(80, math.floor(W * 0.45))
    local left_w  = math.max(1, W - right_w)
    local top_h   = math.max(20, math.floor(H / 2))
    local bot_h   = math.max(1,  H - top_h)

    local w1 = _pim_float(0,     0,      H,     left_w)
    local ok, err = pcall(vim.cmd, "terminal alot")
    if not ok then _pim_scratch("alot", err) end

    local w2 = _pim_float(0,     left_w, top_h, right_w)
    ok, err = pcall(vim.cmd, "terminal abook")
    if not ok then _pim_scratch("abook", err) end

    local w3 = _pim_float(top_h, left_w, bot_h, right_w)
    ok, err = pcall(vim.cmd, "terminal khal interactive")
    if not ok then _pim_scratch("khal", err) end

    _pim_wins = { w1, w2, w3 }
    vim.api.nvim_set_current_win(w1)
end

vim.keymap.set("n", "x", toggle_pim, { silent = true })

-- ── CalDAV background sync on startup ────────────────────────────────────────
vim.api.nvim_create_autocmd("VimEnter", {
    once     = true,
    callback = function()
        vim.fn.jobstart(
            { "sh", "-c", "vdirsyncer sync && ics-to-calendarim" },
            { detach = true }
        )
    end,
})
