#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""Location: ./tests/performance/utils/baseline_manager.py
Copyright 2026
SPDX-License-Identifier: Apache-2.0
Authors: Mihai Criveti

Baseline Manager

Utilities for saving and loading performance test baselines.
Converts test results to standardized JSON format for comparison.
"""

import argparse
import json
import re
import sys
from pathlib import Path
from typing import Dict, Optional
from datetime import datetime


class BaselineManager:
    """Manage performance test baselines"""

    @staticmethod
    def parse_hey_results(results_dir: Path) -> Dict[str, Dict]:
        """
        Parse all hey output files in results directory

        Args:
            results_dir: Directory containing hey output .txt files

        Returns:
            Dictionary mapping test names to their metrics
        """
        results = {}

        for txt_file in results_dir.glob("*.txt"):
            # Skip non-hey output files
            if "system_metrics" in txt_file.name or "docker_stats" in txt_file.name:
                continue
            if "prometheus" in txt_file.name or "logs" in txt_file.name:
                continue

            # Extract test name from filename
            # Format: {category}_{test_name}_{profile}_{timestamp}.txt
            parts = txt_file.stem.split("_")
            if len(parts) >= 2:
                test_name = "_".join(parts[:-2])  # Remove profile and timestamp
            else:
                test_name = txt_file.stem

            # Parse hey output
            metrics = BaselineManager._parse_hey_output(txt_file)
            if metrics:
                results[test_name] = metrics

        return results

    @staticmethod
    def _parse_hey_output(file_path: Path) -> Optional[Dict]:
        """Parse hey output file to extract metrics"""
        try:
            with open(file_path) as f:
                content = f.read()

            metrics = {}

            # Extract summary metrics
            if match := re.search(r"Requests/sec:\s+([\d.]+)", content):
                metrics["rps"] = float(match.group(1))

            if match := re.search(r"Average:\s+([\d.]+)\s+secs", content):
                metrics["avg"] = float(match.group(1)) * 1000  # Convert to ms

            if match := re.search(r"Slowest:\s+([\d.]+)\s+secs", content):
                metrics["max"] = float(match.group(1)) * 1000

            if match := re.search(r"Fastest:\s+([\d.]+)\s+secs", content):
                metrics["min"] = float(match.group(1)) * 1000

            # Extract percentiles
            latency_section = re.search(r"Latency distribution:(.*?)(?=\n\n|\Z)", content, re.DOTALL)
            if latency_section:
                latency_text = latency_section.group(1)

                if match := re.search(r"10%\s+in\s+([\d.]+)\s+secs", latency_text):
                    metrics["p10"] = float(match.group(1)) * 1000

                if match := re.search(r"25%\s+in\s+([\d.]+)\s+secs", latency_text):
                    metrics["p25"] = float(match.group(1)) * 1000

                if match := re.search(r"50%\s+in\s+([\d.]+)\s+secs", latency_text):
                    metrics["p50"] = float(match.group(1)) * 1000

                if match := re.search(r"75%\s+in\s+([\d.]+)\s+secs", latency_text):
                    metrics["p75"] = float(match.group(1)) * 1000

                if match := re.search(r"90%\s+in\s+([\d.]+)\s+secs", latency_text):
                    metrics["p90"] = float(match.group(1)) * 1000

                if match := re.search(r"95%\s+in\s+([\d.]+)\s+secs", latency_text):
                    metrics["p95"] = float(match.group(1)) * 1000

                if match := re.search(r"99%\s+in\s+([\d.]+)\s+secs", latency_text):
                    metrics["p99"] = float(match.group(1)) * 1000

            # Extract status codes
            status_codes = {}
            status_section = re.search(r"Status code distribution:(.*?)(?=\n\n|\Z)", content, re.DOTALL)
            if status_section:
                for line in status_section.group(1).strip().split("\n"):
                    if match := re.search(r"\[(\d+)\]\s+(\d+)\s+responses", line):
                        status_codes[int(match.group(1))] = int(match.group(2))

            metrics["status_codes"] = status_codes

            # Calculate error rate
            total_responses = sum(status_codes.values())
            error_responses = sum(count for code, count in status_codes.items() if code >= 400)
            metrics["error_rate"] = (error_responses / total_responses * 100) if total_responses > 0 else 0
            metrics["total_requests"] = total_responses

            return metrics

        except Exception as e:
            print(f"Warning: Failed to parse {file_path}: {e}", file=sys.stderr)
            return None

    @staticmethod
    def save_baseline(results_dir: Path, output_file: Path, metadata: Optional[Dict] = None) -> Dict:
        """
        Save test results as baseline

        Args:
            results_dir: Directory containing test result files
            output_file: Path to save baseline JSON
            metadata: Optional metadata to include

        Returns:
            Baseline data dictionary
        """
        # Parse results
        results = BaselineManager.parse_hey_results(results_dir)

        # Create baseline structure
        baseline = {
            "version": "1.0",
            "created": datetime.now().isoformat(),
            "metadata": metadata or {},
            "results": results,
            "summary": {
                "total_tests": len(results),
                "avg_rps": sum(r.get("rps", 0) for r in results.values()) / len(results) if results else 0,
                "avg_p95": sum(r.get("p95", 0) for r in results.values()) / len(results) if results else 0,
            },
        }

        # Save to file
        output_file.parent.mkdir(parents=True, exist_ok=True)
        with open(output_file, "w") as f:
            json.dump(baseline, f, indent=2)

        print(f"✅ Baseline saved: {output_file}")
        print(f"   Tests: {baseline['summary']['total_tests']}")
        print(f"   Average RPS: {baseline['summary']['avg_rps']:.1f}")
        print(f"   Average p95: {baseline['summary']['avg_p95']:.1f}ms")

        return baseline

    @staticmethod
    def load_baseline(file_path: Path) -> Dict:
        """Load baseline from JSON file"""
        with open(file_path) as f:
            baseline = json.load(f)

        print(f"✅ Loaded baseline: {file_path}")
        print(f"   Created: {baseline.get('created', 'Unknown')}")
        print(f"   Tests: {baseline.get('summary', {}).get('total_tests', 0)}")

        return baseline

    @staticmethod
    def list_baselines(baselines_dir: Path):
        """List all available baselines"""
        print(f"\nAvailable baselines in {baselines_dir}:")
        print("-" * 80)

        baselines = sorted(baselines_dir.glob("*.json"))
        if not baselines:
            print("No baselines found")
            return

        for baseline_file in baselines:
            try:
                with open(baseline_file) as f:
                    data = json.load(f)

                created = data.get("created", "Unknown")
                metadata = data.get("metadata", {})
                profile = metadata.get("profile", "Unknown")
                tests = data.get("summary", {}).get("total_tests", 0)

                print(f"\n{baseline_file.name}")
                print(f"  Created: {created}")
                print(f"  Profile: {profile}")
                print(f"  Tests: {tests}")

                # Show configuration if available
                config = metadata.get("config", {})
                if config:
                    print("  Config:")
                    for key, value in config.items():
                        print(f"    {key}: {value}")

            except Exception as e:
                print(f"\n{baseline_file.name} - Error: {e}")


def main():
    parser = argparse.ArgumentParser(description="Manage performance test baselines")

    subparsers = parser.add_subparsers(dest="command", help="Command to execute")

    # Save baseline
    save_parser = subparsers.add_parser("save", help="Save results as baseline")
    save_parser.add_argument("results_dir", type=Path, help="Directory containing test results")
    save_parser.add_argument("--output", type=Path, required=True, help="Output baseline file")
    save_parser.add_argument("--profile", help="Test profile name")
    save_parser.add_argument("--server-profile", help="Server profile name")
    save_parser.add_argument("--infrastructure", help="Infrastructure profile name")
    save_parser.add_argument("--metadata", type=json.loads, help="Additional metadata as JSON string")

    # Load baseline
    load_parser = subparsers.add_parser("load", help="Load and display baseline")
    load_parser.add_argument("baseline_file", type=Path, help="Baseline JSON file")

    # List baselines
    list_parser = subparsers.add_parser("list", help="List available baselines")
    list_parser.add_argument("--dir", type=Path, default=Path("baselines"), help="Baselines directory")

    args = parser.parse_args()

    try:
        if args.command == "save":
            # Build metadata
            metadata = args.metadata or {}
            if args.profile:
                metadata["profile"] = args.profile
            if args.server_profile:
                metadata["server_profile"] = args.server_profile
            if args.infrastructure:
                metadata["infrastructure"] = args.infrastructure
            metadata["timestamp"] = datetime.now().isoformat()

            BaselineManager.save_baseline(args.results_dir, args.output, metadata)

        elif args.command == "load":
            baseline = BaselineManager.load_baseline(args.baseline_file)

            # Print summary
            print("\nResults:")
            for test_name, metrics in baseline.get("results", {}).items():
                rps = metrics.get("rps", 0)
                p95 = metrics.get("p95", 0)
                print(f"  {test_name:40} {rps:8.1f} rps  {p95:6.1f}ms p95")

        elif args.command == "list":
            BaselineManager.list_baselines(args.dir)

        else:
            parser.print_help()
            return 1

        return 0

    except Exception as e:
        print(f"❌ Error: {e}", file=sys.stderr)
        import traceback

        traceback.print_exc()
        return 1


if __name__ == "__main__":
    sys.exit(main())
