//! Verifica que un párrafo largo, dentro de un bloque angosto, reserva el
//! alto de **varias líneas** (no se aplasta en una). Es el regresor del bug
//! "textos aplastados" de puriy: sin medición con parley, taffy le daba a la
//! hoja de texto una sola línea de alto y las líneas envueltas se solapaban.

use llimphi_compositor::{measure_text_node, mount, View};
use llimphi_layout::taffy::prelude::*;
use llimphi_layout::taffy::Size as TSize;
use llimphi_layout::LayoutTree;

#[derive(Clone)]
enum Msg {}

#[test]
fn parrafo_largo_reserva_varias_lineas() {
    // Bloque de 200px de ancho con un párrafo que claramente excede una línea.
    let texto = "Lorem ipsum dolor sit amet consectetur adipiscing elit sed do \
                 eiusmod tempor incididunt ut labore et dolore magna aliqua ut \
                 enim ad minim veniam quis nostrud exercitation ullamco laboris.";
    let block: View<Msg> = View::new(Style {
        size: TSize { width: length(200.0_f32), height: auto() },
        flex_direction: FlexDirection::Row,
        flex_wrap: FlexWrap::Wrap,
        ..Default::default()
    })
    .children(vec![View::new(Style {
        size: TSize { width: auto(), height: auto() },
        flex_shrink: 1.0,
        ..Default::default()
    })
    .text_aligned(texto, 16.0_f32, vello::peniko::Color::BLACK, llimphi_text::Alignment::Start)]);

    let mut layout = LayoutTree::new();
    let mounted = mount(&mut layout, block);
    let mut ts = llimphi_text::Typesetter::new();
    let tmap = &mounted.text_measures;
    assert_eq!(tmap.len(), 1, "debería haber exactamente una hoja de texto");

    let computed = layout
        .compute_with_measure(mounted.root, (800.0, 600.0), |nid, known, avail| match tmap.get(&nid)
        {
            Some(tm) => measure_text_node(&mut ts, tm, known, avail),
            None => TSize::ZERO,
        })
        .expect("layout");

    // El nodo de texto es el segundo en orden DFS (root, luego la hoja).
    let leaf_id = mounted.nodes[1].id;
    let rect = computed.get(leaf_id).expect("rect de la hoja");
    // A 16px y ~1.2 de interlínea, una línea ≈ 19px. Con ~150px de texto en
    // 200px de ancho deberían ser >= 4 líneas → bastante más de una.
    assert!(
        rect.h > 40.0,
        "el párrafo se aplastó: alto={} (esperaba varias líneas)",
        rect.h
    );
    assert!(rect.w <= 200.0 + 1.0, "no debería exceder el ancho del bloque");
}

#[test]
fn no_wrap_mide_una_sola_linea_fase_7_1253() {
    use llimphi_layout::taffy::AvailableSpace;
    // Mismo texto largo, mismo ancho disponible (angosto): con `no_wrap` se
    // mide en una sola línea (ancho completo, ignora el available); sin él,
    // envuelve (más alto, ancho acotado al disponible).
    let texto = "una linea larga que normalmente envuelve en varios renglones \
                 cuando el ancho disponible es angosto de verdad";
    let mk = |no_wrap: bool| llimphi_compositor::TextMeasure {
        content: texto.to_string(),
        size_px: 16.0,
        alignment: llimphi_text::Alignment::Start,
        italic: false,
        font_family: None,
        line_height: 1.2,
        weight: 400.0,
        max_lines: None,
        ellipsis: false,
        underline: false,
        strikethrough: false,
        spans: None,
        letter_spacing: 0.0,
        word_spacing: 0.0,
        no_wrap,
        overflow_wrap: false,
    };
    let mut ts = llimphi_text::Typesetter::new();
    let known = TSize { width: None, height: None };
    let avail = TSize {
        width: AvailableSpace::Definite(160.0),
        height: AvailableSpace::MaxContent,
    };
    let env = measure_text_node(&mut ts, &mk(false), known, avail);
    let nw = measure_text_node(&mut ts, &mk(true), known, avail);
    // Envuelto: ancho acotado al disponible y alto de varias líneas.
    assert!(env.width <= 160.0 + 1.0, "wrap acota el ancho: {}", env.width);
    assert!(env.height > 40.0, "wrap reserva varias líneas: {}", env.height);
    // no_wrap: una sola línea → mucho más ancho que el disponible y bajo.
    assert!(nw.width > 160.0, "no_wrap mide ancho completo: {}", nw.width);
    assert!(
        nw.height < env.height,
        "no_wrap es una línea (más bajo que el envuelto): nw={} env={}",
        nw.height,
        env.height
    );
}

#[test]
fn line_height_mayor_reserva_mas_alto() {
    let texto = "una línea de texto que envuelve en dos o tres renglones según \
                 el ancho disponible para el bloque contenedor angosto";
    let medir = |lh: f32| -> f32 {
        let mut ts = llimphi_text::Typesetter::new();
        let tm = llimphi_compositor::TextMeasure {
            content: texto.to_string(),
            size_px: 16.0,
            alignment: llimphi_text::Alignment::Start,
            italic: false,
            font_family: None,
            line_height: lh,
            weight: 400.0,
            max_lines: None,
            ellipsis: false,
            underline: false,
            strikethrough: false,
            spans: None,
            letter_spacing: 0.0,
            word_spacing: 0.0,
            no_wrap: false,
            overflow_wrap: false,
        };
        let known = TSize { width: Some(180.0_f32), height: None };
        let avail = TSize {
            width: AvailableSpace::Definite(180.0),
            height: AvailableSpace::MaxContent,
        };
        measure_text_node(&mut ts, &tm, known, avail).height
    };
    let compacto = medir(1.0);
    let comodo = medir(2.0);
    assert!(
        comodo > compacto * 1.5,
        "line-height: 2 debería reservar bastante más alto que 1.0 (got {compacto} vs {comodo})"
    );
}

#[test]
fn overflow_wrap_parte_la_palabra_larga_fase_7_1254() {
    use llimphi_layout::taffy::AvailableSpace;
    // Una sola palabra sin espacios, más ancha que la caja angosta. Sin
    // `overflow-wrap` parley la deja desbordar (mide más ancho que la caja);
    // con `overflow-wrap` la parte para que entre (ancho acotado, varias líneas
    // ⇒ más alto). Es el regresor directo de la Fase 7.1254.
    let palabrota = "supercalifragilisticoexpialidosoineluctableantidisestablishmentariano";
    let mk = |overflow_wrap: bool| llimphi_compositor::TextMeasure {
        content: palabrota.to_string(),
        size_px: 16.0,
        alignment: llimphi_text::Alignment::Start,
        italic: false,
        font_family: None,
        line_height: 1.2,
        weight: 400.0,
        max_lines: None,
        ellipsis: false,
        underline: false,
        strikethrough: false,
        spans: None,
        letter_spacing: 0.0,
        word_spacing: 0.0,
        no_wrap: false,
        overflow_wrap,
    };
    let mut ts = llimphi_text::Typesetter::new();
    let known = TSize { width: None, height: None };
    let avail = TSize {
        width: AvailableSpace::Definite(80.0),
        height: AvailableSpace::MaxContent,
    };
    let normal = measure_text_node(&mut ts, &mk(false), known, avail);
    let roto = measure_text_node(&mut ts, &mk(true), known, avail);
    // Sin overflow-wrap: la palabra desborda → ancho mayor que la caja.
    assert!(
        normal.width > 80.0,
        "sin overflow-wrap la palabra desborda: {}",
        normal.width
    );
    // Con overflow-wrap: se parte → ancho acotado a la caja y más alto.
    assert!(
        roto.width <= 80.0 + 1.0,
        "overflow-wrap acota el ancho a la caja: {}",
        roto.width
    );
    assert!(
        roto.height > normal.height,
        "overflow-wrap parte en varias líneas (más alto): roto={} normal={}",
        roto.height,
        normal.height
    );
}
