import time
import tracemalloc
import traceback
from functools import wraps
from .report import InspectorReport

# Global call counter
_call_counts = {}
# Track if we're in the outermost call (for timing/memory)
_call_depth = {}
# Store wrapped functions to intercept recursive calls
_wrapped_funcs = {}

def inspect(func):
    # If already wrapped, return existing wrapper
    func_id = id(func)
    if func_id in _wrapped_funcs:
        return _wrapped_funcs[func_id]
    
    @wraps(func)
    def wrapper(*args, **kwargs):
        name = func.__name__
        
        # Initialize depth tracking for this function
        if name not in _call_depth:
            _call_depth[name] = 0
        
        # Increment call count (tracks ALL calls including recursive)
        _call_counts[name] = _call_counts.get(name, 0) + 1
        
        # Check if this is the outermost call
        is_outermost = (_call_depth[name] == 0)
        _call_depth[name] += 1
        
        # Only start timing/memory tracking on outermost call
        if is_outermost:
            start_time = time.perf_counter()
            tracemalloc.start()
        
        error = None
        result = None
        
        try:
            # Temporarily replace the function in its module's globals
            # so recursive calls use the wrapped version
            original_func = None
            if hasattr(func, '__globals__') and name in func.__globals__:
                original_func = func.__globals__[name]
                func.__globals__[name] = wrapper
            
            result = func(*args, **kwargs)
            
            # Restore original function
            if original_func is not None:
                func.__globals__[name] = original_func
                
        except Exception:
            error = traceback.format_exc()
            # Restore on error too
            if original_func is not None:
                func.__globals__[name] = original_func
        
        # Decrement depth
        _call_depth[name] -= 1
        
        # Only collect metrics and return report on outermost call
        if is_outermost:
            current, peak = tracemalloc.get_traced_memory()
            tracemalloc.stop()
            end_time = time.perf_counter()
            
            return InspectorReport(
                func_name=name,
                exec_time=end_time - start_time,
                memory_used=current,
                peak_memory=peak,
                call_count=_call_counts[name],
                exception=error,
                result=result
            )
        
        # For recursive calls, just return the result
        return result
    
    # Store the wrapper for reuse
    _wrapped_funcs[func_id] = wrapper
    return wrapper

# Helper for tests if needed
def _reset_call_counts():
    _call_counts.clear()
    _call_depth.clear()
    _wrapped_funcs.clear()