import re
import logging
from pathlib import Path
from typing import Any, Dict, List, Set
from tools.utils import ToolError, parse_indices

logger = logging.getLogger("MooseCP")

async def handle(args: Dict[str, Any]):
    """
    Extracts specific sections of a file with absolute line numbers and surrounding context.
    This tool MUST be called immediately before edit_file to verify the exact line numbers 
    and content of the block being replaced, preventing misalignment errors.
    """
    path_str = args.get("path")
    pattern = args.get("pattern")
    lines_str = args.get("lines")
    context = int(args.get("context", 1))
    
    if not path_str:
        raise ToolError("Missing path argument")
    
    path = Path(path_str)
    if not path.is_file():
        raise ToolError(f"{path_str} is not a file.")

    try:
        with open(path, "r", encoding="utf-8", errors="replace") as f:
            all_lines = f.readlines()
    except Exception as e:
        raise ToolError(f"Error reading file {path_str}: {e}")

    total_lines = len(all_lines)
    
    # Case 1: No pattern and no lines provided -> Dump whole file
    if not pattern and not lines_str:
        output = [f"{i+1}: {line}" for i, line in enumerate(all_lines)]
        return [{"type": "text", "text": "".join(output)}]

    # Determine target line numbers (1-based)
    target_lines: Set[int] = set()
    
    if pattern:
        try:
            regex = re.compile(pattern)
            for i, line in enumerate(all_lines):
                if regex.search(line):
                    target_lines.add(i + 1)
        except re.error as e:
            raise ToolError(f"Invalid regex pattern: {e}")
            
    if lines_str:
        try:
            indices = parse_indices(lines_str)
            for idx in indices:
                if 1 <= idx <= total_lines:
                    target_lines.add(idx)
        except ToolError as e:
            raise e
        except Exception as e:
            raise ToolError(f"Error parsing line indices: {e}")

    if not target_lines:
        return [{"type": "text", "text": "No matching lines found."}]

    # Expand targets with context
    lines_to_show: Set[int] = set()
    for line in target_lines:
        for offset in range(-context, context + 1):
            ln = line + offset
            if 1 <= ln <= total_lines:
                lines_to_show.add(ln)

    # Sort and format output
    sorted_lines = sorted(list(lines_to_show))
    output = []
    last_line = None
    
    for ln in sorted_lines:
        if last_line is not None and ln > last_line + 1:
            output.append("...\n")
        
        output.append(f"{ln}: {all_lines[ln-1]}")
        last_line = ln

    return [{"type": "text", "text": "".join(output)}]
