use std::borrow::Cow;
use std::cell::OnceCell;
use std::ptr::NonNull;
use std::slice;
use std::str;

use libsqlite3_sys::{
    sqlite3_value, sqlite3_value_blob, sqlite3_value_bytes, sqlite3_value_double,
    sqlite3_value_dup, sqlite3_value_free, sqlite3_value_int64, sqlite3_value_type,
};
use sqlx_core::type_info::TypeInfo;
pub(crate) use sqlx_core::value::{Value, ValueRef};

use crate::type_info::DataType;
use crate::{Sqlite, SqliteError, SqliteTypeInfo};

/// An owned handle to a [`sqlite3_value`].
///
/// # Note: Decoding is Stateful
/// The [`sqlite3_value` interface][value-methods] reserves the right to be stateful:
///
/// > Other interfaces might change the datatype for an sqlite3_value object.
/// > For example, if the datatype is initially SQLITE_INTEGER and sqlite3_value_text(V) is called
/// > to extract a text value for that integer, then subsequent calls to sqlite3_value_type(V)
/// > might return SQLITE_TEXT. Whether or not a persistent internal datatype conversion occurs is
/// > undefined and may change from one release of SQLite to the next.
///
/// Thus, this type is `!Sync` and [`SqliteValueRef`] is `!Send` and `!Sync` to prevent data races.
///
/// Additionally, this statefulness means that the return values of `sqlite3_value_bytes()` and
/// `sqlite3_value_blob()` could be invalidated by later calls to other `sqlite3_value*` methods.
///
/// To prevent undefined behavior from accessing dangling pointers, this type (and any
/// [`SqliteValueRef`] instances created from it) remembers when it was used to decode a
/// borrowed `&[u8]` or `&str` and returns an error if it is used to decode any other type.
///
/// To bypass this error, you must prove that no outstanding borrows exist.
///
/// This may be done in one of a few ways:
/// * If you hold mutable access, call [`Self::reset_borrow()`] which resets the borrowed state.
/// * If you have an immutable reference, call [`Self::clone()`] to get a new instance
///   with no outstanding borrows.
/// * If you hold a [`SqliteValueRef`], call [`SqliteValueRef::to_owned()`]
///   to get a new `SqliteValue` with no outstanding borrows.
///
/// This is *only* necessary if using the same `SqliteValue` or [`SqliteValueRef`] to decode
/// multiple different types. The vast majority of use-cases employing once-through decoding
/// should not have to worry about this.
///
/// [`sqlite3_value`]: https://www.sqlite.org/c3ref/value.html
/// [value-methods]: https://www.sqlite.org/c3ref/value_blob.html
pub struct SqliteValue(ValueHandle);

/// A borrowed reference to a [`sqlite3_value`].
///
/// Semantically, this behaves as a reference to [`SqliteValue`].
///
/// # Note: Decoding is Stateful
/// See [`SqliteValue`] for details.
pub struct SqliteValueRef<'r>(Cow<'r, ValueHandle>);

impl SqliteValue {
    // SAFETY: The sqlite3_value must be non-null and SQLite must not free it. It will be freed on drop.
    pub(crate) unsafe fn dup(
        value: *mut sqlite3_value,
        column_type: Option<SqliteTypeInfo>,
    ) -> Self {
        debug_assert!(!value.is_null());
        let handle = ValueHandle::try_dup_of(value, column_type)
            .expect("SQLite failed to allocate memory for duplicated value");
        Self(handle)
    }

    /// Prove that there are no outstanding borrows of this instance.
    ///
    /// Call this after decoding a borrowed `&[u8]` or `&str`
    /// to reset the internal borrowed state and allow decoding of other types.
    pub fn reset_borrow(&mut self) {
        self.0.reset_blob_borrow();
    }

    /// Call [`sqlite3_value_dup()`] to create a new instance of this type.
    ///
    /// Returns an error if the call returns a null pointer, indicating that
    /// SQLite was unable to allocate the additional memory required.
    ///
    /// Non-panicking version of [`Self::clone()`].
    ///
    /// [`sqlite3_value_dup()`]: https://www.sqlite.org/c3ref/value_dup.html
    pub fn try_clone(&self) -> Result<Self, SqliteError> {
        self.0.try_dup().map(Self)
    }
}

impl Clone for SqliteValue {
    /// Call [`sqlite3_value_dup()`] to create a new instance of this type.
    ///
    /// # Panics
    /// If [`sqlite3_value_dup()`] returns a null pointer, indicating an out-of-memory condition.
    ///
    /// See [`Self::try_clone()`] for a non-panicking version.
    ///
    /// [`sqlite3_value_dup()`]: https://www.sqlite.org/c3ref/value_dup.html
    fn clone(&self) -> Self {
        self.try_clone().expect("failed to clone `SqliteValue`")
    }
}

impl Value for SqliteValue {
    type Database = Sqlite;

    fn as_ref(&self) -> SqliteValueRef<'_> {
        SqliteValueRef::value(self)
    }

    fn type_info(&self) -> Cow<'_, SqliteTypeInfo> {
        Cow::Owned(self.0.type_info())
    }

    fn is_null(&self) -> bool {
        self.0.is_null()
    }
}

impl<'r> SqliteValueRef<'r> {
    /// Attempt to duplicate the internal `sqlite3_value` with [`sqlite3_value_dup()`].
    ///
    /// Returns an error if the call returns a null pointer, indicating that
    /// SQLite was unable to allocate the additional memory required.
    ///
    /// Non-panicking version of [`Self::try_to_owned()`].
    ///
    /// [`sqlite3_value_dup()`]: https://www.sqlite.org/c3ref/value_dup.html
    pub fn try_to_owned(&self) -> Result<SqliteValue, SqliteError> {
        self.0.try_dup().map(SqliteValue)
    }

    pub(crate) fn value(value: &'r SqliteValue) -> Self {
        Self(Cow::Borrowed(&value.0))
    }

    /// # Safety
    /// The supplied sqlite3_value must not be null and SQLite must free it.
    /// It will not be freed on drop.
    /// The lifetime on this struct should tie it to whatever scope it's valid for before SQLite frees it.
    #[allow(unused)]
    pub(crate) unsafe fn borrowed(value: *mut sqlite3_value) -> Self {
        debug_assert!(!value.is_null());
        let handle = ValueHandle::temporary(NonNull::new_unchecked(value));
        Self(Cow::Owned(handle))
    }

    // NOTE: `int()` is deliberately omitted because it will silently truncate a wider value,
    // which is likely to cause bugs:
    // https://github.com/launchbadge/sqlx/issues/3179
    // (Similar bug in Postgres): https://github.com/launchbadge/sqlx/issues/3161
    pub(super) fn int64(&self) -> Result<i64, BorrowedBlobError> {
        self.0.int64()
    }

    pub(super) fn double(&self) -> Result<f64, BorrowedBlobError> {
        self.0.double()
    }

    pub(super) fn blob_borrowed(&self) -> &'r [u8] {
        // SAFETY: lifetime is matched to `'r`
        unsafe { self.0.blob_borrowed() }
    }

    pub(super) fn with_temp_blob<R>(&self, op: impl FnOnce(&[u8]) -> R) -> R {
        self.0.with_blob(op)
    }

    pub(super) fn blob_owned(&self) -> Vec<u8> {
        self.with_temp_blob(|blob| blob.to_vec())
    }

    pub(super) fn text_borrowed(&self) -> Result<&'r str, str::Utf8Error> {
        // SAFETY: lifetime is matched to `'r`
        unsafe { self.0.text_borrowed() }
    }

    pub(super) fn with_temp_text<R>(
        &self,
        op: impl FnOnce(&str) -> R,
    ) -> Result<R, str::Utf8Error> {
        self.0.with_blob(|blob| str::from_utf8(blob).map(op))
    }

    pub(super) fn text_owned(&self) -> Result<String, str::Utf8Error> {
        self.with_temp_text(|text| text.to_string())
    }
}

impl<'r> ValueRef<'r> for SqliteValueRef<'r> {
    type Database = Sqlite;

    /// Attempt to duplicate the internal `sqlite3_value` with [`sqlite3_value_dup()`].
    ///
    /// # Panics
    /// If [`sqlite3_value_dup()`] returns a null pointer, indicating an out-of-memory condition.
    ///
    /// See [`Self::try_to_owned()`] for a non-panicking version.
    ///
    /// [`sqlite3_value_dup()`]: https://www.sqlite.org/c3ref/value_dup.html
    fn to_owned(&self) -> SqliteValue {
        SqliteValue(
            self.0
                .try_dup()
                .expect("failed to convert SqliteValueRef to owned SqliteValue"),
        )
    }

    fn type_info(&self) -> Cow<'_, SqliteTypeInfo> {
        Cow::Owned(self.0.type_info())
    }

    fn is_null(&self) -> bool {
        self.0.is_null()
    }
}

pub(crate) struct ValueHandle {
    value: NonNull<sqlite3_value>,
    column_type: Option<SqliteTypeInfo>,
    // Note: `std::cell` version
    borrowed_blob: OnceCell<Blob>,
    free_on_drop: bool,
}

struct Blob {
    ptr: *const u8,
    len: usize,
}

#[derive(Debug, thiserror::Error)]
#[error("given `SqliteValue` was previously decoded as BLOB or TEXT; `SqliteValue::reset_borrow()` must be called first")]
pub(crate) struct BorrowedBlobError;

// SAFE: only protected value objects are stored in SqliteValue
unsafe impl Send for ValueHandle {}

// SAFETY: the `sqlite3_value_*()` methods reserve the right to be stateful,
// which means method calls aren't thread-safe without mutual exclusion.
//
// impl !Sync for ValueHandle {}

impl ValueHandle {
    /// # Safety
    /// The `sqlite3_value` must be valid and SQLite must not free it. It will be freed on drop.
    unsafe fn try_dup_of(
        value: *mut sqlite3_value,
        column_type: Option<SqliteTypeInfo>,
    ) -> Result<Self, SqliteError> {
        // SAFETY: caller must ensure `value` is valid.
        let value =
            unsafe { NonNull::new(sqlite3_value_dup(value)).ok_or_else(SqliteError::nomem)? };

        Ok(Self {
            value,
            column_type,
            borrowed_blob: OnceCell::new(),
            free_on_drop: true,
        })
    }

    fn temporary(value: NonNull<sqlite3_value>) -> Self {
        Self {
            value,
            column_type: None,
            borrowed_blob: OnceCell::new(),
            free_on_drop: false,
        }
    }

    fn try_dup(&self) -> Result<Self, SqliteError> {
        // SAFETY: `value` is initialized
        unsafe { Self::try_dup_of(self.value.as_ptr(), self.column_type.clone()) }
    }

    fn value_type_info(&self) -> SqliteTypeInfo {
        SqliteTypeInfo(DataType::from_code(unsafe {
            sqlite3_value_type(self.value.as_ptr())
        }))
    }

    fn type_info(&self) -> SqliteTypeInfo {
        let value_type = self.value_type_info();

        // Assume the actual value type is more accurate, if it's not NULL.
        match &self.column_type {
            Some(column_type) if value_type.is_null() => column_type.clone(),
            _ => value_type,
        }
    }

    fn int64(&self) -> Result<i64, BorrowedBlobError> {
        // SAFETY: we have to be certain the caller isn't still holding a borrow from `.blob_borrowed()`
        self.assert_blob_not_borrowed()?;

        Ok(unsafe { sqlite3_value_int64(self.value.as_ptr()) })
    }

    fn double(&self) -> Result<f64, BorrowedBlobError> {
        // SAFETY: we have to be certain the caller isn't still holding a borrow from `.blob_borrowed()`
        self.assert_blob_not_borrowed()?;

        Ok(unsafe { sqlite3_value_double(self.value.as_ptr()) })
    }

    fn is_null(&self) -> bool {
        self.value_type_info().is_null()
    }
}

impl Clone for ValueHandle {
    fn clone(&self) -> Self {
        self.try_dup().unwrap()
    }
}

impl Drop for ValueHandle {
    fn drop(&mut self) {
        if self.free_on_drop {
            unsafe {
                sqlite3_value_free(self.value.as_ptr());
            }
        }
    }
}

impl ValueHandle {
    fn assert_blob_not_borrowed(&self) -> Result<(), BorrowedBlobError> {
        if self.borrowed_blob.get().is_none() {
            Ok(())
        } else {
            Err(BorrowedBlobError)
        }
    }

    fn reset_blob_borrow(&mut self) {
        self.borrowed_blob.take();
    }

    fn get_blob(&self) -> Option<Blob> {
        if let Some(blob) = self.borrowed_blob.get() {
            return Some(Blob { ..*blob });
        }

        // SAFETY: calling `sqlite3_value_bytes` from multiple threads at once is a data race.
        let len = unsafe { sqlite3_value_bytes(self.value.as_ptr()) };

        // This likely means UB in SQLite itself or our usage of it;
        // signed integer overflow is UB in the C standard.
        let len = usize::try_from(len).unwrap_or_else(|_| {
            panic!("sqlite3_value_bytes() returned value out of range for usize: {len}")
        });

        if len == 0 {
            // empty blobs are NULL
            return None;
        }

        let ptr = unsafe { sqlite3_value_blob(self.value.as_ptr()) } as *const u8;
        debug_assert!(!ptr.is_null());

        Some(Blob { ptr, len })
    }

    fn with_blob<R>(&self, with_blob: impl FnOnce(&[u8]) -> R) -> R {
        let Some(blob) = self.get_blob() else {
            return with_blob(&[]);
        };

        // SAFETY: the slice cannot outlive the call
        with_blob(unsafe { blob.as_slice() })
    }

    /// # Safety
    /// Caller must ensure lifetime '`b` cannot outlive `self`.
    unsafe fn blob_borrowed<'a>(&self) -> &'a [u8] {
        let Some(blob) = self.get_blob() else {
            return &[];
        };

        // SAFETY: we need to store that the blob was borrowed
        // to prevent
        let blob = self.borrowed_blob.get_or_init(|| blob);

        unsafe { blob.as_slice() }
    }

    /// # Safety
    /// Caller must ensure lifetime '`b` cannot outlive `self`.
    unsafe fn text_borrowed<'b>(&self) -> Result<&'b str, str::Utf8Error> {
        let Some(blob) = self.get_blob() else {
            return Ok("");
        };

        // SAFETY: lifetime of `blob` will be tied to `'b`.
        let s = str::from_utf8(unsafe { blob.as_slice() })?;

        // We only store the borrow after we ensure the string is valid.
        self.borrowed_blob.set(blob).ok();

        Ok(s)
    }
}

impl Blob {
    /// # Safety
    /// `'a` must not outlive the `sqlite3_value` this blob came from.
    unsafe fn as_slice<'a>(&self) -> &'a [u8] {
        slice::from_raw_parts(self.ptr, self.len)
    }
}
