use std::cell::RefCell;

use pyo3::exceptions::{PyTypeError, PyValueError};
use pyo3::ffi;
use pyo3::prelude::*;
use pyo3::types::{PyAny, PyBytes, PyDict, PyType};

thread_local! {
    static STDLIB_CACHE: RefCell<Option<StdlibCache>> = const { RefCell::new(None) };
}

pub(crate) struct StdlibCache {
    pub(crate) enum_type: Py<PyAny>,
    pub(crate) builtin_bytes: Py<PyAny>,
}

pub(crate) fn with_stdlib_cache<F, R>(py: Python<'_>, f: F) -> PyResult<R>
where
    F: FnOnce(&StdlibCache) -> PyResult<R>,
{
    STDLIB_CACHE.with(|cell| {
        let mut cache_opt = cell.borrow_mut();
        if cache_opt.is_none() {
            let enum_mod = py.import("enum")?;
            let enum_type = enum_mod.getattr("Enum")?.unbind();
            let builtins = py.import("builtins")?;
            let builtin_bytes = builtins.getattr("bytes")?.unbind();

            *cache_opt = Some(StdlibCache {
                enum_type,
                builtin_bytes,
            });
        }
        f(cache_opt.as_ref().unwrap())
    })
}

#[inline]
pub(crate) fn is_buffer_like(value: &Bound<'_, PyAny>) -> bool {
    if value.is_instance_of::<PyBytes>() {
        return true;
    }

    // SAFETY:
    // PyObject_CheckBuffer performs a read-only capability check on a valid PyObject pointer.
    unsafe { ffi::PyObject_CheckBuffer(value.as_ptr()) != 0 }
}

pub(crate) fn try_coerce_buffer_to_bytes<'py>(
    value: &Bound<'py, PyAny>,
) -> PyResult<Option<Bound<'py, PyBytes>>> {
    if let Ok(bytes) = value.cast::<PyBytes>() {
        return Ok(Some(bytes.clone()));
    }
    if !is_buffer_like(value) {
        return Ok(None);
    }

    with_stdlib_cache(value.py(), |cache| {
        let bytes_obj = cache.builtin_bytes.bind(value.py()).call1((value,))?;
        let bytes = bytes_obj
            .cast_into::<PyBytes>()
            .map_err(|_| PyTypeError::new_err("buffer object must be coercible to bytes"))?;
        Ok(Some(bytes))
    })
}

pub(crate) fn dataclass_fields<'py>(
    value: &Bound<'py, PyAny>,
) -> PyResult<Option<Bound<'py, PyDict>>> {
    let cls = value.get_type();
    let fields_any = match cls.getattr("__dataclass_fields__") {
        Ok(v) => v,
        Err(_) => return Ok(None),
    };
    match fields_any.cast::<PyDict>() {
        Ok(fields) => Ok(Some(fields.clone())),
        Err(_) => Ok(None),
    }
}

pub const MAX_DEPTH: usize = 48;
// Capacity threshold (1MB). If buffer exceeds this, we shrink it back.
pub const BUFFER_SHRINK_THRESHOLD: usize = 1024 * 1024;
// Default initial capacity (128 bytes).
pub const BUFFER_DEFAULT_CAPACITY: usize = 128;

/// 编码后智能缩容:避免单次大包导致内存长期驻留.
#[inline]
pub(crate) fn maybe_shrink_buffer(buffer: &mut Vec<u8>) {
    let used = buffer.len();
    if buffer.capacity() > BUFFER_SHRINK_THRESHOLD && used < (BUFFER_SHRINK_THRESHOLD / 4) {
        let target = if used == 0 {
            BUFFER_DEFAULT_CAPACITY
        } else {
            used.next_power_of_two().max(BUFFER_DEFAULT_CAPACITY)
        };
        buffer.shrink_to(target);
    }
}

/// 从已持有的 Python 类型句柄获取当前 GIL 作用域下的绑定引用.
#[inline]
pub(crate) fn class_from_type<'py>(py: Python<'py>, cls: &Py<PyType>) -> Bound<'py, PyType> {
    cls.bind(py).clone()
}

#[inline]
pub fn check_depth(depth: usize) -> PyResult<()> {
    if depth >= MAX_DEPTH {
        return Err(PyValueError::new_err(format!(
            "Recursion depth exceeded (max={}, observed={})",
            MAX_DEPTH, depth
        )));
    }
    Ok(())
}

pub(crate) struct PySequenceFast {
    ptr: *mut ffi::PyObject,
    len: isize,
    is_list: bool,
}

impl PySequenceFast {
    pub(crate) fn new_exact(obj: &Bound<'_, PyAny>, is_list: bool) -> PyResult<Self> {
        // SAFETY:
        // 1. ptr 是一个有效的 Python 对象指针，已知是 list 或 tuple。
        // 2. 我们刚刚增加了引用计数，确保它保持存活。
        let ptr = obj.as_ptr();
        unsafe { ffi::Py_INCREF(ptr) };

        let len = unsafe {
            if is_list {
                let list_ptr = ptr as *mut ffi::PyListObject;
                (*list_ptr).ob_base.ob_size
            } else {
                let tuple_ptr = ptr as *mut ffi::PyTupleObject;
                (*tuple_ptr).ob_base.ob_size
            }
        };
        Ok(Self { ptr, len, is_list })
    }

    pub(crate) fn len(&self) -> usize {
        self.len as usize
    }

    pub(crate) fn get_item<'py>(&self, py: Python<'py>, idx: usize) -> PyResult<Bound<'py, PyAny>> {
        if idx as isize >= self.len {
            return Err(PyValueError::new_err("Index out of bounds"));
        }
        // SAFETY:
        // 1. ptr 保持强引用存活。
        // 2. GetItem 返回借用引用(Borrowed Reference)。
        // 3. 不缓存 items 指针，避免列表扩容导致的 UAF。
        unsafe {
            let item_ptr = if self.is_list {
                ffi::PyList_GetItem(self.ptr, idx as isize)
            } else {
                ffi::PyTuple_GetItem(self.ptr, idx as isize)
            };
            if item_ptr.is_null() {
                return Err(PyErr::fetch(py));
            }
            Ok(Bound::from_borrowed_ptr(py, item_ptr))
        }
    }
}

impl Drop for PySequenceFast {
    fn drop(&mut self) {
        // SAFETY:
        // self.ptr 是一个拥有的引用（强引用）。
        // 当此包装器被删除时，我们需要减少它的引用计数。
        unsafe {
            ffi::Py_DECREF(self.ptr);
        }
    }
}

pub(crate) fn check_exact_sequence_type(obj: &Bound<'_, PyAny>) -> Option<bool> {
    // SAFETY:
    // 在有效的 Python 对象指针上调用标准类型检查宏是安全的。
    unsafe {
        if ffi::PyList_CheckExact(obj.as_ptr()) != 0 {
            Some(true)
        } else if ffi::PyTuple_CheckExact(obj.as_ptr()) != 0 {
            Some(false)
        } else {
            None
        }
    }
}
