use std::collections::BTreeMap;
use std::collections::HashMap;

pub const MAIN: &str = include_str!("../assets/internal/gen/sources/main.json");
pub const US: &str = include_str!("../assets/internal/gen/sources/us.json");
pub const UK: &str = include_str!("../assets/internal/gen/sources/uk.json");

#[test]
fn codegen() {
    let mut content = vec![];
    generate(&mut content);

    let content = String::from_utf8(content).unwrap();
    let content = codegenrs::rustfmt(&content, None).unwrap();
    snapbox::assert_data_eq!(content, snapbox::file!["../src/dict_codegen.rs"].raw());
}

#[test]
fn compat() {
    use std::fmt::Write as _;

    let mut content = String::new();
    let words = parse_dict();
    let mut inverted_words = BTreeMap::new();
    for (good, bads) in words.main {
        for bad in bads {
            inverted_words
                .entry(bad)
                .or_insert_with(Vec::new)
                .push(good.clone());
        }
    }
    for (bad, mut good) in inverted_words {
        if !is_word(&bad) {
            continue;
        }
        if !good.iter().all(|s| is_word(s)) {
            continue;
        }
        good.sort();
        let bad = bad.to_lowercase();
        write!(content, "{bad}").unwrap();
        for good in good {
            let good = good.to_lowercase();
            write!(content, ",{good}").unwrap();
        }
        writeln!(content).unwrap();
    }

    snapbox::assert_data_eq!(content, snapbox::file!["../assets/compatible.csv"].raw());
}

fn is_word(word: &str) -> bool {
    let tokenizer = typos::tokens::Tokenizer::new();

    tokenizer.parse_str(word).flat_map(|t| t.split()).count() == 1 && !word.contains('_')
}

fn generate<W: std::io::Write>(file: &mut W) {
    writeln!(
        file,
        "// This file is @generated by {}",
        file!().replace('\\', "/")
    )
    .unwrap();
    writeln!(file).unwrap();

    let Words {
        main,
        american,
        british,
    } = parse_dict();

    dictgen::DictGen::new()
        .name("MAIN_DICTIONARY")
        .value_type("&[&str]")
        .ordered_map()
        .write(
            file,
            main.into_iter().map(|kv| (kv.0, format!("&{:?}", kv.1))),
        )
        .unwrap();

    dictgen::DictGen::new()
        .name("AMERICAN_DICTIONARY")
        .value_type("&[&str]")
        .ordered_map()
        .write(
            file,
            american
                .into_iter()
                .map(|kv| (kv.0, format!("&{:?}", kv.1))),
        )
        .unwrap();

    dictgen::DictGen::new()
        .name("BRITISH_DICTIONARY")
        .value_type("&[&str]")
        .ordered_map()
        .write(
            file,
            british.into_iter().map(|kv| (kv.0, format!("&{:?}", kv.1))),
        )
        .unwrap();
}

struct Words {
    main: HashMap<String, Vec<String>>,
    american: HashMap<String, Vec<String>>,
    british: HashMap<String, Vec<String>>,
}

fn parse_dict() -> Words {
    Words {
        main: serde_json::from_str(MAIN).unwrap(),
        american: serde_json::from_str(US).unwrap(),
        british: serde_json::from_str(UK).unwrap(),
    }
}
