#!/usr/bin/env python3
# Copyright (c) 2026 PULSE Contributors. Proprietary. All rights reserved.
# See LICENSE file for details.
"""
Performance Profiler: Track tool execution times and detect bottlenecks.

Provides a @profile decorator for function timing and a reporting tool
that identifies the slowest operations.

Usage:
    # In code:
    from pulse.admin.benchmarks.performance_profiler import profile
    @profile
    def expensive_function():
        ...

    # CLI report:
    python performance_profiler.py --report
    python performance_profiler.py --report --top 20

Dependencies: Python stdlib only (Law 4)
"""

import functools
import json
import sys
import time
from datetime import datetime, timezone
from pathlib import Path

SCRIPT_DIR = Path(__file__).resolve().parent
ROOT_DIR = SCRIPT_DIR.parent.parent.parent
STATE_DIR = ROOT_DIR / "state"
PROFILER_FILE = STATE_DIR / "profiler_data.json"

# Max entries to keep (prevents unbounded growth)
MAX_ENTRIES = 1000


def _load_data() -> list[dict]:
    """Load profiler data."""
    if not PROFILER_FILE.exists():
        return []
    try:
        data = json.loads(PROFILER_FILE.read_text(encoding="utf-8"))
        return data if isinstance(data, list) else []
    except (json.JSONDecodeError, Exception):
        return []


def _save_entry(entry: dict) -> None:
    """Append a profiler entry (thread-safe via atomic write)."""
    try:
        data = _load_data()
        data.append(entry)
        # Keep only last MAX_ENTRIES
        if len(data) > MAX_ENTRIES:
            data = data[-MAX_ENTRIES:]
        PROFILER_FILE.parent.mkdir(parents=True, exist_ok=True)
        PROFILER_FILE.write_text(json.dumps(data, indent=1), encoding="utf-8")
    except Exception:
        pass  # Never block the profiled function


def profile(func):
    """Decorator to time function execution and log results."""
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        start = time.perf_counter()
        error = None
        try:
            result = func(*args, **kwargs)
            return result
        except Exception as e:
            error = str(e)
            raise
        finally:
            elapsed = time.perf_counter() - start
            entry = {
                "function": f"{func.__module__}.{func.__qualname__}",
                "elapsed_s": round(elapsed, 4),
                "timestamp": datetime.now(timezone.utc).isoformat(),
                "error": error,
            }
            _save_entry(entry)
    return wrapper


def generate_report(top_n: int = 10) -> str:
    """Generate performance report from profiler data."""
    data = _load_data()
    if not data:
        return "No profiler data found. Add @profile decorator to functions."

    # Aggregate by function
    stats: dict[str, dict] = {}
    for entry in data:
        func = entry.get("function", "unknown")
        elapsed = entry.get("elapsed_s", 0)
        if func not in stats:
            stats[func] = {"calls": 0, "total_s": 0, "max_s": 0, "errors": 0}
        stats[func]["calls"] += 1
        stats[func]["total_s"] += elapsed
        stats[func]["max_s"] = max(stats[func]["max_s"], elapsed)
        if entry.get("error"):
            stats[func]["errors"] += 1

    # Sort by total time
    sorted_stats = sorted(stats.items(), key=lambda x: -x[1]["total_s"])

    lines = [
        f"Performance Report ({len(data)} samples)",
        f"{'='*60}",
        "",
        f"Top {top_n} Slowest Operations:",
        "",
    ]

    for i, (func, s) in enumerate(sorted_stats[:top_n], 1):
        avg = s["total_s"] / s["calls"] if s["calls"] else 0
        error_str = f" ({s['errors']} errors)" if s["errors"] else ""
        lines.append(
            f"  {i}. {func}"
            f"\n     Avg: {avg:.3f}s | Max: {s['max_s']:.3f}s | "
            f"Calls: {s['calls']}{error_str}"
        )

    # Summary
    total_time = sum(s["total_s"] for s in stats.values())
    total_calls = sum(s["calls"] for s in stats.values())
    lines.append("")
    lines.append(f"Total: {total_calls} calls, {total_time:.2f}s total execution time")

    # Regression detection
    lines.append("")
    lines.append("Potential Bottlenecks (avg > 1s):")
    bottlenecks = [(f, s) for f, s in stats.items()
                   if s["total_s"] / max(s["calls"], 1) > 1.0]
    if bottlenecks:
        for func, s in bottlenecks:
            avg = s["total_s"] / s["calls"]
            lines.append(f"  ! {func}: {avg:.2f}s avg")
    else:
        lines.append("  None detected.")

    return "\n".join(lines)


if __name__ == "__main__":
    if "--report" in sys.argv:
        top_n = 10
        if "--top" in sys.argv:
            idx = sys.argv.index("--top")
            if idx + 1 < len(sys.argv):
                top_n = int(sys.argv[idx + 1])
        print(generate_report(top_n))
    elif "--clear" in sys.argv:
        if PROFILER_FILE.exists():
            PROFILER_FILE.unlink()
            print("[PROFILER] Data cleared.")
        else:
            print("[PROFILER] No data to clear.")
    else:
        print("Usage:")
        print("  python performance_profiler.py --report [--top N]")
        print("  python performance_profiler.py --clear")
        print()
        print("In code:")
        print("  from performance_profiler import profile")
        print("  @profile")
        print("  def my_function(): ...")
