package ai.nektron.grownet.debug;

import java.util.Arrays;

public final class BenchmarkStats {
    private final int count;
    private final long totalLatencyNanos;
    private final long totalElapsedNanos;
    private final double minUs;
    private final double p50Us;
    private final double p95Us;
    private final double p99Us;
    private final double maxUs;
    private final double meanUs;
    private final double totalElapsedMs;
    private final double throughputPerSec;

    private BenchmarkStats(
            int count,
            long totalLatencyNanos,
            long totalElapsedNanos,
            double minUs,
            double p50Us,
            double p95Us,
            double p99Us,
            double maxUs,
            double meanUs,
            double totalElapsedMs,
            double throughputPerSec) {
        this.count = count;
        this.totalLatencyNanos = totalLatencyNanos;
        this.totalElapsedNanos = totalElapsedNanos;
        this.minUs = minUs;
        this.p50Us = p50Us;
        this.p95Us = p95Us;
        this.p99Us = p99Us;
        this.maxUs = maxUs;
        this.meanUs = meanUs;
        this.totalElapsedMs = totalElapsedMs;
        this.throughputPerSec = throughputPerSec;
    }

    public static BenchmarkStats fromIterationNanos(long[] iterationDurationsNanos, long totalElapsedNanos) {
        if (iterationDurationsNanos == null || iterationDurationsNanos.length == 0) {
            throw new IllegalArgumentException("Benchmark iterations must not be empty.");
        }
        long[] sortedDurations = Arrays.copyOf(iterationDurationsNanos, iterationDurationsNanos.length);
        Arrays.sort(sortedDurations);

        long totalLatencyNanos = 0L;
        for (long durationNanos : iterationDurationsNanos) {
            totalLatencyNanos += Math.max(0L, durationNanos);
        }

        int count = iterationDurationsNanos.length;
        double totalElapsedMs = nanosToMillis(totalElapsedNanos);
        double throughputPerSec = totalElapsedNanos <= 0L ? 0.0 : count / (totalElapsedNanos / 1_000_000_000.0);
        return new BenchmarkStats(
                count,
                totalLatencyNanos,
                totalElapsedNanos,
                nanosToMicros(sortedDurations[0]),
                percentileMicros(sortedDurations, 0.50),
                percentileMicros(sortedDurations, 0.95),
                percentileMicros(sortedDurations, 0.99),
                nanosToMicros(sortedDurations[sortedDurations.length - 1]),
                nanosToMicros(totalLatencyNanos) / count,
                totalElapsedMs,
                throughputPerSec
        );
    }

    public int getCount() {
        return count;
    }

    long getTotalLatencyNanos() {
        return totalLatencyNanos;
    }

    long getTotalElapsedNanos() {
        return totalElapsedNanos;
    }

    public double getMinUs() {
        return minUs;
    }

    public double getP50Us() {
        return p50Us;
    }

    public double getP95Us() {
        return p95Us;
    }

    public double getP99Us() {
        return p99Us;
    }

    public double getMaxUs() {
        return maxUs;
    }

    public double getMeanUs() {
        return meanUs;
    }

    public double getTotalElapsedMs() {
        return totalElapsedMs;
    }

    public double getThroughputPerSec() {
        return throughputPerSec;
    }

    private static double percentileMicros(long[] sortedDurationsNanos, double percentileValue) {
        if (sortedDurationsNanos.length == 1) {
            return nanosToMicros(sortedDurationsNanos[0]);
        }
        int percentileRank = (int) Math.ceil(percentileValue * sortedDurationsNanos.length);
        int percentileIndex = Math.max(0, Math.min(sortedDurationsNanos.length - 1, percentileRank - 1));
        return nanosToMicros(sortedDurationsNanos[percentileIndex]);
    }

    private static double nanosToMicros(long durationNanos) {
        return durationNanos / 1_000.0;
    }

    private static double nanosToMillis(long durationNanos) {
        return durationNanos / 1_000_000.0;
    }
}
