"""Pytest integration for Story 6-8 load testing.

This module integrates the load test scenarios, collector monitoring,
and completeness validation into the pytest test suite.
"""

import logging
import pytest

from .load_test_scenarios import (
    scenario_constant_load,
    scenario_ramp_up,
    scenario_burst_load,
    scenario_spike_recovery,
)
from .collector_monitor import CollectorMonitor
from .telemetry_completeness import TelemetryCompletenessValidator
from .load_test_report import LoadTestReporter

# Mark all tests as performance/slow and require long timeout
pytestmark = [pytest.mark.performance, pytest.mark.slow]

logger = logging.getLogger(__name__)


@pytest.mark.timeout(300)  # 5 minutes for all scenarios
def test_load_scenarios_1000_rps():
    """Run all 4 load scenarios and validate acceptance criteria.

    This test implements Story 6-8 acceptance criteria:
    - AC1: 1000 predictions/sec sustained for 60s without drops
    - AC2: SDK p95 latency <100ms per prediction
    - AC3: Collector processes efficiently without memory leaks
    - AC4: Telemetry completeness >=99%
    """
    logger.info("Starting comprehensive load test (4 scenarios)")

    results = []
    collector_reports = []

    # Run all 4 scenarios with collector monitoring
    scenarios = [
        ("Constant Load", scenario_constant_load),
        ("Ramp Up", scenario_ramp_up),
        ("Burst Load", scenario_burst_load),
        ("Spike Recovery", scenario_spike_recovery),
    ]

    for scenario_name, scenario_func in scenarios:
        logger.info(f"\n{'=' * 60}")
        logger.info(f"Running scenario: {scenario_name}")
        logger.info(f"{'=' * 60}\n")

        # Run with collector monitoring
        with CollectorMonitor() as monitor:
            result = scenario_func()
            results.append(result)

        # Get collector report
        collector_report = monitor.get_report()
        collector_reports.append(collector_report)
        monitor.print_report(collector_report)

    # Generate comprehensive report with all collector reports
    reporter = LoadTestReporter()
    output_files = reporter.generate_full_report(
        results=results,
        collector_reports=collector_reports,  # Pass ALL collector reports
    )

    # Assert acceptance criteria
    logger.info(f"\n{'=' * 60}")
    logger.info("VALIDATING ACCEPTANCE CRITERIA")
    logger.info(f"{'=' * 60}\n")

    # AC1: At least one scenario must sustain 1000 RPS for 60s (strict requirement)
    ac1_passed = any(r["actual_rps"] >= 1000 and r["duration_seconds"] >= 60 for r in results)
    logger.info(f"AC1 (1000 RPS for 60s): {'PASS' if ac1_passed else 'FAIL'}")
    assert ac1_passed, "Failed to sustain 1000 RPS for 60 seconds (Story 6.8 requirement)"

    # AC2: All scenarios must have p95 latency <100ms
    for result in results:
        p95 = result["latency"]["p95_ms"]
        logger.info(
            f"AC2 ({result['scenario']} p95 latency): {p95:.2f}ms - {'PASS' if p95 < 100 else 'FAIL'}"
        )
        assert p95 < 100, f"p95 latency exceeds 100ms in {result['scenario']}: {p95:.3f}ms"

    # AC3: Collector memory must be stable (<10% growth)
    for idx, report in enumerate(collector_reports):
        stable = report["memory"]["stable"]
        growth = report["memory"]["growth_percent"]
        logger.info(
            f"AC3 (Scenario {idx + 1} collector memory): {growth:+.1f}% - {'PASS' if stable else 'FAIL'}"
        )
        assert stable, f"Collector memory not stable in scenario {idx + 1}: {growth:+.1f}% growth"

    # AC4: No drops in any scenario
    for result in results:
        drops = result["dropped_count"]
        logger.info(
            f"AC4 ({result['scenario']} drops): {drops} - {'PASS' if drops == 0 else 'FAIL'}"
        )
        assert drops == 0, f"Telemetry drops detected in {result['scenario']}: {drops} items"

    logger.info(f"\n{'=' * 60}")
    logger.info("ALL ACCEPTANCE CRITERIA PASSED ✅")
    logger.info(f"{'=' * 60}\n")

    logger.info(f"Full report available at: {output_files['markdown_report']}")


@pytest.mark.timeout(180)  # 3 minutes
def test_telemetry_completeness_under_load():
    """Validate 99%+ telemetry completeness during sustained load.

    This test specifically validates AC4: Telemetry completeness >=99%
    """
    from mca_sdk import MCAClient
    from .performance_helpers import get_collector_endpoint

    logger.info("Testing telemetry completeness under load")

    # Clear export files for fresh test
    validator = TelemetryCompletenessValidator()
    validator.clear_export_files()

    # Run constant load test
    collector_endpoint = get_collector_endpoint()
    client = MCAClient(
        service_name="completeness_test",
        model_id="completeness_model",
        team_name="performance_team",
        model_category="internal",
        model_type="regression",
        collector_endpoint=collector_endpoint,
        buffering_enabled=True,
        max_queue_size=10000,
    )

    # Send 5000 predictions at ~1000 RPS (5 seconds)
    import time

    target_rps = 1000
    num_requests = 5000
    interval = 1.0 / target_rps

    logger.info(f"Sending {num_requests} predictions at {target_rps} RPS...")

    for i in range(num_requests):
        loop_start = time.perf_counter()
        client.record_prediction(latency=0.001)

        # Maintain RPS
        loop_elapsed = time.perf_counter() - loop_start
        sleep_time = interval - loop_elapsed
        if sleep_time > 0:
            time.sleep(sleep_time)

    # Get sent count from buffer stats
    buffer_stats = client.buffer_stats()
    sent_counts = {}
    if "per_signal_stats" in buffer_stats:
        for signal_name, signal_stats in buffer_stats["per_signal_stats"].items():
            sent_counts[signal_name] = signal_stats.get("items_enqueued", 0)

    # Shutdown client to flush
    client.shutdown()

    # Wait for collector to process
    time.sleep(5)

    # Validate completeness
    logger.info("Validating telemetry completeness...")

    for signal_name, sent_count in sent_counts.items():
        if sent_count > 0:
            result = validator.validate_completeness(
                sdk_sent_count=sent_count,
                threshold_percent=99.0,
                signal_type=signal_name,
                wait_for_flush=True,
            )

            validator.print_validation_result(result)

            assert result["passed"], (
                f"Telemetry completeness below 99% for {signal_name}: "
                f"{result['completeness_percent']:.2f}%"
            )

    logger.info("Telemetry completeness validation PASSED ✅")


@pytest.mark.timeout(120)  # 2 minutes
def test_constant_load_60s_1000rps():
    """Test constant load scenario (AC1 validation).

    Validates:
    - 1000 RPS sustained for 60 seconds
    - No telemetry drops
    - p95 latency <100ms
    """
    logger.info("Running constant load scenario (60s at 1000 RPS)")

    with CollectorMonitor() as monitor:
        result = scenario_constant_load(duration_seconds=60, target_rps=1000)

    collector_report = monitor.get_report()
    monitor.print_report(collector_report)

    # Assertions - strict 1000 RPS requirement
    assert (
        result["actual_rps"] >= 1000
    ), f"Failed to maintain 1000 RPS: {result['actual_rps']:.0f} < 1000 (Story 6.8 requirement)"

    assert (
        result["latency"]["p95_ms"] < 100
    ), f"p95 latency exceeds 100ms: {result['latency']['p95_ms']:.3f}ms"

    assert (
        result["dropped_count"] == 0
    ), f"Telemetry drops detected: {result['dropped_count']} items"

    assert collector_report["memory"][
        "stable"
    ], f"Collector memory not stable: {collector_report['memory']['growth_percent']:+.1f}% growth"

    logger.info("Constant load test PASSED ✅")


@pytest.mark.timeout(120)  # 2 minutes
def test_burst_load_scenario():
    """Test burst load scenario (stress test).

    Validates system behavior under 2x load spike:
    - Recovers gracefully after burst
    - No drops during burst
    - Latency remains acceptable
    """
    logger.info("Running burst load scenario")

    with CollectorMonitor() as monitor:
        result = scenario_burst_load()

    collector_report = monitor.get_report()
    monitor.print_report(collector_report)

    # Assertions
    assert (
        result["dropped_count"] == 0
    ), f"Telemetry drops detected during burst: {result['dropped_count']} items"

    assert (
        result["latency"]["p95_ms"] < 100
    ), f"p95 latency exceeds 100ms during burst: {result['latency']['p95_ms']:.3f}ms"

    logger.info("Burst load test PASSED ✅")


@pytest.mark.timeout(90)  # 1.5 minutes
def test_ramp_up_scenario():
    """Test ramp-up scenario (gradual load increase).

    Validates:
    - System handles gradual RPS increase
    - Performance remains stable throughout ramp
    """
    logger.info("Running ramp-up scenario")

    with CollectorMonitor() as monitor:
        result = scenario_ramp_up()

    collector_report = monitor.get_report()
    monitor.print_report(collector_report)

    # Assertions
    assert (
        result["dropped_count"] == 0
    ), f"Telemetry drops detected during ramp-up: {result['dropped_count']} items"

    assert (
        result["latency"]["p95_ms"] < 100
    ), f"p95 latency exceeds 100ms during ramp-up: {result['latency']['p95_ms']:.3f}ms"

    logger.info("Ramp-up test PASSED ✅")


if __name__ == "__main__":
    # Run tests with pytest
    pytest.main([__file__, "-v", "-s", "--log-cli-level=INFO"])
