local Popup = {}

--- Create a new floating window
-- @param config The configuration passed to vim.api.nvim_open_win
-- @param win_opts The options registered with vim.api.nvim_win_set_option
-- @param buf_opts The options registered with vim.api.nvim_buf_set_option
-- @return A new popup
function Popup:new(opts)
  opts = opts or {}
  opts.layout = opts.layout or {}
  opts.win_opts = opts.win_opts or {}
  opts.buf_opts = opts.buf_opts or {}

  Popup.__index = Popup

  local editor_layout = {
    height = vim.o.lines - vim.o.cmdheight - 2, -- Add margin for status and buffer line
    width = vim.o.columns,
  }
  local popup_layout = {
    relative = "editor",
    height = math.floor(editor_layout.height * 0.9),
    width = math.floor(editor_layout.width * 0.8),
    style = "minimal",
    border = "rounded",
  }
  popup_layout.row = math.floor((editor_layout.height - popup_layout.height) / 2)
  popup_layout.col = math.floor((editor_layout.width - popup_layout.width) / 2)

  local obj = {
    buffer = vim.api.nvim_create_buf(false, true),
    layout = vim.tbl_deep_extend("force", popup_layout, opts.layout),
    win_opts = opts.win_opts,
    buf_opts = opts.buf_opts,
  }

  setmetatable(obj, Popup)

  return obj
end

--- Display the popup with the provided content
-- @param content_provider A function accepting the popup's layout and returning the content to display
function Popup:display(content_provider)
  self.win_id = vim.api.nvim_open_win(self.buffer, true, self.layout)
  vim.lsp.util.close_preview_autocmd({ "BufHidden", "BufLeave" }, self.win_id)

  local lines = content_provider(self.layout)
  vim.api.nvim_buf_set_lines(self.bufnr, 0, -1, false, lines)

  -- window options
  for key, value in pairs(self.win_opts) do
    vim.api.nvim_win_set_option(self.win_id, key, value)
  end

  -- buffer options
  for key, value in pairs(self.buf_opts) do
    vim.api.nvim_buf_set_option(self.buffer, key, value)
  end
end

return Popup
