#!/usr/bin/python3
import argparse
import re
from dataclasses import dataclass
from typing import Dict

parser = argparse.ArgumentParser()
parser.add_argument("-b", "--base-results", dest="base_results_file_path", type=str, help="Path to base results output file", required=True)
parser.add_argument("-t", "--test-results", dest="test_results_file_path", type=str, help="Path to test results output file", required=True)

args = parser.parse_args()

@dataclass
class Benchmark:
    test_name: str
    duration_in_ns: int
    plus_or_minus_in_ns: int

def parse_benchmarks_results(file_path: str) -> Dict[str, Benchmark]:
    benchmarks = {}

    with open(file_path, "r") as file:
        for line in file.readlines():
            match = re.match(r"test (.*) \.\.\. bench: +([\d,]+) ns/iter \(\+/- ([\d,]+)\)", line.strip())
            if match:
                benchmark = Benchmark(
                    test_name=match.group(1),
                    duration_in_ns=int(match.group(2).replace(',', '')),
                    plus_or_minus_in_ns=int(match.group(3).replace(',', ''))
                )

                benchmarks[benchmark.test_name] = benchmark

    return benchmarks

base_results = parse_benchmarks_results(args.base_results_file_path)
test_results = parse_benchmarks_results(args.test_results_file_path)

base_test_names = set(base_results.keys())
test_test_names = set(test_results.keys())

removed_from_base = base_test_names - test_test_names
added_by_test = test_test_names - base_test_names
common = base_test_names & test_test_names

print("# Performance Benchmark Report")

if common:
    print(f"| {'Benchmark name':38} | {'Baseline (μs)':>13} | {'Test/PR (μs)':>13} | {'Delta (μs)':>13} | {'Delta %':15} |")
    print(f"| {'-' * 38} | {'-' * 13} | {'-' * 13} | {'-' * 13} | {'-' * 15} |")
    for name in sorted(common):
        # Retrieve base data
        base_duration = base_results[name].duration_in_ns / 1000.0
        base_plus_or_minus = base_results[name].plus_or_minus_in_ns / 1000.0
        base_plus_or_minus_percentage = (100.0 * base_plus_or_minus) / base_duration

        # Retrieve test data
        test_duration = test_results[name].duration_in_ns / 1000.0
        test_plus_or_minus = test_results[name].plus_or_minus_in_ns / 1000.0
        test_plus_or_minus_percentage = (100.0 * test_plus_or_minus) / test_duration

        # Compute delta
        delta_duration = test_duration - base_duration
        delta_percentage = (100.0 * delta_duration) / base_duration
        abs_delta_percentage = abs(delta_percentage)
        max_plus_or_minus_percentage = max(base_plus_or_minus_percentage, test_plus_or_minus_percentage)

        # Format
        delta_str = f"{delta_duration:8.2f}"

        if abs_delta_percentage > max_plus_or_minus_percentage:
            if delta_percentage < 0:
                delta_prefix = "🟢 "
            elif delta_percentage > 0:
                delta_prefix = "🟠 +"
            else:
                delta_prefix = "⚪  "

            delta_percentage_str = f"{delta_prefix}{delta_percentage:.2f}%"
        else:
            delta_percentage_str = "⚪  Unchanged"

        print(f"| `{name:36}` | `{base_duration:8.2f} μs` | `{test_duration:8.2f} μs` | `{delta_str:>8} μs` | `{delta_percentage_str:12}` |")

if removed_from_base:
    print()
    print("Benchmarks removed:")
    for name in removed_from_base:
        print(f"  - {name}")

if added_by_test:
    print()
    print("Benchmarks added:")
    for name in added_by_test:
        print(f"  - {name}")
