pub use crate::core::{display::strong, information::Information, should::Should};
pub use std::fmt::Debug;

/// The inner should implementation, not intended to be used by the final user.
#[derive(Debug)]
pub struct ShouldImpl<L>
where
    L: Debug,
{
    /// The left part of the assertion.
    pub left: Option<L>,

    /// If the assertion must be either true or false.
    pub(crate) truthness: bool,

    /// The infos needed to the right formatting of the final message.
    pub(crate) info: Information,
}

impl<L> ShouldImpl<L>
where
    L: Debug,
{
    /// Creates a new `ShouldImpl` by applicating the passed closure to the
    /// left element.
    pub fn map_left<T: Debug, F>(self, f: F) -> ShouldImpl<T>
    where
        F: FnOnce(L) -> Option<T>,
    {
        let ShouldImpl {
            left,
            truthness,
            info,
        } = self;

        let left = left.and_then(f);

        ShouldImpl {
            left,
            truthness,
            info,
        }
    }

    /// Returns wether the assertion should be true or not.
    pub fn truthness(&self) -> bool {
        self.truthness
    }

    /// Returns a debug reptresentation of the left element.
    pub fn left_dbg(&self) -> &str {
        &self.info.left_dbg
    }

    /// Returns a stringified representation of the left element.
    pub fn stringified(&self) -> Option<String> {
        self.info
            .from_macro
            .as_ref()
            .map(|x| strong(x.stringified).to_string())
    }

    /// Adds a new explanation message to the assertion. Must be used through
    /// the `ConsumingAssert::because` or the `ChainableAssert::because` method.
    pub fn add_message(&mut self, message: String) {
        self.info.messages.push(message);
    }
}

impl<L> Default for ShouldImpl<L>
where
    L: Debug,
{
    fn default() -> Self {
        ShouldImpl {
            left: None,
            truthness: true,
            info: Information::default(),
        }
    }
}

impl<L> From<ShouldImpl<L>> for Should<L>
where
    L: Debug,
{
    fn from(implem: ShouldImpl<L>) -> Self {
        Should(implem)
    }
}

impl<L> From<Should<L>> for ShouldImpl<L>
where
    L: Debug,
{
    fn from(should: Should<L>) -> Self {
        should.0
    }
}

impl<'a, L> From<&'a mut Should<L>> for &'a mut ShouldImpl<L>
where
    L: Debug,
{
    fn from(should: &'a mut Should<L>) -> Self {
        &mut should.0
    }
}
