use crate::fmt::{Display, Formatter};
use crate::{Markup, markup};
use std::io;

/// It displays a type that implements [std::fmt::Display]
pub struct DebugDisplay<T>(pub T);

impl<T> Display for DebugDisplay<T>
where
    T: std::fmt::Display,
{
    fn fmt(&self, f: &mut Formatter<'_>) -> io::Result<()> {
        write!(f, "{}", self.0)
    }
}

/// It displays a `Option<T>`, where `T` implements [std::fmt::Display]
pub struct DisplayOption<T>(pub Option<T>);

impl<T> Display for DisplayOption<T>
where
    T: std::fmt::Display,
{
    fn fmt(&self, fmt: &mut Formatter) -> io::Result<()> {
        use crate as biome_console;

        if let Some(value) = &self.0 {
            markup!({ DebugDisplay(value) }).fmt(fmt)?;
        } else {
            markup!(<Dim>"unset"</Dim>).fmt(fmt)?;
        }
        Ok(())
    }
}

/// A horizontal line with the given print width
pub struct HorizontalLine {
    width: usize,
}

impl HorizontalLine {
    pub fn new(width: usize) -> Self {
        Self { width }
    }
}

impl Display for HorizontalLine {
    fn fmt(&self, fmt: &mut Formatter) -> io::Result<()> {
        fmt.write_str(&"\u{2501}".repeat(self.width))
    }
}

// It prints `\n`
pub struct Softline;

pub const SOFT_LINE: Softline = Softline;

impl Display for Softline {
    fn fmt(&self, fmt: &mut Formatter) -> io::Result<()> {
        fmt.write_str("\n")
    }
}

// It prints `\n\n`
pub struct Hardline;

pub const HARD_LINE: Hardline = Hardline;

impl Display for Hardline {
    fn fmt(&self, fmt: &mut Formatter) -> io::Result<()> {
        fmt.write_str("\n\n")
    }
}

/// It prints N whitespaces, where N is the `width` provided by [Padding::new]
pub struct Padding {
    width: usize,
}

impl Padding {
    pub fn new(width: usize) -> Self {
        Self { width }
    }
}

impl Display for Padding {
    fn fmt(&self, fmt: &mut Formatter) -> io::Result<()> {
        for _ in 0..self.width {
            fmt.write_str(" ")?;
        }
        Ok(())
    }
}

/// It writes a pair of key-value, with the given padding
pub struct KeyValuePair<'a>(pub &'a str, pub Markup<'a>);

impl Display for KeyValuePair<'_> {
    fn fmt(&self, fmt: &mut Formatter) -> io::Result<()> {
        let KeyValuePair(key, value) = self;
        write!(fmt, "  {key}:")?;

        let padding_width = 30usize.saturating_sub(key.len() + 1);

        for _ in 0..padding_width {
            fmt.write_str(" ")?;
        }

        value.fmt(fmt)?;

        fmt.write_str("\n")
    }
}
