-------------------------------------------------------------------
-- Settings
-------------------------------------------------------------------
-- vim.opt.winborder = "rounded"
vim.opt.hlsearch = false
vim.opt.tabstop = 4
vim.opt.cursorcolumn = false
vim.opt.ignorecase = true
vim.opt.shiftwidth = 4
vim.opt.smartindent = true
vim.opt.number = true
vim.opt.relativenumber = true
vim.opt.termguicolors = true
vim.opt.undofile = true
vim.opt.signcolumn = "yes"

-------------------------------------------------------------------
-- Autocommands
-------------------------------------------------------------------
-- show cursor line only in active window (TMUX)
local cursorLineGrp = vim.api.nvim_create_augroup("CursorLine", { clear = true })
vim.api.nvim_create_autocmd(
	{ "InsertLeave", "WinEnter" },
	{ pattern = "*", command = "set cursorline", group = cursorLineGrp }
)
vim.api.nvim_create_autocmd(
	{ "InsertEnter", "WinLeave" },
	{ pattern = "*", command = "set nocursorline", group = cursorLineGrp }
)
vim.api.nvim_create_autocmd(
	{ "InsertLeave", "FocusGained" },
	{ pattern = "*", command = "set cursorline", group = cursorLineGrp }
)
vim.api.nvim_create_autocmd(
	{ "InsertEnter", "FocusLost" },
	{ pattern = "*", command = "set nocursorline", group = cursorLineGrp }
)

-------------------------------------------------------------------
-- Functions
-------------------------------------------------------------------
local center_window = function()
	local height = math.floor(0.618 * vim.o.lines)
	local width = math.floor(0.618 * vim.o.columns)
	return {
		anchor = "NW",
		height = height,
		width = width,
		row = math.floor(0.5 * (vim.o.lines - height)),
		col = math.floor(0.5 * (vim.o.columns - width)),
	}
end

local active_statusline = function()
	local mode, mode_hl = MiniStatusline.section_mode({ trunc_width = 120 })
	local diff = MiniStatusline.section_diff({ trunc_width = 75 })
	local git = MiniStatusline.section_git({ trunc_width = 40 })
	local diagnostics = MiniStatusline.section_diagnostics({ trunc_width = 75 })
	local lsp = MiniStatusline.section_lsp({ trunc_width = 75 })
	local filename = MiniStatusline.section_filename({ trunc_width = 140 })
	local fileinfo = string.match(MiniStatusline.section_fileinfo({ trunc_width = 120 }), "%S+")
	local location = MiniStatusline.section_location({ trunc_width = 75 })

	return MiniStatusline.combine_groups({
		{ hl = mode_hl, strings = { mode } },
		{ hl = "MiniStatuslineDevinfo", strings = { git, diff, diagnostics } },
		"%<", -- Mark general truncate point
		{ hl = "MiniStatuslineFilename", strings = { filename } },
		"%=", -- End left alignment
		{ hl = "MiniStatuslineFileinfo", strings = { lsp, fileinfo } },
		{ hl = mode_hl, strings = { location } },
	})
end

local inactive_statusline = function()
	return active_statusline()
end

-------------------------------------------------------------------
-- Plugin installation
-------------------------------------------------------------------
vim.pack.add({
	{ src = "https://git.cmtec.se/cm/nightly_cm.nvim.git" },
	{ src = "https://github.com/mrjones2014/smart-splits.nvim" },
	{ src = "https://github.com/nvim-lua/plenary.nvim" },
	{ src = "https://github.com/kdheepak/lazygit.nvim" },
	{ src = "https://github.com/echasnovski/mini.icons" },
	{ src = "https://github.com/echasnovski/mini.statusline" },
	{ src = "https://github.com/echasnovski/mini.pick" },
	{ src = "https://github.com/echasnovski/mini.extra" },
	{ src = "https://github.com/neovim/nvim-lspconfig" },
	{ src = "https://github.com/lewis6991/gitsigns.nvim" },
	{ src = "https://github.com/MunifTanjim/nui.nvim" },
	{ src = "https://github.com/L3MON4D3/LuaSnip" },
	{ src = "https://github.com/rafamadriz/friendly-snippets" },
	{ src = "https://github.com/saghen/blink.cmp" },
	{ src = "https://github.com/stevearc/conform.nvim" },
	{ src = "https://github.com/nvim-tree/nvim-tree.lua" },
})

-------------------------------------------------------------------
-- Plugin config
-------------------------------------------------------------------
require("conform").setup({
	formatters_by_ft = {
		lua = { "stylua" },
		python = { "isort", "black" },
		rust = { "rustfmt", lsp_format = "fallback" },
		c = { "clang_format" },
		cpp = { "clang_format" },
		javascript = { "prettier" },
		typescript = { "prettier" },
		javascriptreact = { "prettier" },
		typescriptreact = { "prettier" },
		css = { "prettier" },
		html = { "prettier" },
		json = { "prettier" },
		yaml = { "prettier" },
		markdown = { "prettier" },
		sh = { "shfmt" },
	},
	formatters = {
		clang_format = {
			prepend_args = { "--style={BasedOnStyle: LLVM, IndentWidth: 4, TabWidth: 4, UseTab: Never}" },
		},
		shfmt = {
			prepend_args = { "-i", "4" },
		},
	},
	format_on_save = {
		timeout_ms = 3000,
		lsp_format = "fallback",
	},
})
require("mini.pick").setup({ window = { config = center_window } })
require("mini.extra").setup()
require("mini.statusline").setup({ content = { active = active_statusline, inactive = inactive_statusline } })
require("gitsigns").setup({
	signs = {
		add = { text = "▎" },
		change = { text = "▎" },
		delete = { text = "▎" },
		topdelete = { text = "▎" },
		changedelete = { text = "▎" },
		untracked = { text = "▎" },
	},
	preview_config = {
		-- Options passed to nvim_open_win
		border = "solid",
		style = "minimal",
		relative = "cursor",
		row = 0,
		col = 1,
	},
})
require("luasnip.loaders.from_vscode").lazy_load()
require("blink.cmp").setup({
	signature = { enabled = true },
	completion = {
		documentation = { auto_show = false, auto_show_delay_ms = 500 },
		menu = {
			auto_show = true,
			draw = {
				columns = {
					{ "label", "label_description", gap = 1 },
					{ "kind_icon", "kind" },
				},
			},
		},
	},
	sources = {
		default = { "lsp", "buffer", "path", "snippets" },
		providers = {
			-- defaults to `{ 'buffer' }`
			lsp = { fallbacks = {} },
		},
	},
	fuzzy = { implementation = "lua" },
})
require("nvim-tree").setup({
	view = {
		-- Ensures files open in the window where nvim-tree was triggered
		preserve_window_proportions = true,
		centralize_selection = true,
	},
	update_focused_file = {
		enable = true, -- update the tree to focus the current file
		update_root = false, -- don't change the root dir automatically
		ignore_list = {}, -- you can add filetypes to ignore (e.g. "help")
	},
	actions = {
		open_file = {
			quit_on_open = true, -- auto-close tree when file is opened
			resize_window = true,
			window_picker = {
				enable = false, -- disable asking which window to use
			},
		},
	},
})

-------------------------------------------------------------------
-- LSP config
-------------------------------------------------------------------
vim.lsp.enable({ "lua_ls", "rust_analyzer", "bashls", "pyright", "marksman", "clangd", "yamlls" })

local Input = require("nui.input")
local event = require("nui.utils.autocmd").event

local function lsp_rename()
	local curr_name = vim.fn.expand("<cword>")

	local client = vim.lsp.get_clients({ bufnr = 0 })[1]
	local offset_encoding = client and client.offset_encoding or "utf-16"
	local pos_params = vim.lsp.util.make_position_params(0, offset_encoding)

	local input = Input({
		position = {
			row = vim.fn.winline(),
			col = vim.fn.wincol(),
		},
		size = #curr_name + 10,
		border = {
			style = "rounded",
			text = { top = "Rename", top_align = "left" },
		},
	}, {
		prompt = "",
		default_value = curr_name,
		on_submit = function(new_name)
			if new_name and #new_name > 0 and new_name ~= curr_name then
				local params = {
					textDocument = pos_params.textDocument,
					position = pos_params.position,
					newName = new_name,
				}
				-- send to all clients attached to buffer
				vim.lsp.buf_request(0, "textDocument/rename", params)
			end
		end,
	})

	input:mount()

	input:on(event.BufLeave, function()
		input:unmount()
	end)
	input:map("i", "<Esc>", function()
		input:unmount()
	end, { noremap = true, nowait = true })
	input:map("n", "<Esc>", function()
		input:unmount()
	end, { noremap = true, nowait = true })
end

-------------------------------------------------------------------
-- GUI config
-------------------------------------------------------------------
vim.cmd.colorscheme("nightly_cm")
vim.diagnostic.config({
	severity_sort = true,
	float = { border = "rounded", source = "if_many" },
	underline = { severity = vim.diagnostic.severity.WARN },
	signs = {
		numhl = {
			[vim.diagnostic.severity.ERROR] = "DiagnosticSignError",
		},
		text = {
			[vim.diagnostic.severity.ERROR] = "",
			[vim.diagnostic.severity.WARN] = "",
			[vim.diagnostic.severity.INFO] = "",
			[vim.diagnostic.severity.HINT] = "",
		},
	} or {},
	virtual_text = {
		source = "if_many",
		spacing = 2,
		format = function(diagnostic)
			local diagnostic_message = {
				[vim.diagnostic.severity.ERROR] = diagnostic.message,
				[vim.diagnostic.severity.WARN] = diagnostic.message,
				[vim.diagnostic.severity.INFO] = diagnostic.message,
				[vim.diagnostic.severity.HINT] = diagnostic.message,
			}
			return diagnostic_message[diagnostic.severity]
		end,
	},
})

-------------------------------------------------------------------
-- Keybindings
-------------------------------------------------------------------
local map = vim.keymap.set
vim.g.mapleader = " "
-- QoL bindings
map("i", "jj", "<Esc>", { noremap = true, silent = true })
map("n", "<leader>u", ":update<CR> :source<CR>", { noremap = true, silent = true })
map("n", "<leader>w", ":write<CR>", { noremap = true, silent = true })
map("n", "<leader>q", ":quit<CR>", { noremap = true, silent = true })
map("n", "<leader>v", ":vsplit<CR>", { noremap = true, silent = true })
map("n", "n", "nzzzv", { noremap = true, silent = true })
map("n", "N", "Nzzzv", { noremap = true, silent = true })

-- vim.keymap.set("n", "grn", lsp_rename, { desc = "LSP Rename (popup)" })

-- Git
map("n", "<leader>g", ":LazyGitCurrentFile<CR>", { noremap = true, silent = true })

-- File browser
map("n", "<leader>e", ":NvimTreeFindFileToggle!<CR>", { noremap = true, silent = true })
map("n", "<leader>f", ":Pick files<CR>", { noremap = true, silent = true })
map("n", "<leader>h", ":Pick help<CR>", { noremap = true, silent = true })

-- LSP
map("n", "grr", ":Pick lsp scope='references'<CR>", { noremap = true, silent = true })
map("n", "grt", ":Pick lsp scope='type_definition'<CR>", { noremap = true, silent = true })
map("n", "gri", ":Pick lsp scope='implementation'<CR>", { noremap = true, silent = true })

-- Global Copy/Paste
map({ "n", "v" }, "<leader>y", '"+y', { noremap = true, silent = true })
map({ "n", "v" }, "<leader>d", '"+d', { noremap = true, silent = true })
map({ "n", "v" }, "<leader>c", "1z=", { noremap = true, silent = true })

-- Smart splits to support TMUX
map({ "n", "t" }, "<A-h>", require("smart-splits").resize_left, { desc = "Resize left" })
map({ "n", "t" }, "<A-j>", require("smart-splits").resize_down, { desc = "Resize down" })
map({ "n", "t" }, "<A-k>", require("smart-splits").resize_up, { desc = "Resize up" })
map({ "n", "t" }, "<A-l>", require("smart-splits").resize_right, { desc = "Resize right" })
map({ "n", "t" }, "<C-h>", require("smart-splits").move_cursor_left, { desc = "Move cursor left" })
map({ "n", "t" }, "<C-j>", require("smart-splits").move_cursor_down, { desc = "Move cursor down" })
map({ "n", "t" }, "<C-k>", require("smart-splits").move_cursor_up, { desc = "Move cursor up" })
map({ "n", "t" }, "<C-l>", require("smart-splits").move_cursor_right, { desc = "Move cursor right" })
map("n", "<leader><leader>h", require("smart-splits").swap_buf_left, { desc = "Swap buffer left" })
map("n", "<leader><leader>j", require("smart-splits").swap_buf_down, { desc = "Swap buffer down" })
map("n", "<leader><leader>k", require("smart-splits").swap_buf_up, { desc = "Swap buffer up" })
map("n", "<leader><leader>l", require("smart-splits").swap_buf_right, { desc = "Swap buffer right" })
