local M = {}

-- Find the shout binary: prefer local node_modules, then PATH
local function shout_cmd()
  local local_bin = vim.fn.findfile("node_modules/.bin/shout", vim.fn.getcwd() .. ";")
  if local_bin ~= "" then
    return vim.fn.fnamemodify(local_bin, ":p")
  end
  -- Try bun run
  if vim.fn.executable("bun") == 1 then
    return "bun run shout"
  end
  return "shout"
end

-- Parse shout test output for quickfix entries
local function parse_output(lines, file)
  local items = {}
  for _, line in ipairs(lines) do
    -- Match "FAIL path" lines
    local fail_path = line:match("^FAIL%s+(.+)$")
    if fail_path then
      table.insert(items, {
        filename = fail_path,
        lnum = 1,
        text = "FAIL",
        type = "E",
      })
    end
    -- Match "  $ command" lines after a FAIL (command that failed)
    local cmd = line:match("^%s+%$%s+(.+)$")
    if cmd then
      table.insert(items, {
        filename = file or "",
        lnum = 0,
        text = "$ " .. cmd,
        type = "I",
      })
    end
  end
  return items
end

local function run_shout(args, on_exit)
  local cmd = shout_cmd() .. " test " .. args
  local output_lines = {}

  vim.fn.jobstart(cmd, {
    stdout_buffered = true,
    stderr_buffered = true,
    on_stdout = function(_, data)
      vim.list_extend(output_lines, data)
    end,
    on_stderr = function(_, data)
      vim.list_extend(output_lines, data)
    end,
    on_exit = function(_, code)
      vim.schedule(function()
        if on_exit then
          on_exit(code, output_lines)
        end
      end)
    end,
  })
end

local ns = vim.api.nvim_create_namespace("shout")

local function show_results(code, lines, file)
  -- Clear previous diagnostics
  if file then
    local bufnr = vim.fn.bufnr(file)
    if bufnr ~= -1 then
      vim.diagnostic.reset(ns, bufnr)
    end
  end

  -- Show in quickfix
  local items = parse_output(lines, file)
  vim.fn.setqflist(items, "r")

  if code == 0 then
    vim.notify("shout: all tests passed", vim.log.levels.INFO)
  else
    vim.notify("shout: tests failed (see :copen)", vim.log.levels.ERROR)
    vim.cmd("copen")
  end
end

function M.run()
  local file = vim.fn.expand("%:p")
  vim.notify("shout: running " .. vim.fn.expand("%:t") .. "...", vim.log.levels.INFO)
  run_shout(vim.fn.shellescape(file), function(code, lines)
    show_results(code, lines, file)
  end)
end

function M.update()
  local file = vim.fn.expand("%:p")
  vim.notify("shout: updating " .. vim.fn.expand("%:t") .. "...", vim.log.levels.INFO)
  run_shout("--update " .. vim.fn.shellescape(file), function(code, lines)
    if code == 0 then
      vim.cmd("edit") -- reload the buffer
      vim.notify("shout: updated " .. vim.fn.expand("%:t"), vim.log.levels.INFO)
    else
      show_results(code, lines, file)
    end
  end)
end

function M.run_all()
  vim.notify("shout: running all tests...", vim.log.levels.INFO)
  run_shout("", function(code, lines)
    show_results(code, lines, nil)
  end)
end

return M
