use core::io::Custom;
#[cfg_attr(no_global_oom_handling, expect(unused_imports))]
use core::io::CustomOwner;
use core::{error, result};

use crate::boxed::Box;
#[cfg_attr(any(no_rc, no_sync, no_global_oom_handling), expect(unused_imports))]
use crate::io::const_error;
use crate::io::{Error, ErrorKind};

impl Error {
    /// Creates a new I/O error from a known kind of error as well as an
    /// arbitrary error payload.
    ///
    /// This function is used to generically create I/O errors which do not
    /// originate from the OS itself. The `error` argument is an arbitrary
    /// payload which will be contained in this [`Error`].
    ///
    /// Note that this function allocates memory on the heap.
    /// If no extra payload is required, use the `From` conversion from
    /// `ErrorKind`.
    ///
    /// # Examples
    ///
    /// ```
    /// use std::io::{Error, ErrorKind};
    ///
    /// // errors can be created from strings
    /// let custom_error = Error::new(ErrorKind::Other, "oh no!");
    ///
    /// // errors can also be created from other errors
    /// let custom_error2 = Error::new(ErrorKind::Interrupted, custom_error);
    ///
    /// // creating an error without payload (and without memory allocation)
    /// let eof_error = Error::from(ErrorKind::UnexpectedEof);
    /// ```
    #[cfg(not(no_global_oom_handling))]
    #[stable(feature = "rust1", since = "1.0.0")]
    #[cfg_attr(not(test), rustc_diagnostic_item = "io_error_new")]
    #[inline(never)]
    #[rustc_allow_incoherent_impl]
    pub fn new<E>(kind: ErrorKind, error: E) -> Error
    where
        E: Into<Box<dyn error::Error + Send + Sync>>,
    {
        let custom = custom_owner_from_box(kind, error.into());

        // SAFETY: `custom_owner` has been constructed from a `Box` from the `alloc` crate.
        unsafe { Self::from_custom_owner(custom) }
    }

    /// Creates a new I/O error from an arbitrary error payload.
    ///
    /// This function is used to generically create I/O errors which do not
    /// originate from the OS itself. It is a shortcut for [`Error::new`][new]
    /// with [`ErrorKind::Other`].
    ///
    // FIXME(#74481): Hard-links required to link from `alloc` to `std` for incoherent method
    /// [new]: struct.Error.html#method.new
    ///
    /// # Examples
    ///
    /// ```
    /// use std::io::Error;
    ///
    /// // errors can be created from strings
    /// let custom_error = Error::other("oh no!");
    ///
    /// // errors can also be created from other errors
    /// let custom_error2 = Error::other(custom_error);
    /// ```
    #[cfg(not(no_global_oom_handling))]
    #[stable(feature = "io_error_other", since = "1.74.0")]
    #[rustc_allow_incoherent_impl]
    pub fn other<E>(error: E) -> Error
    where
        E: Into<Box<dyn error::Error + Send + Sync>>,
    {
        Self::new(ErrorKind::Other, error)
    }

    /// Consumes the `Error`, returning its inner error (if any).
    ///
    /// If this [`Error`] was constructed via [`new`][new] or [`other`][other],
    /// then this function will return [`Some`],
    /// otherwise it will return [`None`].
    ///
    // FIXME(#74481): Hard-links required to link from `alloc` to `std` for incoherent method
    /// [new]: struct.Error.html#method.new
    /// [other]: struct.Error.html#method.other
    ///
    /// # Examples
    ///
    /// ```
    /// use std::io::{Error, ErrorKind};
    ///
    /// fn print_error(err: Error) {
    ///     if let Some(inner_err) = err.into_inner() {
    ///         println!("Inner error: {inner_err}");
    ///     } else {
    ///         println!("No inner error");
    ///     }
    /// }
    ///
    /// fn main() {
    ///     // Will print "No inner error".
    ///     print_error(Error::last_os_error());
    ///     // Will print "Inner error: ...".
    ///     print_error(Error::new(ErrorKind::Other, "oh no!"));
    /// }
    /// ```
    #[stable(feature = "io_error_inner", since = "1.3.0")]
    #[must_use = "`self` will be dropped if the result is not used"]
    #[inline]
    #[rustc_allow_incoherent_impl]
    pub fn into_inner(self) -> Option<Box<dyn error::Error + Send + Sync>> {
        let custom_owner = self.into_custom_owner().ok()?;

        let ptr = custom_owner.into_raw().as_ptr();

        // SAFETY:
        // `Error` can only contain a `CustomOwner` if it was constructed using `Box::into_raw`.
        let custom = unsafe { Box::<Custom>::from_raw(ptr) };

        let ptr = custom.into_raw().as_ptr();

        // SAFETY:
        // Any `CustomOwner` from an `Error` was constructed by the `alloc` crate
        // to contain a `Custom` which itself was constructed with `Box::into_raw`.
        Some(unsafe { Box::from_raw(ptr) })
    }

    /// Attempts to downcast the custom boxed error to `E`.
    ///
    /// If this [`Error`] contains a custom boxed error,
    /// then it would attempt downcasting on the boxed error,
    /// otherwise it will return [`Err`].
    ///
    /// If the custom boxed error has the same type as `E`, it will return [`Ok`],
    /// otherwise it will also return [`Err`].
    ///
    /// This method is meant to be a convenience routine for calling
    /// `Box<dyn Error + Sync + Send>::downcast` on the custom boxed error, returned by
    /// [`Error::into_inner`][into_inner].
    ///
    // FIXME(#74481): Hard-links required to link from `alloc` to `std` for incoherent method
    /// [into_inner]: struct.Error.html#method.into_inner
    ///
    /// # Examples
    ///
    /// ```
    /// use std::fmt;
    /// use std::io;
    /// use std::error::Error;
    ///
    /// #[derive(Debug)]
    /// enum E {
    ///     Io(io::Error),
    ///     SomeOtherVariant,
    /// }
    ///
    /// impl fmt::Display for E {
    ///    // ...
    /// #    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
    /// #        todo!()
    /// #    }
    /// }
    /// impl Error for E {}
    ///
    /// impl From<io::Error> for E {
    ///     fn from(err: io::Error) -> E {
    ///         err.downcast::<E>()
    ///             .unwrap_or_else(E::Io)
    ///     }
    /// }
    ///
    /// impl From<E> for io::Error {
    ///     fn from(err: E) -> io::Error {
    ///         match err {
    ///             E::Io(io_error) => io_error,
    ///             e => io::Error::new(io::ErrorKind::Other, e),
    ///         }
    ///     }
    /// }
    ///
    /// # fn main() {
    /// let e = E::SomeOtherVariant;
    /// // Convert it to an io::Error
    /// let io_error = io::Error::from(e);
    /// // Cast it back to the original variant
    /// let e = E::from(io_error);
    /// assert!(matches!(e, E::SomeOtherVariant));
    ///
    /// let io_error = io::Error::from(io::ErrorKind::AlreadyExists);
    /// // Convert it to E
    /// let e = E::from(io_error);
    /// // Cast it back to the original variant
    /// let io_error = io::Error::from(e);
    /// assert_eq!(io_error.kind(), io::ErrorKind::AlreadyExists);
    /// assert!(io_error.get_ref().is_none());
    /// assert!(io_error.raw_os_error().is_none());
    /// # }
    /// ```
    #[stable(feature = "io_error_downcast", since = "1.79.0")]
    #[rustc_allow_incoherent_impl]
    pub fn downcast<E>(self) -> result::Result<E, Self>
    where
        E: error::Error + Send + Sync + 'static,
    {
        if let Some(e) = self.get_ref()
            && e.is::<E>()
        {
            if let Some(b) = self.into_inner()
                && let Ok(err) = b.downcast::<E>()
            {
                Ok(*err)
            } else {
                // Safety: We have just checked that the condition is true
                unsafe { core::hint::unreachable_unchecked() }
            }
        } else {
            Err(self)
        }
    }
}

#[cfg(all(not(no_rc), not(no_sync), not(no_global_oom_handling)))]
#[stable(feature = "rust1", since = "1.0.0")]
impl From<crate::ffi::NulError> for Error {
    /// Converts a [`crate::ffi::NulError`] into a [`Error`].
    fn from(_: crate::ffi::NulError) -> Error {
        const_error!(ErrorKind::InvalidInput, "data provided contains a nul byte")
    }
}

#[stable(feature = "io_error_from_try_reserve", since = "1.78.0")]
impl From<crate::collections::TryReserveError> for Error {
    /// Converts `TryReserveError` to an error with [`ErrorKind::OutOfMemory`].
    ///
    /// `TryReserveError` won't be available as the error `source()`,
    /// but this may change in the future.
    fn from(_: crate::collections::TryReserveError) -> Error {
        // ErrorData::Custom allocates, which isn't great for handling OOM errors.
        ErrorKind::OutOfMemory.into()
    }
}

#[cfg(not(no_global_oom_handling))]
fn custom_owner_from_box(
    kind: ErrorKind,
    error: Box<dyn core::error::Error + Send + Sync>,
) -> CustomOwner {
    use core::ptr::NonNull;

    /// # Safety
    ///
    /// `ptr` must be valid to pass into `Box::from_raw`.
    unsafe fn drop_box_raw<T: ?Sized>(ptr: NonNull<T>) {
        // SAFETY
        // Caller ensures `ptr` is valid to pass into `Box::from_raw`.
        drop(unsafe { Box::from_non_null(ptr) })
    }

    let error = Box::into_non_null(error);

    // SAFETY:
    // * `error` is valid up to a static lifetime, and owns its pointee.
    // * `drop_box_raw` is safe to call for the pointer `error` exactly once.
    // * `drop_box_raw` is safe to call on a pointer to this instance of `Custom`,
    //   and will be stored in a `CustomOwner`.
    let custom = unsafe {
        Custom::from_raw(
            kind,
            error,
            drop_box_raw::<dyn error::Error + Send + Sync>,
            drop_box_raw::<Custom>,
        )
    };

    let custom = Box::into_non_null(Box::new(custom));

    // SAFETY: the `outer_drop` provided to `custom` is valid for itself.
    unsafe { CustomOwner::from_raw(custom) }
}
