local dap, dapui = require('dap'), require('dapui')

-- Open dapui once dap starts.
dapui.setup()
dap.listeners.before.attach.dapui_config = function()
  dapui.open()
end
dap.listeners.before.launch.dapui_config = function()
  dapui.open()
end
dap.listeners.before.event_terminated.dapui_config = function()
  dapui.close()
end
dap.listeners.before.event_exited.dapui_config = function()
  dapui.close()
end

-- Keybindings used in probably all editors/IDEs.
vim.keymap.set('n', '<F5>', dap.continue)
vim.keymap.set('n', '<F10>', dap.step_over)
vim.keymap.set('n', '<F11>', dap.step_into)
vim.keymap.set('n', '<F12>', dap.step_out)
-- Breakpoints.
vim.keymap.set('n', '<Leader>b', dap.toggle_breakpoint)
vim.keymap.set('n', '<Leader>B', dap.set_breakpoint)
vim.keymap.set('n', '<Leader>lp', function() dap.set_breakpoint(nil, nil, vim.fn.input('Log point message: ')) end)
-- Repl, run and dap-ui bindings.
vim.keymap.set('n', '<Leader>dr', dap.repl.open)
vim.keymap.set('n', '<Leader>dl', dap.run_last)
vim.keymap.set({'n', 'v'}, '<Leader>dh', function() require('dap.ui.widgets').hover() end)
vim.keymap.set({'n', 'v'}, '<Leader>dp', function() require('dap.ui.widgets').preview() end)
vim.keymap.set('n', '<Leader>df', function() local widgets = require('dap.ui.widgets') widgets.centered_float(widgets.frames) end)
vim.keymap.set('n', '<Leader>ds', function() local widgets = require('dap.ui.widgets') widgets.centered_float(widgets.scopes) end)

vim.fn.sign_define('DapBreakpoint', {text='󰬪', texthl='', linehl='', numhl=''})

-- Set up lldb (LLVM debugger)
-- Install archlinux: sudo pacman -S lldb
-- Install ubuntu: sudo apt
dap.adapters.lldb = {
    type = 'server',
    port = "${port}",
    executable = {
        command = '/usr/bin/lldb-dap-18',
        args = { "--port", "${port}" },
    },
}

-- Custom debuggable build runs (on top of rust-analyzer debuggables).
local ExecTypes = {
  TEST = "cargo build --tests -q --message-format=json",
  BIN = "cargo build -q --message-format=json"
}
local function runBuild(type)
  local lines = vim.fn.systemlist(type)
  local output = table.concat(lines, "\n")
  local filename = output:match('^.*"executable":"(.*)",.*\n.*,"success":true}$')

  if filename == nil then
    return error("failed to build cargo project")
  end

  return filename
end
-- Custom debuggable configurations.
dap.configurations.rust = {
  {
    name = "Debug Test",
    type = "lldb",
    request = "launch",
    program = function ()
      return runBuild(ExecTypes.TEST)
    end,
    cwd = "${workspaceFolder}",
    stopOnEntry = false,
    showDisassembly = "never"
  },
  {
    name = "Debug Bin",
    type = "lldb",
    request = "launch",
    program = function ()
      return runBuild(ExecTypes.BIN)
    end,
    cwd = "${workspaceFolder}",
    stopOnEntry = false,
    showDisassembly = "never"
  }
}

-- Set up debugpy via uv
dap.adapters.python = {
    type = 'server',
    port = "${port}",
    host = "127.0.0.1",
    executable = {
        command = "uvx",
        args = {
            "debugpy",
            "--listen",
            "127.0.0.1:${port}",
            "--wait-for-client",
        },
    },
    options = {
        source_filetype = 'python',
    },
}

dap.configurations.python = {
    {
        type = 'python';
        request = 'launch';
        name = "Launch file";
        program = "${file}";
        pythonPath = function()
            return "${workspaceFolder}/.venv/bin/python"
        end;
    },
}
