use super::*;

#[test]
fn rendered_line_cache_reuses_until_context_changes() {
    let mut cache = RenderedLineCache::default();
    let request = RenderRequest {
        context: RenderContext {
            gutter: GutterLayout::new(1, false),
            x: 0,
            width: 3,
            wrap: false,
            mode: FormatKind::Json,
        },
        row_limit: 8,
    };

    let first = {
        let rows = cache.get_or_render("abcdef", 1, request);
        span_text(&rows[0].spans)
    };
    assert_eq!(first, "1 │ abc");

    cache.get_or_render("abcdef", 1, request);
    assert_eq!(cache.lines.len(), 1);

    let shifted = RenderRequest {
        context: RenderContext {
            x: 2,
            ..request.context
        },
        ..request
    };
    let second = {
        let rows = cache.get_or_render("abcdef", 1, shifted);
        span_text(&rows[0].spans)
    };

    assert_eq!(second, "1 │ cde");
    assert_eq!(cache.lines.len(), 1);
}

#[test]
fn wrapped_render_cache_reuses_adjacent_rows_from_chunk() {
    let mut cache = RenderedLineCache::default();
    let request = RenderRequest {
        context: RenderContext {
            gutter: GutterLayout::new(1, false),
            x: 0,
            width: 4,
            wrap: true,
            mode: FormatKind::Json,
        },
        row_limit: 8,
    };
    let line = "a".repeat(4096);

    let first = cache.get_or_render_window(&line, 1, 100, 2, request);
    assert_eq!(first.len(), 2);
    assert_eq!(cache.lines.get(&1).unwrap().chunks.len(), 1);

    let second = cache.get_or_render_window(&line, 1, 101, 2, request);
    assert_eq!(second.len(), 2);
    assert_eq!(cache.lines.get(&1).unwrap().chunks.len(), 1);
}

#[test]
fn wrapped_render_cache_records_deep_checkpoints() {
    let mut cache = RenderedLineCache::default();
    let request = RenderRequest {
        context: RenderContext {
            gutter: GutterLayout::new(1, false),
            x: 0,
            width: 16,
            wrap: true,
            mode: FormatKind::Json,
        },
        row_limit: 8,
    };
    let line = format!(
        r#"  "xml": "<root>{}</root>""#,
        r#"<item><name>visible</name></item>"#.repeat(2_000)
    );

    let rows = cache.get_or_render_window(&line, 1, 3_000, 4, request);
    assert_eq!(rows.len(), 4);

    let cached = cache.lines.get(&1).unwrap();
    assert!(
        cached.index.wrap.checkpoints.len() > 4,
        "deep wrapped render should leave reusable row checkpoints"
    );
    assert!(
        !cached.index.highlight.json_value_strings.is_empty(),
        "deep JSON string render should leave XML state checkpoints"
    );

    let checkpointed =
        cache.get_or_render_window(&line, 1, 3_000 + WRAP_RENDER_CHUNK_ROWS + 8, 4, request);
    assert_eq!(checkpointed.len(), 4);
}

#[test]
fn wrapped_deep_window_keeps_embedded_xml_pair_colors() {
    let mut cache = RenderedLineCache::default();
    let request = RenderRequest {
        context: RenderContext {
            gutter: GutterLayout::new(1, false),
            x: 0,
            width: 12,
            wrap: true,
            mode: FormatKind::Json,
        },
        row_limit: 8,
    };
    let line = format!(
        r#"  "xml": "{}<root><child>visible</child></root>""#,
        "x".repeat(480)
    );
    let row_start = wrap_ranges(
        &line,
        request.context.width,
        continuation_indent(&line, request.context.width),
        80,
    )
    .iter()
    .position(|range| line[range.start_byte..range.end_byte].contains("<child>"))
    .unwrap();

    let rows = cache.get_or_render_window(&line, 1, row_start, 3, request);
    let spans = rows
        .iter()
        .flat_map(|row| row.line.spans.iter().cloned())
        .collect::<Vec<_>>();
    let child_styles = styles_for_text(&spans, "child");

    assert_eq!(child_styles.len(), 2);
    assert_eq!(child_styles[0], child_styles[1]);
}

#[test]
fn wrapped_deep_window_keeps_prefix_xml_state_for_visible_close_tag() {
    let mut cache = RenderedLineCache::default();
    let request = RenderRequest {
        context: RenderContext {
            gutter: GutterLayout::new(1, false),
            x: 0,
            width: 12,
            wrap: true,
            mode: FormatKind::Json,
        },
        row_limit: 8,
    };
    let line = format!(
        r#"  "xml": "<root><child>{}</child></root>""#,
        "x".repeat(480)
    );
    let row_start = wrap_ranges(
        &line,
        request.context.width,
        continuation_indent(&line, request.context.width),
        120,
    )
    .iter()
    .position(|range| line[range.start_byte..range.end_byte].contains("</child>"))
    .unwrap();

    let rows = cache.get_or_render_window(&line, 1, row_start, 2, request);
    let spans = rows
        .iter()
        .flat_map(|row| row.line.spans.iter().cloned())
        .collect::<Vec<_>>();
    let child_styles = styles_for_text(&spans, "child");

    assert_eq!(child_styles, vec![xml_depth_style(1)]);
}
