use std::io::{self, Write};

use crate::env::RenderCtx;
use crate::render::{frame, traceback};

pub fn render(text: &str, ctx: &RenderCtx, w: &mut impl Write) -> io::Result<()> {
    for line in text.split_inclusive('\n') {
        let line = line.trim_end_matches('\n');
        // Program output (colorama/rich/pytest/tqdm) can embed raw ANSI. With
        // color off, strip it so escapes don't leak into the box or piped output.
        if ctx.use_color {
            frame::wrap_line(line, ctx, w)?;
        } else {
            frame::wrap_line(&traceback::strip_ansi_pub(line), ctx, w)?;
        }
    }
    if text.is_empty() {
        frame::wrap_line("", ctx, w)?;
    }
    Ok(())
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::env::RenderCtx;

    fn ctx() -> RenderCtx {
        crate::render::test_support::width(30)
    }

    #[test]
    fn renders_single_line_in_box_line() {
        let mut buf = Vec::new();
        render("hello", &ctx(), &mut buf).unwrap();
        let s = String::from_utf8(buf).unwrap();
        assert!(s.contains("hello"));
        assert!(s.starts_with("│ "));
    }

    #[test]
    fn renders_multiline_as_multiple_box_lines() {
        let mut buf = Vec::new();
        render("a\nb\nc", &ctx(), &mut buf).unwrap();
        let s = String::from_utf8(buf).unwrap();
        assert_eq!(s.matches('\n').count(), 3);
    }

    #[test]
    fn trailing_newline_does_not_create_empty_line() {
        let mut buf = Vec::new();
        render("a\n", &ctx(), &mut buf).unwrap();
        let s = String::from_utf8(buf).unwrap();
        assert_eq!(s.matches('\n').count(), 1);
    }

    #[test]
    fn strips_embedded_ansi_when_no_color() {
        // Program output (colorama/rich/pytest) often carries raw ANSI. With
        // color off, those escapes must not leak into the rendered box.
        let mut buf = Vec::new();
        render("\x1b[31mred\x1b[0m normal", &ctx(), &mut buf).unwrap();
        let s = String::from_utf8(buf).unwrap();
        assert!(
            !s.contains('\x1b'),
            "no-color output must not contain ANSI escapes; got {s:?}"
        );
        assert!(s.contains("red"));
        assert!(s.contains("normal"));
    }

    #[test]
    fn preserves_embedded_ansi_when_color() {
        let ctx = RenderCtx {
            use_color: true,
            ..ctx()
        };
        let mut buf = Vec::new();
        render("\x1b[31mred\x1b[0m", &ctx, &mut buf).unwrap();
        let s = String::from_utf8(buf).unwrap();
        assert!(
            s.contains("\x1b[31m"),
            "color on must keep escapes; got {s:?}"
        );
    }
}
