local NodeType = require("snippet_converter.core.node_type")
local base_converter = require("snippet_converter.core.converter")
local err = require("snippet_converter.utils.error")
local io = require("snippet_converter.utils.io")

local M = {}

M.node_visitor = {
  [NodeType.TABSTOP] = function(node)
    if not node.transform then
      return "$" .. node.int
    end
    return ("${%s:%s}"):format(node.int, M.node_visitor[NodeType.TRANSFORM](node.transform))
  end,
  [NodeType.TRANSFORM] = function(node)
    if node.regex then
      err.raise_converter_error("regex in transform node")
    end
    return node.replacement
  end,
  [NodeType.EMACS_LISP_CODE] = function(node)
    return ("`%s`"):format(node.code)
  end,
  [NodeType.TEXT] = function(node)
    -- Escape ambiguous chars and backslashes
    return node.text:gsub([[\]], [[\\]]):gsub("%$", "\\%$"):gsub("`", "\\`")
  end,
}

M.visit_node = setmetatable(M.node_visitor, {
  __index = base_converter.visit_node(M.node_visitor),
})

M.convert = function(snippet, _)
  local description = ""
  if snippet.description then
    -- Replace newline characters with spaces and remove trailing whitespace
    description = ("# name: %s\n"):format(snippet.description:gsub("\n", " "):gsub("%s*$", ""))
  end

  local body = base_converter.convert_ast(snippet.body, M.visit_node)
  return {
    -- The trigger and path are needed for exporting
    trigger = snippet.trigger,
    path = snippet.path,
    body = string.format(
      [[
%s# key: %s
# --
%s]],
      description,
      snippet.trigger,
      body
    ),
  }
end

local HEADER_STRING = [[
# -*- mode: snippet -*-
# Generated by snippet-converter.nvim (https://github.com/smjonas/snippet-converter.nvim)

]]

-- Takes a list of converted snippets for a particular filetype,
-- separates them by newlines and exports them to a file.
-- @param converted_snippets table(trigger: string, body: string) A list of snippet tables where each item is a snippet table to be exported
-- @param mode_name string The filetype of the input snippets
-- @param output_dir string The absolute path to the directory to write the snippets to
M.export = function(converted_snippets, filetype, output_path)
  for _, snippet in ipairs(converted_snippets) do
    local snippet_lines = HEADER_STRING .. snippet.body
    local output_file_path = ("%s/%s-mode/%s"):format(output_path, filetype, snippet.trigger)
    io.write_file(vim.split(snippet_lines, "\n"), output_file_path)
  end
  return output_path
end

return M
