-- acad - neovim keymaps (academic + Ipe figures)
--
-- Drop into your nvim config (e.g., ~/.config/nvim/lua/config/acad-keymaps.lua)
-- and require it from your init.lua.
--
-- Provides:
--   <leader>a*  - academic_cli wrappers (al new lecture, ah pset, ac select course,
--                 ai info, an new course, ar list, ad dashboard, ao output, ...)
--   <leader>i*  - Ipe figure pipeline (ic create, ie edit, ix export-all, il list, ii info)
--
-- Requires:
--   - `acad` on PATH (pipx install acad)
--   - For <leader>i* keymaps: an `ipe-figures.py` companion script in $UNI_ROOT/scripts/
--     (ipe-figures.py is not yet bundled in acad; will be folded in a future release)

-- Keymaps are automatically loaded on the VeryLazy event
-- Default keymaps that are always set: https://github.com/LazyVim/LazyVim/blob/main/lua/lazyvim/config/keymaps.lua

-- ============================================
-- ACADEMIC WORKFLOW (SPC a prefix)
-- ============================================

local function get_current_course()
  local handle = io.popen("readlink ~/current-course 2>/dev/null | xargs basename")
  if handle then
    local result = handle:read("*a"):gsub("%s+", "")
    handle:close()
    return result ~= "" and result or nil
  end
  return nil
end

-- New Lecture
vim.keymap.set("n", "<leader>al", function()
  local course = get_current_course()
  if not course then
    vim.notify("No current course set. Run: ln -sf $UNI_ROOT/COURSE ~/current-course", vim.log.levels.WARN)
    return
  end

  local topic = vim.fn.input("Lecture topic: ")
  if topic == "" then return end

  -- Use --tex flag for neovim (creates .tex files)
  local cmd = string.format('acad new -c "%s" -t "%s" --tex', course, topic)
  local output = vim.fn.system(cmd)
  vim.notify(output, vim.log.levels.INFO)

  -- Parse the created file path from output (e.g., "✅ Created: course/lecture_01.tex")
  local created_file = output:match("Created: ([^\n]+)")
  if created_file then
    local filepath = vim.fn.expand("$UNI_ROOT/" .. created_file)
    vim.cmd("edit " .. filepath)
    vim.defer_fn(function()
      vim.cmd("VimtexCompile")
    end, 100)
  else
    -- Fallback to glob method
    local pattern = vim.fn.expand("$UNI_ROOT/" .. course .. "/lecture_*.tex")
    local lectures = vim.fn.glob(pattern, false, true)
    if #lectures > 0 then
      table.sort(lectures)
      vim.cmd("edit " .. lectures[#lectures])
      vim.defer_fn(function()
        vim.cmd("VimtexCompile")
      end, 100)
    else
      vim.notify("Could not find lecture file", vim.log.levels.ERROR)
    end
  end
end, { desc = "New Lecture" })

-- New Homework/Problem Set
vim.keymap.set("n", "<leader>ah", function()
  local course = get_current_course()
  if not course then
    vim.notify("No current course set.", vim.log.levels.WARN)
    return
  end

  local title = vim.fn.input("Problem set title (or enter for default): ")
  -- Use --tex flag for neovim
  local cmd = string.format('acad pset -c "%s" --tex', course)
  if title ~= "" then
    cmd = cmd .. string.format(' -t "%s"', title)
  end

  local output = vim.fn.system(cmd)
  vim.notify(output, vim.log.levels.INFO)

  -- Parse the created file path from output
  local created_file = output:match("Created: ([^\n]+)")
  if created_file then
    local filepath = vim.fn.expand("$UNI_ROOT/" .. created_file)
    vim.cmd("edit " .. filepath)
    vim.defer_fn(function()
      vim.cmd("VimtexCompile")
    end, 100)
  else
    -- Fallback to glob method
    local psets = vim.fn.glob(vim.fn.expand("$UNI_ROOT/" .. course .. "/psets/hw_*.tex"), false, true)
    if #psets > 0 then
      table.sort(psets)
      vim.cmd("edit " .. psets[#psets])
      vim.defer_fn(function()
        vim.cmd("VimtexCompile")
      end, 100)
    end
  end
end, { desc = "New Homework" })

-- Select Course (set current-course symlink)
vim.keymap.set("n", "<leader>ac", function()
  local university_dir = vim.fn.expand(vim.env.UNI_ROOT or "~/academic")
  local courses = {}

  -- Get directories in $UNI_ROOT that look like courses
  for _, dir in ipairs(vim.fn.glob(university_dir .. "/*", false, true)) do
    if vim.fn.isdirectory(dir) == 1 then
      local name = vim.fn.fnamemodify(dir, ":t")
      -- Skip hidden dirs and common non-course dirs
      if not name:match("^%.") and not name:match("^scripts$") and not name:match("^tester$") then
        table.insert(courses, name)
      end
    end
  end

  vim.ui.select(courses, { prompt = "Select course:" }, function(choice)
    if choice then
      local target = vim.fn.expand("$UNI_ROOT/" .. choice)
      local link = vim.fn.expand("~/current-course")
      vim.fn.delete(link)
      vim.fn.system(string.format("ln -s %s %s", target, link))
      vim.notify("Current course: " .. choice, vim.log.levels.INFO)
    end
  end)
end, { desc = "Select Course" })

-- Show current course info
vim.keymap.set("n", "<leader>ai", function()
  local output = vim.fn.system("acad info")
  vim.notify(output, vim.log.levels.INFO)
end, { desc = "Course Info" })

-- Add new course
vim.keymap.set("n", "<leader>an", function()
  local name = vim.fn.input("Course name (e.g., Math-55a): ")
  if name == "" then return end
  
  local code = vim.fn.input("Course code (optional): ")
  local instructor = vim.fn.input("Instructor (optional): ")
  local schedule = vim.fn.input("Schedule (optional, e.g., MWF 10-11): ")
  
  local cmd = string.format('acad course add "%s"', name)
  if code ~= "" then cmd = cmd .. string.format(' --code "%s"', code) end
  if instructor ~= "" then cmd = cmd .. string.format(' --instructor "%s"', instructor) end
  if schedule ~= "" then cmd = cmd .. string.format(' --schedule "%s"', schedule) end
  
  local output = vim.fn.system(cmd)
  vim.notify(output, vim.log.levels.INFO)
  
  -- Ask if they want to set it as current
  vim.ui.select({"Yes", "No"}, { prompt = "Set as current course?" }, function(choice)
    if choice == "Yes" then
      local link = vim.fn.expand("~/current-course")
      vim.fn.delete(link)
      vim.fn.system(string.format("ln -s $UNI_ROOT/%s %s", name, link))
      vim.notify("Current course set to: " .. name, vim.log.levels.INFO)
    end
  end)
end, { desc = "New Course" })

-- List all courses
vim.keymap.set("n", "<leader>ar", function()
  local output = vim.fn.system("acad list")
  vim.notify(output, vim.log.levels.INFO)
end, { desc = "List Courses" })

-- Dashboard (nvim-native with highlighting)
vim.keymap.set("n", "<leader>ad", function()
  -- Get data from python as JSON
  local json_output = vim.fn.system("acad dashboard --json 2>/dev/null")
  local ok, data = pcall(vim.json.decode, json_output)
  
  local lines = {}
  local highlights = {} -- {line, col_start, col_end, hl_group}
  
  local function add(text, hl)
    table.insert(lines, text)
    if hl then
      table.insert(highlights, {#lines - 1, 0, #text, hl})
    end
  end
  
  local function add_sep(char)
    add(string.rep(char or "─", 60))
  end
  
  -- Header
  add_sep("━")
  add("                 📚 ACADEMIC DASHBOARD", "Title")
  add("               " .. os.date("%A, %B %d, %Y"), "Comment")
  add_sep()
  
  if ok and data then
    -- Current course
    if data.current then
      add("▶ Current: " .. data.current, "String")
      add_sep()
    end
    
    -- Courses
    add("📖 COURSES", "Keyword")
    add("")
    for _, c in ipairs(data.courses or {}) do
      local marker = c.current and "→ " or "  "
      local stats = string.format("%dL %dP", c.lectures or 0, c.psets or 0)
      local line = string.format("%s%-45s %s", marker, c.name, stats)
      table.insert(lines, line)
      if c.current then
        table.insert(highlights, {#lines - 1, 0, 2, "String"})
      end
      table.insert(highlights, {#lines - 1, #line - #stats, #line, "Comment"})
    end
    add("")
    add_sep()
    
    -- Tasks
    add("📋 TASKS", "WarningMsg")
    add("")
    
    local has_tasks = false
    if data.tasks_today and #data.tasks_today > 0 then
      has_tasks = true
      add("🔴 DUE TODAY", "ErrorMsg")
      for _, t in ipairs(data.tasks_today) do
        add("   • " .. t.description, "ErrorMsg")
      end
      add("")
    end
    
    if data.tasks_week and #data.tasks_week > 0 then
      has_tasks = true
      add("🟡 This Week", "WarningMsg")
      for _, t in ipairs(data.tasks_week) do
        local due = t.due and (" [" .. t.due:sub(1, 10) .. "]") or ""
        add("   • " .. t.description .. due, "Normal")
      end
      add("")
    end
    
    if data.tasks_backlog and #data.tasks_backlog > 0 then
      has_tasks = true
      add("📝 Backlog (no due date)", "Comment")
      for _, t in ipairs(data.tasks_backlog) do
        add("   • " .. t.description, "Comment")
      end
      add("")
    end
    
    if not has_tasks then
      add("✅ No upcoming academic tasks", "String")
      add("")
    end
    
    add_sep()
    
    -- Recent
    add("📁 RECENT", "Function")
    add("")
    for _, f in ipairs(data.recent or {}) do
      local age = f.age or ""
      local hl = age == "today" and "String" or (age == "yesterday" and "WarningMsg" or "Comment")
      add(string.format("   %s/%s    %s", f.course, f.file, age))
      table.insert(highlights, {#lines - 1, #lines[#lines] - #age, #lines[#lines], hl})
    end
    add("")
    add_sep()
    add(string.format("   %s                                    v2.2", data.semester or ""), "Comment")
  else
    -- Fallback to plain text
    local plain = vim.fn.system("acad dashboard")
    for line in plain:gmatch("[^\n]+") do
      add(line)
    end
  end
  
  add_sep("━")
  
  -- Create buffer and window
  local buf = vim.api.nvim_create_buf(false, true)
  vim.api.nvim_buf_set_lines(buf, 0, -1, false, lines)
  vim.bo[buf].modifiable = false
  vim.bo[buf].bufhidden = "wipe"
  
  -- Apply highlights
  for _, hl in ipairs(highlights) do
    pcall(vim.api.nvim_buf_add_highlight, buf, -1, hl[4], hl[1], hl[2], hl[3])
  end
  
  local width = 65
  local height = math.min(#lines + 2, math.floor(vim.o.lines * 0.8))
  vim.api.nvim_open_win(buf, true, {
    relative = "editor",
    width = width,
    height = height,
    row = math.floor((vim.o.lines - height) / 2),
    col = math.floor((vim.o.columns - width) / 2),
    style = "minimal",
    border = "rounded",
    title = " Dashboard ",
    title_pos = "center",
  })
  vim.keymap.set("n", "q", "<cmd>close<cr>", { buffer = buf })
  vim.keymap.set("n", "<Esc>", "<cmd>close<cr>", { buffer = buf })
end, { desc = "Academic Dashboard" })

-- Open course directory (nice file picker)
vim.keymap.set("n", "<leader>ao", function()
  local course = get_current_course()
  local base_path = course and vim.fn.expand("$UNI_ROOT/" .. course) or vim.fn.expand(vim.env.UNI_ROOT or "~/academic")
  
  -- Gather files
  local items = {}
  
  if course then
    -- Add lectures
    local lectures = vim.fn.glob(base_path .. "/lecture_*.tex", false, true)
    table.sort(lectures)
    for _, f in ipairs(lectures) do
      local name = vim.fn.fnamemodify(f, ":t")
      table.insert(items, {display = "📄 " .. name, path = f, type = "lecture"})
    end
    
    -- Add psets
    local psets = vim.fn.glob(base_path .. "/psets/hw_*.tex", false, true)
    table.sort(psets)
    for _, f in ipairs(psets) do
      local name = vim.fn.fnamemodify(f, ":t")
      table.insert(items, {display = "📝 psets/" .. name, path = f, type = "pset"})
    end
    
    -- Add preamble if exists
    local preamble = base_path .. "/preamble.tex"
    if vim.fn.filereadable(preamble) == 1 then
      table.insert(items, {display = "⚙️  preamble.tex", path = preamble, type = "config"})
    end
  else
    -- List courses
    local dirs = vim.fn.glob(base_path .. "/*", false, true)
    for _, d in ipairs(dirs) do
      if vim.fn.isdirectory(d) == 1 then
        local name = vim.fn.fnamemodify(d, ":t")
        if not name:match("^%.") and name ~= "scripts" then
          table.insert(items, {display = "📚 " .. name, path = d, type = "course"})
        end
      end
    end
  end
  
  if #items == 0 then
    vim.notify("No files found", vim.log.levels.WARN)
    return
  end
  
  -- Try telescope first, fall back to vim.ui.select
  local has_telescope, telescope = pcall(require, "telescope.pickers")
  if has_telescope then
    local actions = require("telescope.actions")
    local action_state = require("telescope.actions.state")
    local finders = require("telescope.finders")
    local conf = require("telescope.config").values
    
    telescope.new({}, {
      prompt_title = course and ("📖 " .. course) or "📚 Courses",
      finder = finders.new_table({
        results = items,
        entry_maker = function(entry)
          return {
            value = entry,
            display = entry.display,
            ordinal = entry.display,
            path = entry.path,
          }
        end,
      }),
      sorter = conf.generic_sorter({}),
      attach_mappings = function(prompt_bufnr, map)
        actions.select_default:replace(function()
          actions.close(prompt_bufnr)
          local selection = action_state.get_selected_entry()
          if selection.value.type == "course" then
            -- Set as current and reopen
            local link = vim.fn.expand("~/current-course")
            vim.fn.delete(link)
            vim.fn.system("ln -s " .. selection.path .. " " .. link)
            vim.notify("Switched to: " .. vim.fn.fnamemodify(selection.path, ":t"), vim.log.levels.INFO)
            vim.schedule(function()
              vim.cmd("lua vim.api.nvim_input('<leader>ao')")
            end)
          else
            vim.cmd("edit " .. selection.path)
          end
        end)
        return true
      end,
    }):find()
  else
    -- Fallback to vim.ui.select
    local display_items = {}
    for _, item in ipairs(items) do
      table.insert(display_items, item.display)
    end
    
    vim.ui.select(display_items, {
      prompt = course and ("📖 " .. course) or "📚 Select Course",
    }, function(choice, idx)
      if not choice then return end
      local item = items[idx]
      if item.type == "course" then
        local link = vim.fn.expand("~/current-course")
        vim.fn.delete(link)
        vim.fn.system("ln -s " .. item.path .. " " .. link)
        vim.notify("Switched to: " .. vim.fn.fnamemodify(item.path, ":t"), vim.log.levels.INFO)
      else
        vim.cmd("edit " .. item.path)
      end
    end)
  end
end, { desc = "Open Course Files" })

-- ============================================
-- IPE FIGURES (SPC i prefix)
-- ============================================

-- Get figures directory relative to current file
local function get_figures_dir()
  local file = vim.fn.expand("%:p:h")
  return file .. "/figures"
end

-- Create new Ipe figure
vim.keymap.set("n", "<leader>ic", function()
  local name = vim.fn.input("Figure name: ")
  if name == "" then return end
  
  local figures_dir = get_figures_dir()
  local cmd = string.format('python3 $UNI_ROOT/scripts/ipe-figures.py create "%s" -d "%s" --json', name, figures_dir)
  local output = vim.fn.system(cmd)
  
  local ok, data = pcall(vim.json.decode, output)
  if ok and data then
    -- Copy LaTeX include to clipboard
    vim.fn.setreg('+', data.latex)
    vim.notify("📋 Copied: " .. data.latex, vim.log.levels.INFO)
    -- Open Ipe and switch to workspace 8
    local ipe_path = vim.fn.fnamemodify(data.ipe, ":p")
    vim.fn.system(string.format('open -a Ipe "%s"', ipe_path))
    vim.fn.system('aerospace workspace 8')
  else
    vim.notify("Failed to create figure: " .. output, vim.log.levels.ERROR)
  end
end, { desc = "Create Ipe Figure" })

-- Edit existing Ipe figure (with picker)
vim.keymap.set("n", "<leader>ie", function()
  local figures_dir = get_figures_dir()
  local cmd = string.format('python3 $UNI_ROOT/scripts/ipe-figures.py list -d "%s" --json', figures_dir)
  local output = vim.fn.system(cmd)
  
  local ok, figures = pcall(vim.json.decode, output)
  if not ok or #figures == 0 then
    vim.notify("No figures found", vim.log.levels.WARN)
    return
  end
  
  local items = {}
  for _, f in ipairs(figures) do
    local status = f.has_pdf and "✓" or "○"
    table.insert(items, status .. " " .. f.name)
  end
  
  vim.ui.select(items, { prompt = "📊 Edit Figure:" }, function(_, idx)
    if not idx then return end
    local fig = figures[idx]
    local ipe_path = vim.fn.fnamemodify(fig.ipe, ":p")
    vim.fn.system(string.format('open -a Ipe "%s"', ipe_path))
    vim.fn.system('aerospace workspace 8')
  end)
end, { desc = "Edit Ipe Figure" })

-- Export all figures to PDF
vim.keymap.set("n", "<leader>ix", function()
  local figures_dir = get_figures_dir()
  local output = vim.fn.system(string.format('python3 $UNI_ROOT/scripts/ipe-figures.py export-all -d "%s"', figures_dir))
  vim.notify(output, vim.log.levels.INFO)
end, { desc = "Export All Figures" })

-- List figures
vim.keymap.set("n", "<leader>il", function()
  local figures_dir = get_figures_dir()
  local cmd = string.format('python3 $UNI_ROOT/scripts/ipe-figures.py list -d "%s" --json', figures_dir)
  local output = vim.fn.system(cmd)
  
  local ok, figures = pcall(vim.json.decode, output)
  if not ok or #figures == 0 then
    vim.notify("No figures found", vim.log.levels.WARN)
    return
  end
  
  local lines = {"📊 Ipe Figures:", ""}
  for _, f in ipairs(figures) do
    local status = f.has_pdf and "✓" or "○"
    table.insert(lines, "  " .. status .. " " .. f.name)
  end
  vim.notify(table.concat(lines, "\n"), vim.log.levels.INFO)
end, { desc = "List Ipe Figures" })

-- Insert figure reference at cursor
vim.keymap.set("n", "<leader>ii", function()
  local figures_dir = get_figures_dir()
  local cmd = string.format('python3 $UNI_ROOT/scripts/ipe-figures.py list -d "%s" --json', figures_dir)
  local output = vim.fn.system(cmd)
  
  local ok, figures = pcall(vim.json.decode, output)
  if not ok or #figures == 0 then
    vim.notify("No figures found", vim.log.levels.WARN)
    return
  end
  
  local items = {}
  for _, f in ipairs(figures) do
    table.insert(items, f.name)
  end
  
  vim.ui.select(items, { prompt = "Insert Figure:" }, function(choice)
    if not choice then return end
    local latex = string.format("\\incipe{%s}", choice)
    vim.api.nvim_put({latex}, "c", true, true)
  end)
end, { desc = "Insert Figure Reference" })

-- which-key group registration (if using which-key)
local ok, wk = pcall(require, "which-key")
if ok then
  wk.add({
    { "<leader>a", group = "academic" },
    { "<leader>i", group = "ipe figures" },
    { "<leader>B", group = "blog/site" },
    { "<leader>N", group = "notes" },
    { "<leader>H", group = "homework" },
    { "<leader>E", group = "educational" },
  })
end
