local config = require("turbo-debug.config") local M = {} local win_id = nil local buf_id = nil -- Each entry: -- { action, key_letter, pre, post } -- The formatted line is: "
()"  with  taken from the
-- config.opts.keys[action] lookup. Highlight spans are computed against
-- the actual key letter in the final line.
local flow = {
  { "continue",      "c", "(",                 ")ontinue" },
  { "step_over",     "s", "(",                 ")tep over" },
  { "step_into",     "d", "(",                 ")escend into" },
  { "step_out",      "r", "(",                 ")eturn out" },
  { "run_to_cursor", "C", "run to (",          ") cursor" },
  { "restart",       "R", "(",                 ")estart session" },
  { "terminate",     "q", "(",                 ")uit debugging" },
}

local breakpoints = {
  { "breakpoint",        "x", "(", ") toggle breakpoint" },
  { "cond_breakpoint",   "X", "(", ") conditional breakpoint" },
  { "clear_breakpoints", "D", "(", ")elete all breakpoints" },
}

local inspect = {
  { "watch", "W", "(", ")atch expression" },
  { "hover", "K", "(", ") hover / inspect" },
  { "eval",  "E", "(", ")val in REPL" },
}

local meta = {
  { "help", "?", "(", ") this popup" },
}

local globals = {
  { "breakpoint",        "toggle breakpoint" },
  { "cond_breakpoint",   "conditional breakpoint" },
  { "clear_breakpoints", "delete all breakpoints" },
}

local dapui_panes = {
  { "", "expand / jump to target" },
  { "o",    "open target (jump to frame / breakpoint)" },
  { "e",    "edit (watches)" },
  { "d",    "remove (watches)" },
  { "r",    "send to REPL" },
  { "t",    "toggle subtle frames (call stack)" },
}

-- ▶ U+25B6, ● U+25CF, ◉ U+25C9, ◆ U+25C6, ❯ U+276F, ≡ U+2261
local ICON_FLOW    = "\xe2\x96\xb6"
local ICON_BRK     = "\xe2\x97\x8f"
local ICON_INSPECT = "\xe2\x97\x89"
local ICON_META    = "\xe2\x97\x86"
local ICON_CHEVRON = "\xe2\x9d\xaf"
local ICON_STACK   = "\xe2\x89\xa1"

-- a brand header and tagline
local BRAND_TITLE = " " .. ICON_FLOW .. " TURBO DEBUG"
local BRAND_TAGLINE = "   modal text + GUI debugger · left hand keys, right hand mouse"

local function build()
  local lines = { "", BRAND_TITLE, BRAND_TAGLINE, "" }
  local hl = {} -- list of { line_idx, col_start, col_end, hl_group }

  -- title + tagline highlights
  hl[#hl + 1] = { 1, 0, #BRAND_TITLE,   "TurboDebugHelpBrand" }
  hl[#hl + 1] = { 2, 0, #BRAND_TAGLINE, "TurboDebugHelpTagline" }

  local function section(icon, title)
    lines[#lines + 1] = "  " .. icon .. "  " .. title
    local idx = #lines - 1
    hl[#hl + 1] = { idx, 2, 2 + #icon, "TurboDebugHelpSectionIcon" }
    hl[#hl + 1] = { idx, 2 + #icon + 2, 2 + #icon + 2 + #title, "TurboDebugHelpSection" }
  end

  local function entry(pre, keyletter, post)
    -- format: "      
()"
    -- e.g., "      (s)tep over"  or  "      run to (C) cursor"
    -- the key letter is inside the parens pair
    local indent = "      "
    -- pre already ends at "("; post already starts with ")"
    local line = indent .. pre .. keyletter .. post
    local idx = #lines
    lines[#lines + 1] = line
    -- highlight: the key letter itself
    local col = #indent + #pre
    hl[#hl + 1] = { idx, col, col + #keyletter, "TurboDebugHelpKey" }
    -- highlight the surrounding parens dimmer
    hl[#hl + 1] = { idx, col - 1, col, "TurboDebugHelpParen" }
    hl[#hl + 1] = { idx, col + #keyletter, col + #keyletter + 1, "TurboDebugHelpParen" }
  end

  local function add_modal(list)
    for _, row in ipairs(list) do
      local action, letter, pre, post = row[1], row[2], row[3], row[4]
      local configured = config.opts.keys[action]
      if configured then
        -- if the user remapped, show their actual key (preserves surprise-free behavior)
        local lk = configured
        entry(pre, lk, post)
      end
    end
  end

  local function add_global(list)
    for _, row in ipairs(list) do
      local action, label = row[1], row[2]
      local key = config.opts.global_keys[action]
      if key then
        local indent = "      "
        local line = indent .. string.format("%-13s ", key) .. label
        local idx = #lines
        lines[#lines + 1] = line
        hl[#hl + 1] = { idx, #indent, #indent + #key, "TurboDebugHelpKey" }
      end
    end
  end

  local function add_pane(list)
    for _, row in ipairs(list) do
      local key, label = row[1], row[2]
      local indent = "      "
      local line = indent .. string.format("%-6s ", key) .. label
      local idx = #lines
      lines[#lines + 1] = line
      hl[#hl + 1] = { idx, #indent, #indent + #key, "TurboDebugHelpKey" }
    end
  end

  section(ICON_FLOW, "Flow")
  add_modal(flow)
  lines[#lines + 1] = ""

  section(ICON_BRK, "Breakpoints")
  add_modal(breakpoints)
  lines[#lines + 1] = ""

  section(ICON_INSPECT, "Inspect")
  add_modal(inspect)
  lines[#lines + 1] = ""

  section(ICON_META, "Help")
  add_modal(meta)
  lines[#lines + 1] = ""

  section(ICON_CHEVRON, "Always available")
  add_global(globals)
  lines[#lines + 1] = ""

  section(ICON_STACK, "In dap-ui panes")
  add_pane(dapui_panes)
  lines[#lines + 1] = ""

  local help_key = config.opts.keys.help or "?"
  lines[#lines + 1] = "   press " .. help_key .. " anytime to reopen"
  lines[#lines + 1] = ""

  local width = 0
  for _, l in ipairs(lines) do if vim.fn.strdisplaywidth(l) > width then width = vim.fn.strdisplaywidth(l) end end
  return lines, width + 2, hl
end

function M.close()
  if win_id and vim.api.nvim_win_is_valid(win_id) then
    vim.api.nvim_win_close(win_id, true)
  end
  win_id = nil
  if buf_id and vim.api.nvim_buf_is_valid(buf_id) then
    vim.api.nvim_buf_delete(buf_id, { force = true })
  end
  buf_id = nil
end

function M.open()
  M.close()

  local lines, width, hl = build()
  local height = #lines

  buf_id = vim.api.nvim_create_buf(false, true)
  vim.api.nvim_buf_set_lines(buf_id, 0, -1, false, lines)

  local ns = vim.api.nvim_create_namespace("turbo-debug.help")
  for _, span in ipairs(hl) do
    pcall(vim.api.nvim_buf_set_extmark, buf_id, ns, span[1], span[2], {
      end_row = span[1], end_col = span[3], hl_group = span[4],
    })
  end

  vim.bo[buf_id].modifiable = false
  vim.bo[buf_id].bufhidden = "wipe"

  local row = math.floor((vim.o.lines - height) / 2)
  local col = math.floor((vim.o.columns - width) / 2)

  win_id = vim.api.nvim_open_win(buf_id, false, {
    relative = "editor",
    row = row,
    col = col,
    width = width,
    height = height,
    style = "minimal",
    border = "rounded",
    focusable = false,
    noautocmd = true,
  })

  vim.api.nvim_create_autocmd({ "CursorMoved", "InsertEnter", "CmdlineEnter" }, {
    once = true,
    callback = function() M.close() end,
  })
end

function M.is_open()
  return win_id ~= nil and vim.api.nvim_win_is_valid(win_id)
end

return M