"""Performance test helper utilities.

Shared functions for performance, load, and stress testing.
Separated from test modules for clarity and reusability.
"""

import json
import logging
import os
import statistics
from pathlib import Path
from typing import Dict, List, Optional

logger = logging.getLogger(__name__)

# Performance baselines file location
BASELINES_FILE = Path(__file__).parent / "performance_baselines.json"


def get_collector_endpoint() -> str:
    """Get collector endpoint from environment or default to localhost.

    Returns:
        Collector endpoint URL (defaults to http://localhost:4318)

    Environment Variables:
        MCA_TEST_COLLECTOR_ENDPOINT: Override default collector endpoint for CI/CD
    """
    return os.environ.get("MCA_TEST_COLLECTOR_ENDPOINT", "http://localhost:4318")


def load_performance_baselines() -> Dict:
    """Load performance baselines from JSON file.

    Returns:
        Dict with baseline values and thresholds
    """
    if not BASELINES_FILE.exists():
        logger.warning(f"Baselines file not found: {BASELINES_FILE}")
        return {
            "baselines": {},
            "regression_thresholds": {
                "throughput_min_percent": 80,
                "latency_max_multiplier": 2.0,
                "memory_growth_max_percent": 120,
                "cpu_increase_max_percent": 3,
            },
        }

    with open(BASELINES_FILE, "r") as f:
        return json.load(f)


def save_performance_baselines(baselines: Dict) -> None:
    """Save performance baselines to JSON file.

    Args:
        baselines: Dict with baseline values to save
    """
    with open(BASELINES_FILE, "w") as f:
        json.dump(baselines, f, indent=2)
    logger.info(f"Saved performance baselines to {BASELINES_FILE}")


def check_regression(
    metric_name: str,
    current_value: float,
    baseline_value: float,
    threshold_percent: float = 20.0,
    higher_is_better: bool = True,
) -> tuple[bool, str]:
    """Check if current metric represents a regression from baseline.

    Args:
        metric_name: Name of metric being checked
        current_value: Current measured value
        baseline_value: Baseline value to compare against
        threshold_percent: Percent change threshold for regression
        higher_is_better: True if higher values are better (throughput),
                         False if lower is better (latency)

    Returns:
        Tuple of (is_regression, message)
    """
    if baseline_value == 0:
        return False, f"{metric_name}: No baseline for comparison"

    percent_change = ((current_value - baseline_value) / baseline_value) * 100

    if higher_is_better:
        # For throughput: regression if current is significantly lower
        is_regression = percent_change < -threshold_percent
        status = "REGRESSION" if is_regression else "OK"
        message = (
            f"{metric_name}: {current_value:.2f} vs baseline {baseline_value:.2f} "
            f"({percent_change:+.1f}%) - {status}"
        )
    else:
        # For latency/memory: regression if current is significantly higher
        is_regression = percent_change > threshold_percent
        status = "REGRESSION" if is_regression else "OK"
        message = (
            f"{metric_name}: {current_value:.2f} vs baseline {baseline_value:.2f} "
            f"({percent_change:+.1f}%) - {status}"
        )

    return is_regression, message


def calculate_windowed_percentiles(
    values: List[float], window_size: int, percentiles: List[int] = [50, 95, 99]
) -> Dict[str, List[float]]:
    """Calculate percentiles for time windows.

    Args:
        values: List of measurements in time order
        window_size: Number of values per window
        percentiles: List of percentiles to calculate (0-100)

    Returns:
        Dict with percentile name -> list of windowed values
    """
    results = {f"p{p}": [] for p in percentiles}

    for i in range(0, len(values), window_size):
        window = values[i : i + window_size]
        if len(window) < 10:  # Skip windows with too few samples
            continue

        window_sorted = sorted(window)
        for p in percentiles:
            if p == 50:
                value = statistics.median(window_sorted)
            else:
                # Use quantiles for other percentiles
                n = 100
                quantile_values = statistics.quantiles(window_sorted, n=n)
                idx = min(p - 1, len(quantile_values) - 1)  # p=95 -> index 94
                value = quantile_values[idx]
            results[f"p{p}"].append(value)

    return results


def estimate_production_hours(
    iterations: int, operations_per_iteration: int, avg_production_rps: float = 5.0
) -> float:
    """Estimate production hours equivalent for accelerated test.

    Args:
        iterations: Number of test iterations
        operations_per_iteration: Operations per iteration
        avg_production_rps: Average production request rate

    Returns:
        Estimated production hours
    """
    total_operations = iterations * operations_per_iteration
    production_seconds = total_operations / avg_production_rps
    return production_seconds / 3600  # Convert to hours


def format_memory_size(bytes_value: float) -> str:
    """Format bytes as human-readable size.

    Args:
        bytes_value: Size in bytes

    Returns:
        Formatted string (e.g., "1.5 MB")
    """
    if bytes_value < 1024:
        return f"{bytes_value:.0f} B"
    elif bytes_value < 1024 * 1024:
        return f"{bytes_value / 1024:.1f} KB"
    elif bytes_value < 1024 * 1024 * 1024:
        return f"{bytes_value / 1024 / 1024:.1f} MB"
    else:
        return f"{bytes_value / 1024 / 1024 / 1024:.2f} GB"


def detect_linear_growth(values: List[float], threshold: float = 0.05) -> bool:
    """Detect if values show linear (unbounded) growth pattern.

    Args:
        values: List of measurements over time
        threshold: Growth rate threshold (0.05 = 5%)

    Returns:
        True if linear growth detected
    """
    if len(values) < 10:
        return False

    # Compare first 10% vs last 10%
    segment_size = len(values) // 10
    first_segment = values[:segment_size]
    last_segment = values[-segment_size:]

    avg_first = statistics.mean(first_segment)
    avg_last = statistics.mean(last_segment)

    if avg_first == 0:
        return False

    growth_rate = (avg_last - avg_first) / avg_first
    return growth_rate > threshold


def calculate_cpu_stability(
    cpu_samples: List[float], threshold_percent: float = 3.0
) -> tuple[bool, float]:
    """Calculate CPU stability (no gradual increase).

    Args:
        cpu_samples: List of CPU usage samples over time
        threshold_percent: Maximum acceptable increase percentage

    Returns:
        Tuple of (is_stable, increase_percent)
    """
    if len(cpu_samples) < 10:
        return True, 0.0

    # Compare first half vs second half
    midpoint = len(cpu_samples) // 2
    first_half = cpu_samples[:midpoint]
    second_half = cpu_samples[midpoint:]

    avg_first = statistics.mean(first_half)
    avg_second = statistics.mean(second_half)

    if avg_first == 0:
        return True, 0.0

    increase_percent = ((avg_second - avg_first) / avg_first) * 100
    is_stable = increase_percent < threshold_percent

    return is_stable, increase_percent


def parse_prometheus_metric(
    text: str, metric_name: str, label_filter: Optional[Dict[str, str]] = None
) -> float:
    """Parse a specific metric value from Prometheus text format.

    Args:
        text: Prometheus metrics text
        metric_name: Name of metric to parse
        label_filter: Optional label key-value pairs to filter by

    Returns:
        Parsed metric value or 0.0 if not found
    """
    import re

    # Build regex pattern
    if label_filter:
        label_pattern = ",".join([f'{k}="{v}"' for k, v in label_filter.items()])
        pattern = rf"{metric_name}\{{{label_pattern}[^}}]*\}}\s+([\d.]+)"
    else:
        pattern = rf"{metric_name}\{{[^}}]*\}}\s+([\d.]+)"

    match = re.search(pattern, text)
    if match:
        return float(match.group(1))
    return 0.0


def calculate_throughput_variance(
    actual_rps: float, target_rps: float, tolerance_percent: float = 5.0
) -> tuple[bool, float]:
    """Calculate throughput variance from target.

    Args:
        actual_rps: Actual measured RPS
        target_rps: Target RPS
        tolerance_percent: Acceptable variance percentage

    Returns:
        Tuple of (within_tolerance, variance_percent)
    """
    if target_rps == 0:
        return True, 0.0

    variance_percent = abs((actual_rps - target_rps) / target_rps) * 100
    within_tolerance = variance_percent <= tolerance_percent

    return within_tolerance, variance_percent
