"""
Outline Tool - Hierarchical code outline extraction via stdlib ast.

This module provides the CodeOutliner class for generating a hierarchical
outline of Python source code, showing classes, methods, and functions
with their line ranges and docstrings.

Key Features:
 - Uses Python's built-in ast module (no external dependencies)
 - Extracts class and function definitions with line ranges
 - Includes first line of docstrings for each definition
 - Distinguishes between methods and top-level functions

Usage:
 outliner = CodeOutliner()
 outline = outliner.outline(python_code)

Output Format:
 [Class] ClassName (Lines 10-50)
   'First line of class docstring'
   [Method] __init__ (Lines 11-20)
   [Method] process (Lines 22-35)
 [Func] top_level_function (Lines 55-70)

Integration:
 - Used by mcp_client.py for py_get_code_outline tool
 - Used by simulation tests for code structure verification

See Also:
 - src/file_cache.py for ASTParser (tree-sitter based)
 - src/summarize.py for heuristic file summaries
"""
import ast

from pathlib import Path


class CodeOutliner:
 def __init__(self) -> None:
  pass

 def outline(self, code: str) -> str:
  """
  [C: tests/test_outline_tool.py:test_code_outliner_imgui_scopes, tests/test_outline_tool.py:test_code_outliner_nested_ifs, tests/test_outline_tool.py:test_code_outliner_type_hints]
  """
  code = code.lstrip(chr(0xFEFF))
  try:
   tree = ast.parse(code)
  except SyntaxError as e:
   return f"ERROR parsing code: {e}"
  output = []

  def get_docstring(node: ast.AST) -> str | None:
   if isinstance(node, (ast.AsyncFunctionDef, ast.FunctionDef, ast.ClassDef, ast.Module)):
    doc = ast.get_docstring(node)
    if doc:
     return doc.splitlines()[0]
   return None

  count = [0]
  def walk(node: ast.AST, indent: int = 0) -> None:
   """
   [C: src/summarize.py:_summarise_python]
   """
   count[0] += 1
   if count[0] > 100000:
    raise Exception("Infinite loop detected! " + str(type(node)))
   """
   [C: src/summarize.py:_summarise_python]
   """
   if isinstance(node, ast.ClassDef):
    start_line = node.lineno
    end_line = getattr(node, "end_lineno", start_line)
    output.append(f"{'  ' * indent}[Class] {node.name} (Lines {start_line}-{end_line})")
    doc = get_docstring(node)
    if doc:
     output.append(f"{'  ' * (indent + 1)}\"\"\"{doc}\"\"\"")
    for item in node.body:
     walk(item, indent + 1)
   elif isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)):
    start_line = node.lineno
    end_line = getattr(node, "end_lineno", start_line)
    prefix = "[Async Func]" if isinstance(node, ast.AsyncFunctionDef) else "[Func]"
    if indent > 0:
     prefix = "[Method]"
    returns = ""
    if getattr(node, "returns", None):
     try:
      returns = f" -> {ast.unparse(node.returns)}"
     except Exception:
      pass
    output.append(f"{'  ' * indent}{prefix} {node.name}{returns} (Lines {start_line}-{end_line})")
    doc = get_docstring(node)
    if doc:
     output.append(f"{'  ' * (indent + 1)}\"\"\"{doc}\"\"\"")
    for item in node.body:
     walk(item, indent + 1)
   elif isinstance(node, ast.With):
    is_imgui = False
    try:
     for item in node.items:
      ctx_str = ast.unparse(item.context_expr)
      if "imscope." in ctx_str or "imgui." in ctx_str:
       start_line = node.lineno
       end_line = getattr(node, "end_lineno", start_line)
       output.append(f"{'  ' * indent}[ImGui Scope] {ctx_str} (Lines {start_line}-{end_line})")
       is_imgui = True
       break
    except Exception:
     pass
    for item in node.body:
     walk(item, indent + 1 if is_imgui else indent)
   else:
    for block_attr in ("body", "orelse", "handlers", "finalbody"):
     block = getattr(node, block_attr, [])
     if isinstance(block, list):
      for item in block:
       walk(item, indent)
  
  for node in tree.body:
   walk(node)
  return "\n".join(output)

def get_outline(path: Path, code: str) -> str:
 suffix = path.suffix.lower()
 if suffix == ".py":
  outliner = CodeOutliner()
  return outliner.outline(code)
 else:
  return f"Outlining not supported for {suffix} files yet."