// Copyright (C) 2025 Huawei Device Co., Ltd.
// 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.

//! Sequence ID generation module for tasks.
//!
//! This module provides thread-safe generation of unique task sequence IDs
//! using atomic operations to ensure uniqueness across concurrent operations.

use std::num::NonZeroU64;
use std::sync::atomic::{AtomicU64, Ordering};

/// Represents a unique task sequence ID.
///
/// Wraps a `NonZeroU64` to ensure that task IDs are always non-zero and unique.
pub struct TaskSeq(pub NonZeroU64);

impl TaskSeq {
    /// Generates the next unique task sequence ID.
    ///
    /// Uses an atomic counter with `Relaxed` ordering to generate IDs
    /// efficiently while ensuring thread safety. Handles overflow by
    /// resetting to 0 and continues.
    ///
    /// # Returns
    ///
    /// A new `TaskSeq` with a unique non-zero ID
    ///
    /// # Panics
    ///
    /// Panics if the generated ID is zero after overflow reset, which should be
    /// impossible since we immediately increment from 0.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use request_api10::seq::TaskSeq;
    ///
    /// let task_id1 = TaskSeq::next();
    /// let task_id2 = TaskSeq::next();
    ///
    /// assert_ne!(task_id1.0, task_id2.0);
    /// assert!(task_id1.0.get() > 0);
    /// assert!(task_id2.0.get() > 0);
    /// ```
    pub fn next() -> Self {
        // Static atomic counter to track the next available ID
        static NEXT_ID: AtomicU64 = AtomicU64::new(0);

        // Start with current value using relaxed ordering for efficiency
        let mut last = NEXT_ID.load(Ordering::Relaxed);

        // Loop until we successfully update the counter using compare-and-swap
        loop {
            // Calculate next ID, handling potential overflow
            let id = match last.checked_add(1) {
                Some(id) => id,
                None => {
                    // Reset to 0 on overflow, though in practice this is unlikely
                    error!("Task ID overflow, resetting to 0");
                    0
                }
            };

            // Try to update the atomic counter using weak compare-and-swap
            match NEXT_ID.compare_exchange_weak(last, id, Ordering::Relaxed, Ordering::Relaxed) {
                Ok(_) => return TaskSeq(NonZeroU64::new(id).unwrap()),
                Err(id) => last = id,
            }
        }
    }
}