"""Debugging utilities for pattern development."""

import inspect
import time
from contextlib import contextmanager
from typing import Any, Callable, Dict, List, Optional

from pygeai_orchestration.core.base import BasePattern, PatternResult


class DebugTracer:
    """Trace pattern execution for debugging."""

    def __init__(self, enabled: bool = True):
        """Initialize debug tracer.

        Args:
            enabled: Enable tracing
        """
        self.enabled = enabled
        self.traces: List[Dict[str, Any]] = []

    def trace(
        self,
        event: str,
        pattern: Optional[str] = None,
        data: Optional[Dict[str, Any]] = None
    ) -> None:
        """Record a trace event.

        Args:
            event: Event description
            pattern: Pattern name
            data: Additional data
        """
        if not self.enabled:
            return

        trace_entry = {
            "timestamp": time.time(),
            "event": event,
            "pattern": pattern,
            "data": data or {}
        }

        self.traces.append(trace_entry)

    def get_traces(self, pattern: Optional[str] = None) -> List[Dict[str, Any]]:
        """Get recorded traces.

        Args:
            pattern: Filter by pattern name

        Returns:
            List of trace entries
        """
        if pattern:
            return [t for t in self.traces if t.get("pattern") == pattern]
        return self.traces

    def clear(self) -> None:
        """Clear all traces."""
        self.traces.clear()

    def format_traces(self, pattern: Optional[str] = None) -> str:
        """Format traces as readable string.

        Args:
            pattern: Filter by pattern name

        Returns:
            Formatted trace output
        """
        traces = self.get_traces(pattern)

        if not traces:
            return "No traces recorded"

        lines = ["Debug Traces:", "=" * 60]

        for i, trace in enumerate(traces, 1):
            timestamp = time.strftime(
                "%H:%M:%S",
                time.localtime(trace["timestamp"])
            )
            event = trace["event"]
            pattern_name = trace.get("pattern", "N/A")

            lines.append(f"\n[{i}] {timestamp} - {pattern_name}")
            lines.append(f"    Event: {event}")

            if trace.get("data"):
                lines.append("    Data:")
                for key, value in trace["data"].items():
                    value_str = str(value)[:100]
                    lines.append(f"      {key}: {value_str}")

        return "\n".join(lines)

    @contextmanager
    def trace_execution(self, pattern_name: str, task: str):
        """Context manager to trace pattern execution.

        Args:
            pattern_name: Pattern name
            task: Task being executed

        Yields:
            Tracer instance
        """
        self.trace("execution_start", pattern_name, {"task": task})
        start_time = time.time()

        try:
            yield self
        except Exception as e:
            self.trace(
                "execution_error",
                pattern_name,
                {"error": str(e), "type": type(e).__name__}
            )
            raise
        finally:
            duration = time.time() - start_time
            self.trace(
                "execution_end",
                pattern_name,
                {"duration": duration}
            )


class PatternInspector:
    """Inspect and analyze patterns."""

    @staticmethod
    def inspect_pattern(pattern: BasePattern) -> Dict[str, Any]:
        """Inspect pattern details.

        Args:
            pattern: Pattern to inspect

        Returns:
            Pattern inspection data
        """
        pattern_class = type(pattern)

        methods = []
        for name, method in inspect.getmembers(pattern_class, predicate=inspect.isfunction):
            if not name.startswith("_"):
                sig = inspect.signature(method)
                methods.append({
                    "name": name,
                    "signature": str(sig),
                    "is_async": inspect.iscoroutinefunction(method)
                })

        return {
            "class_name": pattern_class.__name__,
            "module": pattern_class.__module__,
            "config": pattern.config.model_dump() if hasattr(pattern, "config") else {},
            "methods": methods,
            "docstring": inspect.getdoc(pattern_class),
        }

    @staticmethod
    def format_inspection(inspection: Dict[str, Any]) -> str:
        """Format inspection data as readable string.

        Args:
            inspection: Inspection data

        Returns:
            Formatted output
        """
        lines = [
            f"Pattern: {inspection['class_name']}",
            f"Module: {inspection['module']}",
            "=" * 60,
        ]

        if inspection.get("docstring"):
            lines.append(f"\n{inspection['docstring']}\n")

        lines.append("\nConfiguration:")
        for key, value in inspection.get("config", {}).items():
            lines.append(f"  {key}: {value}")

        lines.append("\nMethods:")
        for method in inspection.get("methods", []):
            async_marker = " (async)" if method["is_async"] else ""
            lines.append(f"  {method['name']}{method['signature']}{async_marker}")

        return "\n".join(lines)

    @staticmethod
    def compare_results(result1: PatternResult, result2: PatternResult) -> Dict[str, Any]:
        """Compare two pattern results.

        Args:
            result1: First result
            result2: Second result

        Returns:
            Comparison data
        """
        return {
            "success_match": result1.success == result2.success,
            "result_match": result1.result == result2.result,
            "iterations_diff": result1.iterations - result2.iterations,
            "metadata_diff": {
                "added": set(result2.metadata.keys()) - set(result1.metadata.keys()),
                "removed": set(result1.metadata.keys()) - set(result2.metadata.keys()),
                "changed": {
                    k for k in result1.metadata.keys() & result2.metadata.keys()
                    if result1.metadata[k] != result2.metadata[k]
                }
            }
        }


def trace_method(tracer: DebugTracer):
    """Decorator to trace method calls.

    Args:
        tracer: Debug tracer instance

    Returns:
        Decorator function
    """
    def decorator(func: Callable) -> Callable:
        async def async_wrapper(*args, **kwargs):
            pattern_name = args[0].__class__.__name__ if args else "Unknown"
            func_name = func.__name__

            tracer.trace(
                f"method_call: {func_name}",
                pattern_name,
                {"args": str(args[1:])[:100], "kwargs": str(kwargs)[:100]}
            )

            try:
                result = await func(*args, **kwargs)
                tracer.trace(
                    f"method_return: {func_name}",
                    pattern_name,
                    {"result": str(result)[:100]}
                )
                return result
            except Exception as e:
                tracer.trace(
                    f"method_error: {func_name}",
                    pattern_name,
                    {"error": str(e)}
                )
                raise

        def sync_wrapper(*args, **kwargs):
            pattern_name = args[0].__class__.__name__ if args else "Unknown"
            func_name = func.__name__

            tracer.trace(
                f"method_call: {func_name}",
                pattern_name,
                {"args": str(args[1:])[:100], "kwargs": str(kwargs)[:100]}
            )

            try:
                result = func(*args, **kwargs)
                tracer.trace(
                    f"method_return: {func_name}",
                    pattern_name,
                    {"result": str(result)[:100]}
                )
                return result
            except Exception as e:
                tracer.trace(
                    f"method_error: {func_name}",
                    pattern_name,
                    {"error": str(e)}
                )
                raise

        if inspect.iscoroutinefunction(func):
            return async_wrapper
        else:
            return sync_wrapper

    return decorator
