"""
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:
  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

  def walk(node: ast.AST, indent: int = 0) -> None:
   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]"
    # Check if it's a method
    # We can check the indent or the parent, but in AST walk we know if we are inside a ClassDef
    # Let's use a simpler heuristic for the outline: if indent > 0, it's likely a method.
    if indent > 0:
     prefix = "[Method]"
    output.append(f"{'  ' * indent}{prefix} {node.name} (Lines {start_line}-{end_line})")
    doc = get_docstring(node)
    if doc:
     output.append(f"{'  ' * (indent + 1)}\"\"\"{doc}\"\"\"")
  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."

