local M = {}

-- Namespace for extmarks
local ns = vim.api.nvim_create_namespace("formula_references")

-- Store state for toggling
local is_active = false
M.extmarks = {}

-- Function to find frontmatter boundaries
local function find_frontmatter_boundaries(lines)
  local start_line, end_line
  for i, line in ipairs(lines) do
    if line:match("^---$") then
      if not start_line then
        start_line = i
      else
        end_line = i
        break
      end
    end
  end
  vim.print("start_line", start_line)
  vim.print("end_line", end_line)
  return start_line, end_line
end

-- Helper function to extract Kurse values from lines within frontmatter
local function extract_kurse_values(lines, start_line, end_line)
  -- vim.print("lines", lines)
  local kurse_values = {}

  if not start_line or not end_line then
    return kurse_values
  end

  local kurse_start
  for i = start_line, end_line do
    if lines[i]:lower():match("^kurse%s*:") then
      -- vim.print(lines[i])
      kurse_start = i
      break
    end
  end
  vim.print("kurse_start", kurse_start)

  if not kurse_start then
    vim.notify("Kurse-Schlüsselwort nicht in Frontmatter gefunden", vim.log.levels.WARN)
    return kurse_values
  end

  local current_line = kurse_start + 1
  vim.print("current_line", current_line)
  while current_line <= end_line do
    local line = lines[current_line]
    -- vim.print("line", line)
    if line:match("^%s*-%s*") then
      local value = line:match("^%s*-%s*[\"' ]*(.*)[\"' ]*$")
      if value and value ~= "" then
        table.insert(kurse_values, value)
      end
      current_line = current_line + 1
    elseif line:match("^%s*$") then
      current_line = current_line + 1
    elseif not line:match("^%s") and current_line > kurse_start + 1 then
      break
    else
      current_line = current_line + 1
    end
  end

  vim.print("kurse_values", kurse_values)
  if vim.tbl_isempty(kurse_values) then
    vim.notify("Keine Kurse-Werte unter dem Kurse-Schlüsselwort gefunden", vim.log.levels.WARN)
  end

  return kurse_values
end

-- Function to parse frontmatter and get Kurse values
local function get_kurse_values()
  local buf = vim.api.nvim_get_current_buf()
  local lines = vim.api.nvim_buf_get_lines(buf, 0, -1, false)

  local start_line, end_line = find_frontmatter_boundaries(lines)
  vim.print("start_line", start_line)
  vim.print("end_line", end_line)
  if not start_line or not end_line then
    vim.notify("Frontmatter-Grenzen (---) nicht gefunden", vim.log.levels.WARN)
    return {}
  end

  -- vim.print("lines", lines)
  return extract_kurse_values(lines, start_line, end_line)
end

-- Function to get math tag under cursor
local function get_math_tag()
  local line = vim.api.nvim_get_current_line()
  local col = vim.api.nvim_win_get_cursor(0)[2]

  local pattern = "%(%d+%.%d+%.?%d*%)"
  local start_pos, end_pos = line:find(pattern)

  if start_pos and end_pos and col >= start_pos - 1 and col <= end_pos - 1 then
    return line:sub(start_pos, end_pos)
  end
  return nil
end

local function search_related_files_ripgrep(kurse_values, math_tag)
  local results = {}
  local current_file = vim.fn.expand("%:p")
  local parent_dir = vim.fn.fnamemodify(current_file, ":p:h")
  vim.print("parent_dir", parent_dir)

  if vim.fn.executable("rg") == 0 then
    vim.notify("ripgrep (rg) ist nicht installiert oder nicht im Pfad.", vim.log.levels.ERROR)
    return results
  end

  local escaped_tag = string.gsub(math_tag, "[%(%)%.%*%+%-%[%]%^%$]", "\\%0")
  vim.print("math_tag", math_tag)
  vim.print("escaped_tag", escaped_tag)

  -- Construct the ripgrep command.
  local command = {
    "rg",
    "--regexp",
    [[^(\s*)(.*?\()]] .. escaped_tag .. [[(\)\s*)$]], -- Capture initial line and indentation
    "--glob",
    "*.md",
    "--line-number",
    -- "--replace",  -- Remove --replace for now, add back later
    -- "$1$2",        -- Remove for initial search
    "--multiline",
    "--multiline-dotall",
    parent_dir,
  }

  local ripgrep_output = vim.fn.systemlist(table.concat(command, " "))
  vim.print("ripgrep_output", ripgrep_output)

  if vim.tbl_isempty(ripgrep_output) then
    vim.notify(
      "Keine Ergebnisse für " .. math_tag .. " in Dateien mit passenden Kurse-Werten gefunden",
      vim.log.levels.INFO
    )
    return results -- No results from ripgrep
  end

  -- Process ripgrep output
  for _, line in ipairs(ripgrep_output) do
    local file_path, line_num_str, content = line:match("^(.-):(%d+):(.*)$")
    if file_path and line_num_str and content then
      local file_name = vim.fn.fnamemodify(file_path, ":t")

      -- Get the initial indentation from the FIRST ripgrep match.
      local initial_indent = content:match("^%s*")
      vim.print("initial_indent", initial_indent)

      if file_path ~= current_file then
        vim.print("file_path", file_path)
        -- Extract Kurse values (Frontmatter parsing - no changes needed here)
        local success, file_content_lines = pcall(vim.fn.readfile, file_path)
        if not success then
          goto continue
        end
        local file_kurse_values = {}
        local start_line, end_line = find_frontmatter_boundaries(file_content_lines)
        if start_line and end_line then
          file_kurse_values = extract_kurse_values(file_content_lines, start_line, end_line)
          vim.print("file_kurse_values", file_kurse_values)
        end

        local has_matching_kurse = false
        vim.print("has_matching_kurse", has_matching_kurse)
        for _, kurse in ipairs(kurse_values) do
          if vim.tbl_contains(file_kurse_values, kurse) then
            has_matching_kurse = true
            break
          end
        end

        if has_matching_kurse then
          -- Construct the SECOND ripgrep command, with the dynamic negative lookahead.
          local indented_block_command = {
            "rg",
            "--regexp",
            [[^]] .. initial_indent .. [[\s+.*$]], -- Negative lookahead for greater indentation.
            "--or",
            [[^\s]],
            "--glob",
            "*.md",
            "--multiline",
            "--multiline-dotall",
            "--replace", --Replace to avoid errors
            "$0",
            "-q",
            file_path,
          }

          local find_command = {
            "rg",
            "--regexp",
            [[^]] .. initial_indent .. [[.*\(]] .. escaped_tag .. [[\)\s*$]], -- Capture initial line and indentation
            "--glob",
            "*.md",
            "--line-number",
            "--multiline",
            "--multiline-dotall",
            file_path,
          }

          local start_line_output = vim.fn.systemlist(table.concat(find_command, " "))
          local block_start_line = 0
          for _, start_line in ipairs(start_line_output) do
            local _, start_num, _ = start_line:match("^(.-):(%d+):(.*)$")
            block_start_line = tonumber(start_num)
          end
          -- Run a SECOND ripgrep to find the end of the indented block.
          local indented_block_output = vim.fn.system(table.concat(indented_block_command, " "))

          local block_end_line = 0

          --Find the line number where it is less indented or the next char is not a space
          if indented_block_output ~= "" then
            local end_line_num_str = indented_block_output:match("^(.-):(%d+):(.*)$")
            block_end_line = tonumber(end_line_num_str)
            vim.print("block_end_line", block_end_line)
          else
            block_end_line = #file_content_lines + 1
          end
          local final_content = ""

          for i = block_start_line, block_end_line - 1, 1 do
            final_content = final_content .. file_content_lines[i] .. "\n"
            vim.print("final_content", final_content)
          end

          vim.print("results", results)
          table.insert(results, {
            file = file_name,
            line = tonumber(line_num_str),
            content = final_content, -- The complete indented block.
          })
        end
      end
      ::continue::
    end
  end

  return results
end

-- Function to clear existing extmarks
local function clear_extmarks()
  local buf = vim.api.nvim_get_current_buf()
  for _, mark in ipairs(M.extmarks) do
    vim.api.nvim_buf_del_extmark(buf, ns, mark)
  end
  M.extmarks = {}
end

-- Function to create virtual text
local function create_virtual_text(results)
  local buf = vim.api.nvim_get_current_buf()
  local current_line = vim.api.nvim_win_get_cursor(0)[1]

  if #results == 0 then
    return
  end

  -- Bestimme die Einrückung der aktuellen Cursorzeile
  local line = vim.api.nvim_get_current_line()
  local cursor_indent = #line:match("^%s*")

  local virt_lines = {}
  for _, result in ipairs(results) do
    -- Bereinige den Inhalt (z. B. LaTeX und entferne \r)
    local content = require("utils.markdown.latex_conceal").conceal_latex(result.content):gsub("\r", "")

    -- Teile den Inhalt in Zeilen auf
    local content_lines = vim.split(content, "\n")

    -- Bestimme die Einrückung der ersten Zeile des Inhalts
    local first_line_indent = content_lines[1] and #content_lines[1]:match("^%s*") or 0

    -- Verarbeite jede Zeile des Inhalts
    for _, content_line in ipairs(content_lines) do
      vim.print("content_line", content_line)
      -- Entferne die ursprüngliche Einrückung der Zeile relativ zur ersten Zeile
      local line_indent = #content_line:match("^%s*")
      local relative_indent = line_indent - first_line_indent
      local line_content = content_line:sub(line_indent + 1)

      -- Füge die Einrückung der Cursorzeile hinzu, plus relative Einrückung
      local indented_content = string.rep(" ", cursor_indent + 2 + relative_indent) .. line_content

      -- Erstelle die virtuelle Zeile
      table.insert(virt_lines, {
        {
          indented_content .. "  >  Zeile " .. result.line .. " in " .. result.file,
          "Markdown_Virtual",
        },
      })
    end
  end

  local mark_id = vim.api.nvim_buf_set_extmark(buf, ns, current_line - 1, -1, {
    virt_text_pos = "overlay",
    virt_text_win_col = 0,
    hl_mode = "combine",
    virt_lines = virt_lines,
  })

  table.insert(M.extmarks, mark_id)
end

-- Main toggle function
local function toggle_formula_references()
  if is_active then
    clear_extmarks()
    is_active = false
    return
  end

  local kurse_values = get_kurse_values()
  vim.print("kurse_values", kurse_values)
  if vim.tbl_isempty(kurse_values) then
    vim.notify("Keine Kurse-Werte in Frontmatter gefunden", vim.log.levels.WARN)
    return
  end

  local math_tag = get_math_tag()
  if not math_tag then
    vim.notify("Kein Mathe-Tag unter dem Cursor gefunden", vim.log.levels.WARN)
    return
  end

  local results = search_related_files_ripgrep(kurse_values, math_tag)
  if #results == 0 then
    vim.notify("Keine Referenzen für " .. math_tag .. " gefunden", vim.log.levels.INFO)
    return
  end

  create_virtual_text(results)
  is_active = true
end

-- Setup keymap
function M.setup()
  vim.keymap.set("n", "<leader>gr", toggle_formula_references, {
    desc = "Formelreferenzen umschalten",
    noremap = true,
    silent = true,
  })
end

return M