# Performance profiling test for MCA SDK
# AC: SDK overhead <50ms p50, p95 <100ms at 1000 req/s

import time
import statistics
import pytest
from mca_sdk import MCAClient

# Mark as performance test for CI integration
pytestmark = pytest.mark.performance


def run_performance_test(duration_seconds=10, target_rps=1000):
    """Run performance test at target requests per second."""
    client = MCAClient(
        service_name="perf_test",
        model_id="test_model",
        team_name="test_team",
        collector_endpoint="http://localhost:4318",
        buffering_enabled=True,
    )

    latencies = []
    interval = 1.0 / target_rps  # Time between requests
    start_time = time.time()
    request_count = 0

    print(f"Starting performance test: {target_rps} req/s for {duration_seconds}s")
    print(f"Target interval: {interval * 1000:.3f}ms between requests")

    while time.time() - start_time < duration_seconds:
        req_start = time.perf_counter()

        # Record prediction (this is what we're measuring overhead for)
        client.record_prediction(latency=0.001)  # 1ms model latency

        req_end = time.perf_counter()
        sdk_overhead = (req_end - req_start) * 1000  # Convert to ms
        latencies.append(sdk_overhead)

        request_count += 1

        # Busy-wait to maintain target RPS.
        # time.sleep has ~15ms resolution on Windows, making it unable to
        # sustain 1000 RPS at 1ms intervals (~640 RPS actual).
        # perf_counter busy-wait achieves sub-ms scheduling accuracy.
        target_tick = req_start + interval
        while time.perf_counter() < target_tick:
            pass

    elapsed_time = time.time() - start_time
    actual_rps = request_count / elapsed_time

    # Calculate percentiles
    latencies.sort()
    p50 = statistics.median(latencies)
    p95 = latencies[int(len(latencies) * 0.95)]
    p99 = latencies[int(len(latencies) * 0.99)]
    avg = statistics.mean(latencies)

    print("\n=== Performance Test Results ===")
    print(f"Duration: {elapsed_time:.2f}s")
    print(f"Requests: {request_count}")
    print(f"Actual RPS: {actual_rps:.1f}")
    print("\nSDK Overhead (milliseconds):")
    print(f"  Average: {avg:.2f}ms")
    print(f"  p50:     {p50:.2f}ms")
    print(f"  p95:     {p95:.2f}ms")
    print(f"  p99:     {p99:.2f}ms")
    print("\nAcceptance Criteria:")
    print(f"  p50 < 50ms:  {'PASS' if p50 < 50 else 'FAIL'} ({p50:.2f}ms)")
    print(f"  p95 < 100ms: {'PASS' if p95 < 100 else 'FAIL'} ({p95:.2f}ms)")

    return {
        "p50": p50,
        "p95": p95,
        "p99": p99,
        "avg": avg,
        "actual_rps": actual_rps,
        "pass": p50 < 50 and p95 < 100,
    }


@pytest.mark.performance
@pytest.mark.slow
def test_sdk_performance_under_load():
    """Pytest integration: Validate SDK overhead <50ms p50, <100ms p95.

    Duration: 10s provides 10,000 samples for statistical confidence in p95/p99.
    """
    results = run_performance_test(duration_seconds=10, target_rps=1000)
    assert results["pass"], (
        f"Performance test failed: p50={results['p50']:.2f}ms (target<50ms), "
        f"p95={results['p95']:.2f}ms (target<100ms)"
    )
    assert results["p50"] < 50, f"p50 latency {results['p50']:.2f}ms exceeds 50ms target"
    assert results["p95"] < 100, f"p95 latency {results['p95']:.2f}ms exceeds 100ms target"


if __name__ == "__main__":
    results = run_performance_test(duration_seconds=10, target_rps=1000)
    exit(0 if results["pass"] else 1)
