#[cfg(test)]
mod tests {
    use pdfmake_rust::color::parse_color;

    #[test]
    fn test_hex_rgb() {
        let result = parse_color("#3498db");
        assert!(result.is_some());
        let (r, g, b) = result.unwrap();
        assert!((r - 0.204).abs() < 0.01);
        assert!((g - 0.596).abs() < 0.01);
        assert!((b - 0.859).abs() < 0.01);
    }

    #[test]
    fn test_hex_shorthand() {
        assert_eq!(parse_color("#fff"), Some((1.0, 1.0, 1.0)));
    }

    #[test]
    fn test_rgba() {
        let result = parse_color("rgba(52, 152, 219, 1.0)");
        assert!(result.is_some());
        let (r, g, b) = result.unwrap();
        assert!((r - 0.204).abs() < 0.01);
    }

    #[test]
    fn test_rgb() {
        let result = parse_color("rgb(52, 152, 219)");
        assert!(result.is_some());
    }

    #[test]
    fn test_color_names() {
        assert_eq!(parse_color("blue"), Some((0.0, 0.0, 1.0)));
        assert_eq!(parse_color("red"), Some((1.0, 0.0, 0.0)));
    }

    #[test]
    fn test_fill_none() {
        assert_eq!(parse_color("none"), None);
    }

    #[test]
    fn test_invalid() {
        assert_eq!(parse_color("invalid"), None);
    }

    #[test]
    fn test_color_to_pdf_rgb_delegates_to_parse_color() {
        use pdfmake_rust::color::color_to_pdf_rgb;
        assert_eq!(color_to_pdf_rgb("#FF0000"), Some((1.0, 0.0, 0.0)));
        assert_eq!(color_to_pdf_rgb("blue"), Some((0.0, 0.0, 1.0)));
        assert_eq!(color_to_pdf_rgb("none"), None);
    }

    #[test]
    fn text_with_color_emits_rg_instruction() {
        use pdfmake_rust::{Document, DocumentNode, PdfMake, Style, TextNode};

        let document = Document::builder()
            .content(DocumentNode::Text(
                TextNode::new("Red text").inline_style(Style::new().color("#FF0000")),
            ))
            .build();

        let bytes = PdfMake::new().render(&document).unwrap();
        let content = decompress_page_stream(&bytes);
        assert!(
            content.contains("1.000 0.000 0.000 rg"),
            "Expected '1.000 0.000 0.000 rg' in page stream, got:\n{}",
            content
        );
        assert!(
            content.contains("0.000 0.000 0.000 rg"),
            "Expected color reset '0.000 0.000 0.000 rg' in page stream"
        );
    }

    #[test]
    fn text_without_color_has_no_rg_instruction() {
        use pdfmake_rust::{Document, DocumentNode, PdfMake, TextNode};

        let document = Document::builder()
            .content(DocumentNode::text("Plain text"))
            .build();

        let bytes = PdfMake::new().render(&document).unwrap();
        let content = decompress_page_stream(&bytes);
        assert!(
            !content.contains(" rg\n"),
            "Plain text should not have color instructions, got:\n{}",
            content
        );
    }

    #[test]
    fn text_with_named_color_emits_rg_instruction() {
        use pdfmake_rust::{Document, DocumentNode, PdfMake, Style, TextNode};

        let document = Document::builder()
            .content(DocumentNode::Text(
                TextNode::new("Blue text").inline_style(Style::new().color("blue")),
            ))
            .build();

        let bytes = PdfMake::new().render(&document).unwrap();
        let content = decompress_page_stream(&bytes);
        assert!(
            content.contains("0.000 0.000 1.000 rg"),
            "Expected blue color '0.000 0.000 1.000 rg' in page stream, got:\n{}",
            content
        );
    }

    fn decompress_page_stream(pdf_bytes: &[u8]) -> String {
        use std::io::Read;

        let pdf_text = String::from_utf8_lossy(pdf_bytes);
        let stream_start_marker = "stream\n";

        let mut last_stream_data: Vec<u8> = Vec::new();
        let mut pos = 0;
        while let Some(idx) = pdf_text[pos..].find(stream_start_marker) {
            let abs_idx = pos + idx;
            let header = &pdf_text[pos..abs_idx];
            if header.contains("/Filter /FlateDecode") {
                let data_start = abs_idx + stream_start_marker.len();
                if let Some(end_idx) = pdf_text[data_start..].find("endstream") {
                    let compressed = &pdf_bytes[data_start..data_start + end_idx];
                    let mut decoder = flate2::read::ZlibDecoder::new(compressed);
                    let mut decompressed = Vec::new();
                    if decoder.read_to_end(&mut decompressed).is_ok() {
                        last_stream_data = decompressed;
                    }
                }
            }
            pos = abs_idx + stream_start_marker.len();
        }

        String::from_utf8_lossy(&last_stream_data).to_string()
    }
}