use std::alloc::Layout;
use std::ptr::NonNull;
use std::vec::Vec;

use crate::size_classes::SIZE_CLASSES;

#[repr(align(64))]
pub(crate) struct SlabAllocator {
    pools: [Vec<NonNull<u8>>; SIZE_CLASSES.len()],
}

impl SlabAllocator {
    #[inline]
    pub(crate) fn new() -> Self {
        Self {
            pools: std::array::from_fn(|_| Vec::new()),
        }
    }

    #[inline]
    fn get_pool_index(size: usize) -> Option<usize> {
        SIZE_CLASSES
            .iter()
            .position(|&class_size| size <= class_size)
    }

    #[inline]
    pub(crate) unsafe fn alloc(&mut self, size: usize) -> Option<NonNull<u8>> {
        let pool_idx = Self::get_pool_index(size)?;
        self.pools[pool_idx].pop()
    }

    #[inline]
    pub(crate) unsafe fn dealloc(&mut self, ptr: NonNull<u8>, size: usize) {
        let Some(pool_idx) = Self::get_pool_index(size) else {
            if let Ok(layout) = Layout::from_size_align(size, 8) {
                eprintln!(
                    "[slab::dealloc] dealloc ptr={:p} size={}",
                    ptr.as_ptr(),
                    size
                );
                eprintln!(
                    "[slab::dealloc] backtrace:\n{}",
                    std::backtrace::Backtrace::capture()
                );
                std::alloc::dealloc(ptr.as_ptr(), layout);
            } else {
                let fallback = Layout::from_size_align(8, 8).expect("fallback layout invalid");
                eprintln!(
                    "[slab::dealloc] dealloc ptr={:p} size=fallback(8)",
                    ptr.as_ptr()
                );
                eprintln!(
                    "[slab::dealloc] backtrace:\n{}",
                    std::backtrace::Backtrace::capture()
                );
                std::alloc::dealloc(ptr.as_ptr(), fallback);
            }
            return;
        };

        if self.pools[pool_idx].len() < 64 {
            self.pools[pool_idx].push(ptr);
        } else {
            if let Ok(layout) = Layout::from_size_align(size, 8) {
                eprintln!(
                    "[slab::dealloc] dealloc ptr={:p} size={}",
                    ptr.as_ptr(),
                    size
                );
                eprintln!(
                    "[slab::dealloc] backtrace:\n{}",
                    std::backtrace::Backtrace::capture()
                );
                std::alloc::dealloc(ptr.as_ptr(), layout);
            } else {
                let fallback = Layout::from_size_align(8, 8).expect("fallback layout invalid");
                eprintln!(
                    "[slab::dealloc] dealloc ptr={:p} size=fallback(8)",
                    ptr.as_ptr()
                );
                eprintln!(
                    "[slab::dealloc] backtrace:\n{}",
                    std::backtrace::Backtrace::capture()
                );
                std::alloc::dealloc(ptr.as_ptr(), fallback);
            }
        }
    }
}
