//! The pure generators rebuild the index, index-strings, and CSS assets.

mod common;

use common::{default_sorted_sequences, golden, VERSIONS};
use emoji_test_regex_pattern::generate::{
    generate_css_unicode_range, generate_index, generate_index_strings,
};
use emoji_test_regex_pattern::OutputKind;
use pretty_assertions::assert_eq;

#[test]
fn generate_index_reproduces_golden() {
    for &dir in VERSIONS {
        let sorted = default_sorted_sequences(dir);
        let produced = generate_index(&sorted);
        assert_eq!(produced, golden(dir, OutputKind::Index), "index for {dir}");
    }
}

#[test]
fn generate_index_strings_reproduces_golden() {
    for &dir in VERSIONS {
        let sorted = default_sorted_sequences(dir);
        let produced = generate_index_strings(&sorted);
        assert_eq!(
            produced,
            golden(dir, OutputKind::IndexStrings),
            "index-strings for {dir}"
        );
    }
}

#[test]
fn generate_css_reproduces_golden() {
    for &dir in VERSIONS {
        let sequences = default_sorted_sequences(dir);
        // The CSS asset is a code-point set, so any order works. The build
        // appends the trailing newline.
        let produced = format!("{}\n", generate_css_unicode_range(&sequences));
        assert_eq!(produced, golden(dir, OutputKind::Css), "css for {dir}");
    }
}

#[test]
fn generate_index_formats_code_points() {
    // Zero-pads to four hex digits, uppercase, no padding past four. Astral base
    // stays five digits. Separator is space arrow space.
    let input = ["#\u{20E3}".to_owned(), "\u{1F004}\u{FE0F}".to_owned()];
    let out = generate_index(&input);
    assert_eq!(
        out,
        "U+0023 U+20E3 \u{2192} #\u{20E3}\nU+1F004 U+FE0F \u{2192} \u{1F004}\u{FE0F}\n"
    );
}

#[test]
fn generate_css_singleton_and_range() {
    // The digits 0 through 9 (U+30..=U+39) coalesce into one range. The lone
    // keycap combiner stays a singleton.
    let input = ["0123456789\u{20E3}".to_owned()];
    let out = generate_css_unicode_range(&input);
    assert_eq!(out, "U+30-39,U+20E3");
}

#[test]
fn generate_css_uppercase_no_padding() {
    let input = ["\u{A9}".to_owned(), "\u{1F004}".to_owned()];
    let out = generate_css_unicode_range(&input);
    assert_eq!(out, "U+A9,U+1F004");
}

#[test]
fn generate_index_strings_is_join_plus_newline() {
    let input = ["a".to_owned(), "b".to_owned()];
    assert_eq!(generate_index_strings(&input), "a\nb\n");
}

#[test]
fn generators_keep_duplicate_input_lines() {
    // The build never deduplicates. An extra sequence that also appears in a
    // version's own list ends up in the input twice, so both assets carry two
    // identical lines. A generator that collapsed duplicates would drop lines
    // the golden files require.
    let handshake = "\u{1F91D}\u{1F3FB}".to_owned();
    let input = [handshake.clone(), handshake.clone()];

    assert_eq!(
        generate_index_strings(&input),
        format!("{handshake}\n{handshake}\n")
    );
    assert_eq!(
        generate_index(&input),
        "U+1F91D U+1F3FB \u{2192} \u{1F91D}\u{1F3FB}\n\
         U+1F91D U+1F3FB \u{2192} \u{1F91D}\u{1F3FB}\n"
    );
}

#[test]
fn latest_golden_carries_duplicate_lines() {
    // Ground the duplicate behavior in real data. The highest version lists the
    // handshake and wrestler skin-tone sequences in its own set, and the extra
    // sequences add them again. Both index assets keep the repeats.
    let index_strings = golden("emoji-17.0", OutputKind::IndexStrings);
    let handshake_line = "\u{1F91D}\u{1F3FB}\n";
    let count = index_strings.matches(handshake_line).count();
    assert_eq!(count, 2, "handshake light-skin should appear twice");

    let index = golden("emoji-17.0", OutputKind::Index);
    let index_line = "U+1F91D U+1F3FB \u{2192} \u{1F91D}\u{1F3FB}\n";
    assert_eq!(
        index.matches(index_line).count(),
        2,
        "handshake light-skin index line should appear twice"
    );
}

#[test]
fn generators_handle_empty_input() {
    let empty: [String; 0] = [];
    assert_eq!(generate_index(&empty), "");
    assert_eq!(generate_index_strings(&empty), "");
    assert_eq!(generate_css_unicode_range(&empty), "");
}

#[test]
fn generate_index_handles_empty_string_element() {
    // An empty string has no code points, so the id is empty. The line is the
    // separator with empty id and empty emoji, then a newline.
    let input = [String::new()];
    assert_eq!(generate_index(&input), " \u{2192} \n");
    assert_eq!(generate_index_strings(&input), "\n");
    assert_eq!(generate_css_unicode_range(&input), "");
}

#[test]
fn generate_css_is_input_order_independent() {
    // The CSS asset is a code-point set. Reversing the input must not change the
    // output. The build passes the unsorted sequence list, so order cannot
    // matter.
    for &dir in VERSIONS {
        let mut forward = default_sorted_sequences(dir);
        let out_forward = generate_css_unicode_range(&forward);
        forward.reverse();
        let out_reversed = generate_css_unicode_range(&forward);
        assert_eq!(out_forward, out_reversed, "css order dependence in {dir}");
    }
}
