//! Benchmark execution runtime
//!
//! This module provides the execution engine that runs registered benchmarks
//! and collects timing data.

use crate::registry::{find_benchmark, list_benchmark_names};
use crate::timing::BenchSpec;
use crate::types::{BenchError, RunnerReport};

/// Runs a benchmark by name
///
/// Looks up the benchmark function in the registry and executes it with the
/// given specification. The benchmark's runner handles all timing, including
/// any setup/teardown logic.
///
/// # Arguments
///
/// * `spec` - Benchmark specification including function name, iterations, and warmup
///
/// # Returns
///
/// * `Ok(BenchReport)` - Report containing timing samples
/// * `Err(BenchError)` - If the function is not found or execution fails
///
/// # Example
///
/// ```no_run
/// use mobench_sdk::{BenchSpec, run_benchmark};
///
/// fn main() -> Result<(), Box<dyn std::error::Error>> {
///     let spec = BenchSpec::new("my_benchmark", 100, 10)?;
///
///     let report = run_benchmark(spec)?;
///     println!("Mean: {} ns", report.mean_ns());
///     Ok(())
/// }
/// ```
pub fn run_benchmark(spec: BenchSpec) -> Result<RunnerReport, BenchError> {
    // Find the benchmark function in the registry
    let bench_fn = find_benchmark(&spec.name).ok_or_else(|| {
        let available = list_benchmark_names()
            .into_iter()
            .map(String::from)
            .collect();
        BenchError::UnknownFunction(spec.name.clone(), available)
    })?;

    // Call the runner directly - it handles setup/teardown and timing internally
    let report = (bench_fn.runner)(spec)?;

    Ok(report)
}

/// Builder for constructing and running benchmarks
///
/// Provides a fluent interface for configuring benchmark parameters.
///
/// # Example
///
/// ```no_run
/// use mobench_sdk::BenchmarkBuilder;
///
/// fn main() -> Result<(), Box<dyn std::error::Error>> {
///     let report = BenchmarkBuilder::new("my_benchmark")
///         .iterations(100)
///         .warmup(10)
///         .run()?;
///
///     println!("Median: {} ns", report.median_ns());
///     Ok(())
/// }
/// ```
#[derive(Debug, Clone)]
pub struct BenchmarkBuilder {
    function: String,
    iterations: u32,
    warmup: u32,
}

impl BenchmarkBuilder {
    /// Creates a new benchmark builder
    ///
    /// # Arguments
    ///
    /// * `function` - Name of the benchmark function to run
    pub fn new(function: impl Into<String>) -> Self {
        Self {
            function: function.into(),
            iterations: 100, // Default
            warmup: 10,      // Default
        }
    }

    /// Sets the number of iterations
    ///
    /// # Arguments
    ///
    /// * `n` - Number of times to run the benchmark (after warmup)
    pub fn iterations(mut self, n: u32) -> Self {
        self.iterations = n;
        self
    }

    /// Sets the number of warmup iterations
    ///
    /// # Arguments
    ///
    /// * `n` - Number of warmup runs (not measured)
    pub fn warmup(mut self, n: u32) -> Self {
        self.warmup = n;
        self
    }

    /// Runs the benchmark and returns the report
    ///
    /// # Returns
    ///
    /// * `Ok(BenchReport)` - Report containing timing samples
    /// * `Err(BenchError)` - If the function is not found or execution fails
    pub fn run(self) -> Result<RunnerReport, BenchError> {
        let spec = BenchSpec {
            name: self.function,
            iterations: self.iterations,
            warmup: self.warmup,
        };

        run_benchmark(spec)
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_builder_defaults() {
        let builder = BenchmarkBuilder::new("test_fn");
        assert_eq!(builder.iterations, 100);
        assert_eq!(builder.warmup, 10);
    }

    #[test]
    fn test_builder_customization() {
        let builder = BenchmarkBuilder::new("test_fn").iterations(50).warmup(5);
        assert_eq!(builder.iterations, 50);
        assert_eq!(builder.warmup, 5);
    }
}
