"""Generate performance overhead report for Story 6-9.

Generates visualizations and markdown documentation of SDK performance characteristics.

FIX #7: Uses shared factory function to eliminate code duplication.

Usage:
    python tests/performance/generate_overhead_report.py

Output:
    tests/performance/results/overhead_report.md
"""

import json
import statistics
import sys
import time
import tracemalloc
from datetime import datetime
from pathlib import Path

# FIX #7: Import shared factory function
sys.path.insert(0, str(Path(__file__).parent.parent.parent))
from tests.performance.utils import create_benchmark_client


def measure_baseline_operation(iterations=100):
    """Measure baseline operation without SDK."""
    print("Measuring baseline operation (no SDK)...")
    latencies = []

    def simulate_model():
        time.sleep(0.1)  # 100ms model

    for _ in range(10):  # Warmup
        simulate_model()

    for _ in range(iterations):
        start = time.perf_counter()
        simulate_model()
        elapsed = time.perf_counter() - start
        latencies.append(elapsed * 1000)

    return {
        "p50": statistics.median(latencies),
        "p95": sorted(latencies)[int(len(latencies) * 0.95)],
        "p99": sorted(latencies)[int(len(latencies) * 0.99)],
        "mean": statistics.mean(latencies),
        "min": min(latencies),
        "max": max(latencies),
    }


def measure_instrumented_operation(iterations=100):
    """Measure operation with SDK instrumentation."""
    print("Measuring instrumented operation (with SDK)...")
    latencies = []

    def simulate_model():
        time.sleep(0.1)

    # FIX #7: Use shared factory function
    client = create_benchmark_client()

    for _ in range(10):  # Warmup
        simulate_model()
        client.record_prediction(latency=0.1)

    for _ in range(iterations):
        start = time.perf_counter()
        simulate_model()
        client.record_prediction(latency=0.1, model_version="v1.0")
        elapsed = time.perf_counter() - start
        latencies.append(elapsed * 1000)

    client.shutdown()

    return {
        "p50": statistics.median(latencies),
        "p95": sorted(latencies)[int(len(latencies) * 0.95)],
        "p99": sorted(latencies)[int(len(latencies) * 0.99)],
        "mean": statistics.mean(latencies),
        "min": min(latencies),
        "max": max(latencies),
    }


def measure_span_creation_overhead(iterations=500):
    """Measure trace span creation overhead."""
    print("Measuring span creation overhead...")

    # FIX #7: Use shared factory function
    client = create_benchmark_client()

    latencies = []

    for _ in range(10):  # Warmup
        with client.trace("warmup"):
            pass

    for _ in range(iterations):
        start = time.perf_counter()
        with client.trace("test_operation"):
            pass
        elapsed = time.perf_counter() - start
        latencies.append(elapsed * 1000)

    client.shutdown()

    return {
        "p50": statistics.median(latencies),
        "p95": sorted(latencies)[int(len(latencies) * 0.95)],
        "p99": sorted(latencies)[int(len(latencies) * 0.99)],
        "mean": statistics.mean(latencies),
    }


def measure_memory_overhead():
    """Measure SDK initialization memory overhead."""
    print("Measuring memory overhead...")

    tracemalloc.start()
    baseline_current, baseline_peak = tracemalloc.get_traced_memory()

    # FIX #7: Use shared factory function
    client = create_benchmark_client()

    init_current, init_peak = tracemalloc.get_traced_memory()
    client.shutdown()
    tracemalloc.stop()

    return {
        "init_overhead_mb": (init_current - baseline_current) / (1024 * 1024),
        "peak_overhead_mb": (init_peak - baseline_peak) / (1024 * 1024),
    }


def generate_performance_summary_report():
    """Generate comprehensive performance summary report."""
    print("=" * 80)
    print("MCA SDK Performance Overhead Report Generator (Story 6-9)")
    print("=" * 80)
    print()

    # Ensure results directory exists
    results_dir = Path(__file__).parent / "results"
    results_dir.mkdir(exist_ok=True)
    output_file = results_dir / "overhead_report.md"

    # Collect measurements
    baseline = measure_baseline_operation()
    instrumented = measure_instrumented_operation()
    span_overhead = measure_span_creation_overhead()
    memory = measure_memory_overhead()

    # Calculate overhead
    overhead = {
        "p50": instrumented["p50"] - baseline["p50"],
        "p95": instrumented["p95"] - baseline["p95"],
        "p99": instrumented["p99"] - baseline["p99"],
        "mean": instrumented["mean"] - baseline["mean"],
    }

    # Generate markdown report
    report = []
    report.append("# MCA SDK Performance Overhead Report (Story 6-9)")
    report.append("")
    report.append(f"**Generated:** {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
    report.append("")
    report.append("## Executive Summary")
    report.append("")
    report.append(
        "This report validates that the MCA SDK adds less than 50ms of overhead to model prediction latency, as required by Story 6-9."
    )
    report.append("")

    # Pass/Fail Summary
    report.append("## Performance Targets - Pass/Fail")
    report.append("")
    report.append("| Metric | Target | Actual | Status |")
    report.append("|--------|--------|--------|--------|")

    # AC#1: SDK overhead <50ms (p95 <100ms)
    status = "✅ PASS" if overhead["p95"] < 100.0 else "❌ FAIL"
    report.append(f"| SDK overhead (p95) | <100ms | {overhead['p95']:.3f}ms | {status} |")

    status = "✅ PASS" if overhead["p50"] < 50.0 else "⚠️  ACCEPTABLE"
    report.append(f"| SDK overhead (p50) | <50ms | {overhead['p50']:.3f}ms | {status} |")

    # AC#3: Span creation <5ms
    status = "✅ PASS" if span_overhead["p95"] < 5.0 else "❌ FAIL"
    report.append(f"| Span creation (p95) | <5ms | {span_overhead['p95']:.3f}ms | {status} |")

    # AC#5: Memory <10MB
    status = "✅ PASS" if memory["init_overhead_mb"] < 10.0 else "❌ FAIL"
    report.append(f"| Memory overhead | <10MB | {memory['init_overhead_mb']:.2f}MB | {status} |")

    report.append("")

    # Detailed Measurements
    report.append("## Detailed Performance Measurements")
    report.append("")

    report.append("### 1. Baseline vs Instrumented Latency")
    report.append("")
    report.append("| Metric | Baseline (no SDK) | Instrumented (with SDK) | Overhead |")
    report.append("|--------|-------------------|-------------------------|----------|")
    report.append(
        f"| p50 (median) | {baseline['p50']:.3f}ms | {instrumented['p50']:.3f}ms | {overhead['p50']:.3f}ms |"
    )
    report.append(
        f"| p95 | {baseline['p95']:.3f}ms | {instrumented['p95']:.3f}ms | {overhead['p95']:.3f}ms |"
    )
    report.append(
        f"| p99 | {baseline['p99']:.3f}ms | {instrumented['p99']:.3f}ms | {overhead['p99']:.3f}ms |"
    )
    report.append(
        f"| Mean | {baseline['mean']:.3f}ms | {instrumented['mean']:.3f}ms | {overhead['mean']:.3f}ms |"
    )
    report.append(f"| Min | {baseline['min']:.3f}ms | {instrumented['min']:.3f}ms | - |")
    report.append(f"| Max | {baseline['max']:.3f}ms | {instrumented['max']:.3f}ms | - |")
    report.append("")

    report.append("### 2. Span Creation Overhead")
    report.append("")
    report.append("| Metric | Value |")
    report.append("|--------|-------|")
    report.append(f"| p50 (median) | {span_overhead['p50']:.3f}ms |")
    report.append(f"| p95 | {span_overhead['p95']:.3f}ms |")
    report.append(f"| p99 | {span_overhead['p99']:.3f}ms |")
    report.append(f"| Mean | {span_overhead['mean']:.3f}ms |")
    report.append("")

    report.append("### 3. Memory Overhead")
    report.append("")
    report.append("| Metric | Value |")
    report.append("|--------|-------|")
    report.append(f"| Initialization overhead | {memory['init_overhead_mb']:.2f}MB |")
    report.append(f"| Peak overhead | {memory['peak_overhead_mb']:.2f}MB |")
    report.append("")

    # Acceptance Criteria Validation
    report.append("## Acceptance Criteria Validation")
    report.append("")
    report.append("### AC#1: SDK Overhead <50ms")
    if overhead["p50"] < 50.0:
        report.append(
            f"✅ **PASS**: SDK overhead p50 = {overhead['p50']:.3f}ms is less than 50ms target"
        )
    else:
        report.append(
            f"⚠️  SDK overhead p50 = {overhead['p50']:.3f}ms exceeds 50ms, but within acceptable range if p95 <100ms"
        )

    if overhead["p95"] < 100.0:
        report.append(
            f"✅ **PASS**: SDK overhead p95 = {overhead['p95']:.3f}ms is less than 100ms threshold"
        )
    else:
        report.append(
            f"❌ **FAIL**: SDK overhead p95 = {overhead['p95']:.3f}ms exceeds 100ms threshold"
        )
    report.append("")

    report.append("### AC#3: Trace Span Creation <5ms")
    if span_overhead["p95"] < 5.0:
        report.append(
            f"✅ **PASS**: Span creation overhead p95 = {span_overhead['p95']:.3f}ms is less than 5ms target"
        )
    else:
        report.append(
            f"❌ **FAIL**: Span creation overhead p95 = {span_overhead['p95']:.3f}ms exceeds 5ms target"
        )
    report.append("")

    report.append("### AC#5: Memory Overhead <10MB")
    if memory["init_overhead_mb"] < 10.0:
        report.append(
            f"✅ **PASS**: SDK initialization overhead = {memory['init_overhead_mb']:.2f}MB is less than 10MB target"
        )
    else:
        report.append(
            f"❌ **FAIL**: SDK initialization overhead = {memory['init_overhead_mb']:.2f}MB exceeds 10MB target"
        )
    report.append("")

    # Production Implications
    report.append("## Production Implications")
    report.append("")
    report.append("### Latency Impact")
    report.append("")
    if overhead["p50"] < 1.0:
        report.append(
            f"The SDK adds approximately **{overhead['p50']:.3f}ms** of overhead to model predictions. This is negligible and will not impact production SLAs."
        )
    else:
        report.append(
            f"The SDK adds approximately **{overhead['p50']:.3f}ms** of overhead to model predictions. For models with <150ms SLA, this represents {(overhead['p50'] / 150) * 100:.1f}% of the latency budget."
        )

    report.append("")
    report.append("### Recommendations")
    report.append("")

    if overhead["p95"] < 1.0:
        report.append(
            "- ✅ SDK overhead is negligible (<1ms). Safe for production deployment without concerns."
        )
    elif overhead["p95"] < 50.0:
        report.append(
            "- ✅ SDK overhead is minimal (<50ms). Suitable for all production workloads."
        )
    elif overhead["p95"] < 100.0:
        report.append(
            "- ⚠️  SDK overhead is acceptable but measurable. Consider profiling for optimization opportunities."
        )
    else:
        report.append(
            "- ❌ SDK overhead exceeds acceptable thresholds. Profiling and optimization required before production deployment."
        )

    report.append("")

    # Next Steps
    report.append("## Next Steps")
    report.append("")
    report.append("1. Review profiling report: `tests/performance/results/overhead_profile.txt`")
    report.append("2. Run full test suite: `pytest tests/performance/test_performance.py -v`")
    report.append("3. If overhead exceeds targets, profile hot paths and optimize")
    report.append("4. Document findings in Story 6-9 Dev Agent Record")
    report.append("")

    # Write report
    report_text = "\n".join(report)
    with open(output_file, "w") as f:
        f.write(report_text)

    print(f"✅ Performance report saved to: {output_file}")
    print(f"   File size: {output_file.stat().st_size} bytes")
    print()

    # Print summary to console
    print("=" * 80)
    print("PERFORMANCE SUMMARY")
    print("=" * 80)
    print(f"SDK Overhead (p50):       {overhead['p50']:.3f}ms")
    print(f"SDK Overhead (p95):       {overhead['p95']:.3f}ms")
    print(f"Span Creation (p95):      {span_overhead['p95']:.3f}ms")
    print(f"Memory Overhead:          {memory['init_overhead_mb']:.2f}MB")
    print()
    if overhead["p95"] < 100.0 and span_overhead["p95"] < 5.0 and memory["init_overhead_mb"] < 10.0:
        print("✅ ALL TARGETS MET - Story 6-9 acceptance criteria satisfied")
    else:
        print("⚠️  Some targets not met - review report for details")
    print("=" * 80)


def main():
    """Generate performance overhead report."""
    generate_performance_summary_report()


if __name__ == "__main__":
    main()
