_G._test_cmd_to_wins = {}

_G._build_test_cmd = function()
  local bufnr = vim.fn.bufnr("%")
  local filetype = vim.fn.getbufvar(bufnr, "&filetype")
  if filetype == "" then
    vim.api.nvim_err_writeln("cannot build test command for filetype:" .. filetype)
    return nil
  end

  local path = vim.fn.expand("%:p")

  -- TODO: allow line number to be passed
  -- TODO: check file exists before running command
  if filetype == "ruby" then
    if not path:find("_spec.rb") then
      path = path:gsub("/app/", "/spec/"):gsub([[.rb$]], "_spec.rb")
    end

    return "bundle exec rspec --format=progress --no-profile " .. path
  elseif filetype == "go" then
    return "go test -v " .. path:gsub("^(.*)/(.*go)$", "%1/...")
  elseif filetype == "typescript" or filetype == "javascript" then
    return "pnpm test -- " .. path
  else
    vim.api.nvim_err_writeln("filetype not supported: " .. filetype)
    return nil
  end
end

_G.run_tests = function()
  local cmd = _G._build_test_cmd()
  if cmd == nil then
    return
  end

  vim.api.nvim_command([[silent up]])

  local winid = _G._test_cmd_to_wins[cmd]
  local current_winid = vim.fn.win_getid()
  if
      winid == nil
      or not vim.api.nvim_win_is_valid(winid)
      or winid == current_winid
      or not vim.fn.win_gotoid(winid) == -1
  then
    vim.api.nvim_command([[10split]])
    winid = vim.fn.win_getid()
    _G._test_cmd_to_wins[cmd] = winid
  end

  local buf = vim.api.nvim_create_buf(false, true)
  vim.api.nvim_buf_set_option(buf, "bufhidden", "delete")
  vim.api.nvim_win_set_buf(winid, buf)

  vim.fn.termopen(cmd)
  vim.api.nvim_command([[normal! G]])
  vim.api.nvim_command([[wincmd p]])
end

_G.focus_tests = function()
  local cmd = _G._build_test_cmd()
  if cmd == nil then
    return
  end

  local winid = _G._test_cmd_to_wins[cmd]
  if winid == nil or not vim.api.nvim_win_is_valid(winid) then
    return
  end

  vim.fn.win_gotoid(winid)
  vim.api.nvim_command([[wincmd =]])
end

_G.close_tests = function()
  local current_winid = vim.api.nvim_get_current_win()
  for _, winid in pairs(_G._test_cmd_to_wins) do
    if current_winid == winid and vim.api.nvim_win_is_valid(winid) then
      vim.api.nvim_win_close(winid, false)
      return
    end
  end

  local cmd = _G._build_test_cmd()
  if cmd == nil then
    return
  end

  local winid = _G._test_cmd_to_wins[cmd]
  if winid == nil or not vim.api.nvim_win_is_valid(winid) then
    return
  end

  vim.api.nvim_win_close(winid, false)
end

_G.copy_test_cmd = function()
  local cmd = _G._build_test_cmd()
  if cmd == nil then
    return
  end

  vim.fn.setreg("+", cmd)
  print("Copied: " .. cmd)
end

vim.api.nvim_set_keymap("n", "<leader>cr", [[:lua _G.run_tests()<cr>]], { noremap = true, silent = true })
vim.api.nvim_set_keymap("n", "<leader>cf", [[:lua _G.focus_tests()<cr>]], { noremap = true, silent = true })
vim.api.nvim_set_keymap("n", "<leader>cq", [[:lua _G.close_tests()<cr>]], { noremap = true, silent = true })
vim.api.nvim_set_keymap("n", "<leader>cc", [[:lua _G.copy_test_cmd()<cr>]], { noremap = true, silent = true })