// Copyright 2024 RustFS Team
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! I/O scheduler metrics recording functions.
//!
//! This module provides metrics recording for I/O scheduler operations.

/// Record I/O scheduler decision.
///
/// # Arguments
///
/// * `buffer_size` - Buffer size in bytes
/// * `load_level` - Load level string
/// * `strategy` - Strategy type string
#[inline(always)]
pub fn record_io_scheduler_decision(buffer_size: usize, load_level: &str, strategy: &str) {
    use metrics::{counter, gauge, histogram};

    counter!("rustfs.io.scheduler.decisions").increment(1);
    gauge!("rustfs.io.scheduler.buffer_size").set(buffer_size as f64);
    counter!("rustfs.io.scheduler.load", "level" => load_level.to_string()).increment(1);
    counter!("rustfs.io.scheduler.strategy", "type" => strategy.to_string()).increment(1);
    histogram!("rustfs.io.scheduler.buffer_size.histogram").record(buffer_size as f64);
}

/// Record I/O priority decision.
///
/// # Arguments
///
/// * `priority` - Priority level string
/// * `size` - Request size in bytes
#[inline(always)]
pub fn record_io_priority_decision(priority: &str, size: usize) {
    use metrics::{counter, histogram};

    counter!("rustfs.io.priority.decisions").increment(1);
    counter!("rustfs.io.priority.by_level", "priority" => priority.to_string()).increment(1);
    histogram!("rustfs.io.priority.request_size").record(size as f64);
}

/// Record load level change.
///
/// # Arguments
///
/// * `from` - Previous load level
/// * `to` - New load level
#[inline(always)]
pub fn record_load_level_change(from: &str, to: &str) {
    use metrics::counter;
    counter!("rustfs.io.load.changes", "from" => from.to_string(), "to" => to.to_string()).increment(1);
}

/// Record bandwidth observation.
///
/// # Arguments
///
/// * `bps` - Bytes per second
#[inline(always)]
pub fn record_bandwidth_observation(bps: u64) {
    use metrics::{gauge, histogram};
    gauge!("rustfs.io.bandwidth.bps").set(bps as f64);
    histogram!("rustfs.io.bandwidth.histogram").record(bps as f64);
}

/// Record buffer size adjustment.
///
/// # Arguments
///
/// * `original` - Original buffer size
/// * `adjusted` - Adjusted buffer size
/// * `reason` - Reason for adjustment
#[inline(always)]
pub fn record_buffer_size_adjustment(original: usize, adjusted: usize, reason: &str) {
    use metrics::{counter, gauge};
    counter!("rustfs.io.buffer.adjustments", "reason" => reason.to_string()).increment(1);
    gauge!("rustfs.io.buffer.original").set(original as f64);
    gauge!("rustfs.io.buffer.adjusted").set(adjusted as f64);
}

/// Record queue operation.
///
/// # Arguments
///
/// * `operation` - Operation type ("enqueue" or "dequeue")
/// * `priority` - Priority level
/// * `queue_size` - Current queue size
#[inline(always)]
pub fn record_queue_operation(operation: &str, priority: &str, queue_size: usize) {
    use metrics::{counter, gauge};
    counter!("rustfs.io.queue.operations", "operation" => operation.to_string(), "priority" => priority.to_string()).increment(1);
    gauge!("rustfs.io.queue.size", "priority" => priority.to_string()).set(queue_size as f64);
}

/// Record starvation event.
///
/// # Arguments
///
/// * `priority` - Starved priority level
#[inline(always)]
pub fn record_starvation_event(priority: &str) {
    use metrics::counter;
    counter!("rustfs.io.starvation.events", "priority" => priority.to_string()).increment(1);
}

/// I/O scheduler statistics.
#[derive(Debug, Clone, Default)]
pub struct IoSchedulerStats {
    /// Number of scheduler decisions.
    pub decisions: u64,
    /// Number of priority decisions.
    pub priority_decisions: u64,
    /// Number of load level changes.
    pub load_changes: u64,
    /// Number of buffer adjustments.
    pub buffer_adjustments: u64,
    /// Number of starvation events.
    pub starvation_events: u64,
}

impl IoSchedulerStats {
    /// Create new statistics.
    pub fn new() -> Self {
        Self::default()
    }

    /// Record a scheduler decision.
    pub fn record_decision(&mut self) {
        self.decisions += 1;
    }

    /// Record a priority decision.
    pub fn record_priority_decision(&mut self) {
        self.priority_decisions += 1;
    }

    /// Record a load change.
    pub fn record_load_change(&mut self) {
        self.load_changes += 1;
    }

    /// Record a buffer adjustment.
    pub fn record_buffer_adjustment(&mut self) {
        self.buffer_adjustments += 1;
    }

    /// Record a starvation event.
    pub fn record_starvation(&mut self) {
        self.starvation_events += 1;
    }

    /// Reset statistics.
    pub fn reset(&mut self) {
        *self = Self::default();
    }
}

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

    #[test]
    fn test_record_io_scheduler_decision() {
        record_io_scheduler_decision(128 * 1024, "low", "sequential");
        record_io_scheduler_decision(64 * 1024, "high", "random");
    }

    #[test]
    fn test_record_io_priority_decision() {
        record_io_priority_decision("high", 1024);
        record_io_priority_decision("normal", 1024 * 1024);
        record_io_priority_decision("low", 10 * 1024 * 1024);
    }

    #[test]
    fn test_record_load_level_change() {
        record_load_level_change("low", "medium");
        record_load_level_change("medium", "high");
    }

    #[test]
    fn test_record_bandwidth_observation() {
        record_bandwidth_observation(100 * 1024 * 1024);
        record_bandwidth_observation(500 * 1024 * 1024);
    }

    #[test]
    fn test_record_buffer_size_adjustment() {
        record_buffer_size_adjustment(128 * 1024, 64 * 1024, "concurrency");
        record_buffer_size_adjustment(128 * 1024, 256 * 1024, "sequential");
    }

    #[test]
    fn test_record_queue_operation() {
        record_queue_operation("enqueue", "high", 10);
        record_queue_operation("dequeue", "high", 9);
    }

    #[test]
    fn test_record_starvation_event() {
        record_starvation_event("low");
    }

    #[test]
    fn test_io_scheduler_stats() {
        let mut stats = IoSchedulerStats::new();

        stats.record_decision();
        stats.record_priority_decision();
        stats.record_load_change();
        stats.record_buffer_adjustment();
        stats.record_starvation();

        assert_eq!(stats.decisions, 1);
        assert_eq!(stats.priority_decisions, 1);
        assert_eq!(stats.load_changes, 1);
        assert_eq!(stats.buffer_adjustments, 1);
        assert_eq!(stats.starvation_events, 1);
    }
}
