use std::sync::Arc;
use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};

/// We want to have limited amount of clocks, because all the clocks we create during
/// qdrant operations are preserved for the entire live-span of the cluster.
///
/// Limiting number of clocks essentially means that we are limiting number of parallel
/// update operations which can happen on the cluster, but this number should be high enough
/// for all reasonable use cases.
const DEFAULT_MAX_CLOCKS: usize = 64;

#[derive(Clone, Debug)]
pub struct ClockSet {
    clocks: Vec<Arc<Clock>>,
    max_clocks: usize,
}

impl Default for ClockSet {
    fn default() -> Self {
        Self::new()
    }
}

impl ClockSet {
    pub fn new() -> Self {
        Self::with_max_clocks(DEFAULT_MAX_CLOCKS)
    }

    pub fn with_max_clocks(max_clocks: usize) -> Self {
        Self {
            clocks: Vec::new(),
            max_clocks,
        }
    }

    /// Get the first available clock from the set, or create a new one.
    pub fn get_clock(&mut self) -> Option<ClockGuard> {
        for (id, clock) in self.clocks.iter().enumerate() {
            if clock.try_lock() {
                return Some(ClockGuard::new(id as u32, clock.clone()));
            }
        }

        let id = self.clocks.len();
        if id >= self.max_clocks {
            return None;
        }

        let clock = Arc::new(Clock::new_locked());

        self.clocks.push(clock.clone());

        Some(ClockGuard::new(id as u32, clock))
    }
}

#[derive(Debug)]
pub struct ClockGuard {
    id: u32,
    clock: Arc<Clock>,
}

impl ClockGuard {
    fn new(id: u32, clock: Arc<Clock>) -> Self {
        Self { id, clock }
    }

    pub fn id(&self) -> u32 {
        self.id
    }

    /// Return current clock tick.
    pub fn current_tick(&self) -> Option<u64> {
        self.clock.current_tick()
    }

    /// Advance clock by a single tick and return current tick.
    #[must_use = "new clock value must be used"]
    pub fn tick_once(&mut self) -> u64 {
        self.clock.tick_once()
    }

    /// Advance clock to `new_tick`, if `new_tick` is newer than current tick.
    pub fn advance_to(&mut self, new_tick: u64) {
        self.clock.advance_to(new_tick)
    }
}

impl Drop for ClockGuard {
    fn drop(&mut self) {
        self.clock.release();
    }
}

#[derive(Debug)]
struct Clock {
    /// Tracks the *next* clock tick
    next_tick: AtomicU64,
    available: AtomicBool,
}

impl Clock {
    fn new_locked() -> Self {
        Self {
            next_tick: 0.into(),
            available: false.into(),
        }
    }

    /// Return current clock tick.
    fn current_tick(&self) -> Option<u64> {
        let next_tick = self.next_tick.load(Ordering::Relaxed);
        next_tick.checked_sub(1)
    }

    /// Advance clock by a single tick and return current tick.
    ///
    /// # Thread safety
    ///
    /// Clock *has to* be locked (using [`Clock::try_lock`]) before calling [`Clock::tick_once`]!
    #[must_use = "new clock tick value must be used"]
    fn tick_once(&self) -> u64 {
        // `Clock` tracks *next* tick, so we increment `next_tick` by 1 and return *previous* value
        // of `next_tick` (which is *exactly* what `fetch_add(1)` does)
        let current_tick = self.next_tick.fetch_add(1, Ordering::Relaxed);

        // If `current_tick` is `0`, then "revert" `next_tick` back to `0`.
        // We expect that `advance_to` would be used to advance "past" the initial `0`.
        //
        // Executing multiple atomic operations sequentially is not strictly thread-safe,
        // but we expect that `Clock` would be "locked" before calling `tick_once`.
        if current_tick == 0 {
            self.next_tick.store(0, Ordering::Relaxed);
        }

        current_tick
    }

    /// Advance clock to `new_tick`, if `new_tick` is newer than current tick.
    fn advance_to(&self, new_tick: u64) {
        // `Clock` tracks *next* tick, so if we want to advance *current* tick to `new_tick`,
        // we have to advance `next_tick` to `new_tick + 1`
        self.next_tick.fetch_max(new_tick + 1, Ordering::Relaxed);
    }

    /// Try to acquire exclusive lock over this clock.
    ///
    /// Returns `true` if the lock was successfully acquired, or `false` if the clock is already
    /// locked.
    fn try_lock(&self) -> bool {
        self.available.swap(false, Ordering::Relaxed)
    }

    /// Release the exclusive lock over this clock.
    ///
    /// No-op if the clock is not locked.
    fn release(&self) {
        self.available.store(true, Ordering::Relaxed);
    }
}

#[cfg(test)]
mod tests {
    use std::iter;

    use rand::prelude::*;

    use super::*;

    /// Tick a single clock, it should increment after we advance it from 0 (or higher).
    #[test]
    fn test_clock_set_single_tick() {
        let mut clock_set = ClockSet::new();

        // Don't tick from 0 unless we explicitly advance
        assert_eq!(clock_set.get_clock().unwrap().tick_once(), 0);
        assert_eq!(clock_set.get_clock().unwrap().tick_once(), 0);
        assert_eq!(clock_set.get_clock().unwrap().tick_once(), 0);

        clock_set.get_clock().unwrap().advance_to(0);

        // Following ticks should increment
        assert_eq!(clock_set.get_clock().unwrap().tick_once(), 1);
        assert_eq!(clock_set.get_clock().unwrap().tick_once(), 2);
        assert_eq!(clock_set.get_clock().unwrap().tick_once(), 3);
        assert_eq!(clock_set.get_clock().unwrap().tick_once(), 4);
    }

    /// Test a single clock, but tick it multiple times on the same guard.
    #[test]
    fn test_clock_set_single_tick_twice() {
        let mut clock_set = ClockSet::new();

        // Don't tick from 0 unless we explicitly advance
        {
            let mut clock = clock_set.get_clock().unwrap();
            assert_eq!(clock.tick_once(), 0);
            assert_eq!(clock.tick_once(), 0);
            clock.advance_to(0);
        }

        // Following ticks should increment
        {
            let mut clock = clock_set.get_clock().unwrap();
            assert_eq!(clock.tick_once(), 1);
            assert_eq!(clock.tick_once(), 2);
            assert_eq!(clock.tick_once(), 3);
            assert_eq!(clock.tick_once(), 4);
        }
    }

    /// Advance a clock to a higher number, which should increase it.
    #[test]
    fn test_clock_set_single_advance_high() {
        let mut clock_set = ClockSet::new();

        // Bring the clock up to 4
        assert_eq!(clock_set.get_clock().unwrap().tick_once(), 0);
        clock_set.get_clock().unwrap().advance_to(0);
        assert_eq!(clock_set.get_clock().unwrap().tick_once(), 1);
        assert_eq!(clock_set.get_clock().unwrap().tick_once(), 2);
        assert_eq!(clock_set.get_clock().unwrap().tick_once(), 3);
        assert_eq!(clock_set.get_clock().unwrap().tick_once(), 4);

        // If we advance to 100, we should continue from 101
        clock_set.get_clock().unwrap().advance_to(100);
        assert_eq!(clock_set.get_clock().unwrap().tick_once(), 101);
    }

    /// Advance a clock to a lower number, which should not do anything.
    #[test]
    fn test_clock_set_single_advance_low() {
        let mut clock_set = ClockSet::new();

        // Bring the clock up to 4
        assert_eq!(clock_set.get_clock().unwrap().tick_once(), 0);
        clock_set.get_clock().unwrap().advance_to(0);
        assert_eq!(clock_set.get_clock().unwrap().tick_once(), 1);
        assert_eq!(clock_set.get_clock().unwrap().tick_once(), 2);
        assert_eq!(clock_set.get_clock().unwrap().tick_once(), 3);
        assert_eq!(clock_set.get_clock().unwrap().tick_once(), 4);

        // If we advance to a low number, just continue
        clock_set.get_clock().unwrap().advance_to(0);
        assert_eq!(clock_set.get_clock().unwrap().tick_once(), 5);

        clock_set.get_clock().unwrap().advance_to(1);
        assert_eq!(clock_set.get_clock().unwrap().tick_once(), 6);
    }

    /// Test multiple clocks in various configurations.
    #[test]
    fn test_clock_multi_tick() {
        let mut clock_set = ClockSet::new();

        // 2 parallel operations, that fails and doesn't advance
        {
            let mut clock1 = clock_set.get_clock().unwrap();
            let mut clock2 = clock_set.get_clock().unwrap();
            assert_eq!(clock1.tick_once(), 0);
            assert_eq!(clock2.tick_once(), 0);
        }

        // 2 parallel operations
        {
            let mut clock1 = clock_set.get_clock().unwrap();
            let mut clock2 = clock_set.get_clock().unwrap();
            assert_eq!(clock1.tick_once(), 0);
            assert_eq!(clock2.tick_once(), 0);
            clock1.advance_to(0);
            clock2.advance_to(0);
        }

        // 1 operation
        {
            let mut clock1 = clock_set.get_clock().unwrap();
            assert_eq!(clock1.tick_once(), 1);
            clock1.advance_to(1);
        }

        // 1 operation, without advancing should still tick
        {
            let mut clock1 = clock_set.get_clock().unwrap();
            assert_eq!(clock1.tick_once(), 2);
        }

        // 2 parallel operations
        {
            let mut clock1 = clock_set.get_clock().unwrap();
            let mut clock2 = clock_set.get_clock().unwrap();
            assert_eq!(clock1.tick_once(), 3);
            assert_eq!(clock2.tick_once(), 1);
            clock1.advance_to(3);
            clock2.advance_to(1);
        }

        // 3 parallel operations, but clock 2 was much newer on some node
        {
            let mut clock1 = clock_set.get_clock().unwrap();
            let mut clock2 = clock_set.get_clock().unwrap();
            let mut clock3 = clock_set.get_clock().unwrap();
            assert_eq!(clock1.tick_once(), 4);
            assert_eq!(clock2.tick_once(), 2);
            assert_eq!(clock3.tick_once(), 0);
            clock1.advance_to(4);
            clock2.advance_to(10);
            clock3.advance_to(0);
        }

        // 3 parallel operations, advancing in a different order should not matter
        {
            let mut clock1 = clock_set.get_clock().unwrap();
            let mut clock2 = clock_set.get_clock().unwrap();
            let mut clock3 = clock_set.get_clock().unwrap();
            assert_eq!(clock1.tick_once(), 5);
            assert_eq!(clock2.tick_once(), 11);
            assert_eq!(clock3.tick_once(), 1);
            clock3.advance_to(1);
            clock2.advance_to(11);
            clock1.advance_to(5);
        }

        // 3 parallel operations, advancing just some should still tick all
        {
            let mut clock1 = clock_set.get_clock().unwrap();
            let mut clock2 = clock_set.get_clock().unwrap();
            let mut clock3 = clock_set.get_clock().unwrap();
            assert_eq!(clock1.tick_once(), 6);
            assert_eq!(clock2.tick_once(), 12);
            assert_eq!(clock3.tick_once(), 2);
            clock2.advance_to(12);
        }

        // 1 operation
        {
            let mut clock1 = clock_set.get_clock().unwrap();
            assert_eq!(clock1.tick_once(), 7);
        }

        // Test final state of all clocks
        {
            let mut clock1 = clock_set.get_clock().unwrap();
            let mut clock2 = clock_set.get_clock().unwrap();
            let mut clock3 = clock_set.get_clock().unwrap();
            let mut clock4 = clock_set.get_clock().unwrap();
            assert_eq!(clock1.tick_once(), 8);
            assert_eq!(clock2.tick_once(), 13);
            assert_eq!(clock3.tick_once(), 3);
            assert_eq!(clock4.tick_once(), 0);
        }
    }

    /// Test a number of parallel operations with some running for a long time. Clocks are resolved
    /// unordered.
    #[test]
    fn test_clock_set_long_running_unordered() {
        let mut clock_set = ClockSet::new();

        // Clock 1 runs for a long while
        let mut clock1 = clock_set.get_clock().unwrap();
        assert_eq!(clock1.tick_once(), 0);

        // 2 quick parallel operations
        {
            let mut clock2 = clock_set.get_clock().unwrap();
            let mut clock3 = clock_set.get_clock().unwrap();
            assert_eq!(clock2.tick_once(), 0);
            assert_eq!(clock3.tick_once(), 0);
            clock2.advance_to(0);
            clock3.advance_to(0);
        }

        // Clock 2 runs for a long while
        let clock2 = clock_set.get_clock().unwrap();
        assert_eq!(clock1.tick_once(), 0);

        // 2 quick parallel operations
        {
            let mut clock3 = clock_set.get_clock().unwrap();
            let mut clock4 = clock_set.get_clock().unwrap();
            assert_eq!(clock3.tick_once(), 1);
            assert_eq!(clock4.tick_once(), 0);
            clock4.advance_to(0);
        }

        // Clock 1 finally resolves
        clock1.advance_to(0);
        drop(clock1);

        // 3 quick parallel operations
        {
            let mut clock1 = clock_set.get_clock().unwrap();
            let mut clock3 = clock_set.get_clock().unwrap();
            let mut clock4 = clock_set.get_clock().unwrap();
            assert_eq!(clock1.tick_once(), 1);
            assert_eq!(clock3.tick_once(), 2);
            assert_eq!(clock4.tick_once(), 1);
        }

        // Clock 2 finally resolves
        drop(clock2);

        // Test final state of all clocks
        {
            let mut clock1 = clock_set.get_clock().unwrap();
            let mut clock2 = clock_set.get_clock().unwrap();
            let mut clock3 = clock_set.get_clock().unwrap();
            let mut clock4 = clock_set.get_clock().unwrap();
            let mut clock5 = clock_set.get_clock().unwrap();
            assert_eq!(clock1.tick_once(), 2);
            assert_eq!(clock2.tick_once(), 1);
            assert_eq!(clock3.tick_once(), 3);
            assert_eq!(clock4.tick_once(), 2);
            assert_eq!(clock5.tick_once(), 0);
        }
    }

    /// Test and increment a lot of clocks, but at some point 10% of the clocks gets stuck.
    #[test]
    fn test_clock_set_many() {
        const N: usize = 5000;

        let mut clock_set = ClockSet::with_max_clocks(N);

        // Tick all clocks past 0
        {
            let mut clocks = iter::repeat_with(|| clock_set.get_clock().unwrap())
                .take(N)
                .collect::<Vec<_>>();
            clocks.shuffle(&mut rand::rng());

            for clock in &mut clocks {
                assert_eq!(clock.tick_once(), 0);
                clock.advance_to(0);
            }
        }

        // Tick all clocks 10 times
        {
            for tick in 0..10 {
                let mut clocks = iter::repeat_with(|| clock_set.get_clock().unwrap())
                    .take(N)
                    .collect::<Vec<_>>();
                clocks.shuffle(&mut rand::rng());

                for clock in &mut clocks {
                    assert_eq!(clock.tick_once(), 1 + tick);
                }
            }
        }

        // Now the first 10% gets stuck
        let mut stuck_clocks = iter::repeat_with(|| clock_set.get_clock().unwrap())
            .take(N / 10)
            .collect::<Vec<_>>();
        for clock in stuck_clocks.iter_mut() {
            assert_eq!(clock.tick_once(), 11);
        }

        // Tick all other clocks 10 times
        {
            for tick in 0..10 {
                let mut clocks = iter::repeat_with(|| clock_set.get_clock().unwrap())
                    .take(N - (N / 10))
                    .collect::<Vec<_>>();
                clocks.shuffle(&mut rand::rng());

                for clock in clocks.iter_mut() {
                    assert_eq!(clock.tick_once(), 11 + tick);
                }
            }
        }

        // All stuck clocks resolve
        drop(stuck_clocks);

        // Test all clocks
        {
            let mut stuck_clocks = iter::repeat_with(|| clock_set.get_clock().unwrap())
                .take(N / 10)
                .collect::<Vec<_>>();
            let mut clocks = iter::repeat_with(|| clock_set.get_clock().unwrap())
                .take(N - (N / 10))
                .collect::<Vec<_>>();

            for clock in stuck_clocks.iter_mut() {
                assert_eq!(clock.tick_once(), 12);
            }
            for clock in clocks.iter_mut() {
                assert_eq!(clock.tick_once(), 21);
            }
        }
    }

    /// Test and increment a lot of clocks, but each iteration, we get on more clock stuck.
    #[test]
    fn test_clock_set_many_staggered_stuck() {
        const N: usize = 500;

        let mut clock_set = ClockSet::with_max_clocks(N);

        let mut stuck_clocks = Vec::new();

        for i in 0..N {
            let mut clock_to_stuck = clock_set.get_clock().unwrap();
            let mut clocks = iter::repeat_with(|| clock_set.get_clock().unwrap())
                .take(N - i - 1)
                .collect::<Vec<_>>();

            if clock_to_stuck.tick_once() == 0 {
                clock_to_stuck.advance_to(0);
            }

            for clock in clocks.iter_mut() {
                if clock.tick_once() == 0 {
                    clock.advance_to(0);
                }
            }

            stuck_clocks.push(clock_to_stuck);
        }

        // All stuck clocks resolve now
        drop(stuck_clocks);

        // Test all clocks
        {
            let mut clocks = iter::repeat_with(|| clock_set.get_clock().unwrap())
                .take(N)
                .enumerate()
                .collect::<Vec<_>>();

            for (i, clock) in clocks.iter_mut() {
                assert_eq!(clock.tick_once(), *i as u64 + 1);
            }
        }
    }
}
