use crate::{
    CharStringExt, Lint, Token, TokenStringExt,
    linting::{LintKind, Suggestion},
};

pub fn match_to_lint_four_digits(
    toks: &[Token],
    src: &[char],
    decade: &[char],
    suffix: &[char],
    pre: Option<&[Token]>,
    post: Option<&[Token]>,
) -> Option<Lint> {
    enum UsageJudgment {
        NotMistake,
        IsMistake,
        Unsure,
    }

    struct Context<'a> {
        sep_is_hyphen: bool,
        word: &'a [char],
    }

    let before = if pre.is_some_and(|b| b.len() >= 2)
        && let [.., pw, psep] = pre.unwrap()
        && (psep.kind.is_whitespace() || psep.kind.is_hyphen())
        && pw.kind.is_word()
    {
        Some(Context {
            sep_is_hyphen: psep.kind.is_hyphen(),
            word: pw.get_ch(src),
        })
    } else {
        None
    };

    let after = if post.is_some_and(|a| a.len() >= 2)
        && let [nsep, nw, ..] = post.unwrap()
        && (nsep.kind.is_whitespace() || nsep.kind.is_hyphen())
        && nw.kind.is_word()
    {
        Some(Context {
            sep_is_hyphen: nsep.kind.is_hyphen(),
            word: nw.get_ch(src),
        })
    } else {
        None
    };

    let judgment = match (&before, &after) {
        // Words before the decade which suggest the apostrophe is a mistake
        (Some(before), _)
            if before
                .word
                .eq_any_ignore_ascii_case_str(&["early", "mid", "late"]) =>
        {
            UsageJudgment::IsMistake
        }
        // Hyphen before suggests username, not a mistake
        (Some(before), _) if before.sep_is_hyphen => UsageJudgment::NotMistake,
        // "style" after the decade suggests the apostrophe is a mistake
        (_, Some(after)) if after.word.eq_str("style") => UsageJudgment::IsMistake,
        (Some(before), _) if !before.sep_is_hyphen && before.word.eq_str("the") => {
            // Go back one more word and look for "in the" before the decade
            if let [.., ppw, ppsep, _, _] = pre.unwrap()
                && ppsep.kind.is_whitespace()
                && ppw.kind.is_preposition()
            {
                UsageJudgment::IsMistake
            } else {
                UsageJudgment::Unsure
            }
        }
        _ => UsageJudgment::Unsure,
    };

    if !matches!(judgment, UsageJudgment::IsMistake) {
        return None;
    }

    Some(Lint {
        span: toks.span()?,
        lint_kind: LintKind::Usage,
        message: "Plural decades do not use an apostrophe before the `s`".to_owned(),
        suggestions: vec![Suggestion::ReplaceWith([decade, suffix].concat())],
        ..Default::default()
    })
}

#[cfg(test)]
mod lints {
    use crate::linting::pooled_linter::for_tests::create_test_pool;

    create_test_pool!(PluralDecades, PluralDecades, PluralDecades::default());
    use super::super::PluralDecades;
    use crate::linting::tests::{assert_lint_count, assert_no_lints, assert_suggestion_result};

    // Made-up examples

    #[test]
    fn eighties() {
        assert_lint_count("in the 1980's", test_linter(), 1);
    }

    #[test]
    #[ignore = "wip"]
    fn nineties() {
        assert_lint_count("the 1990’s were a bit grungy", test_linter(), 1);
    }

    #[test]
    fn dont_flag_three_digits() {
        assert_lint_count("200's doesn't look like a decade", test_linter(), 0);
    }

    #[test]
    fn dont_flag_five_digits() {
        assert_lint_count("20000's doesn't look like a decade", test_linter(), 0);
    }

    #[test]
    fn dont_flag_with_thousands_separator() {
        assert_lint_count("Nobody says \"in the 1,950's\".", test_linter(), 0);
    }

    #[test]
    fn dont_flag_not_ending_with_0() {
        assert_lint_count("1977's best month was October", test_linter(), 0);
    }

    // Real-world examples using sentences found on GitHub

    // 1900s (2 examples)

    #[test]
    #[ignore = "Too ambiguous to lint?"]
    // 1900 is probably a username, but otherwise it looks like a decade with the common apostrophe error
    fn dont_flag_ambiguous_1900s_nppl() {
        assert_no_lints(
            "star and fork 1900's gists by creating an account on GitHub.",
            test_linter(),
        );
    }

    #[test]
    #[ignore = "wip"]
    fn fix_in_1900s_npsg() {
        assert_suggestion_result(
            "Children Aged 0-4 in 1900's Norway.",
            test_linter(),
            "Children Aged 0-4 in 1900s Norway.",
        );
    }

    // 1910s (1 example)

    #[test]
    #[ignore = "Looks like a product name, not a decade"]
    fn ignore_hp_1910s() {
        assert_no_lints("Add support for HP 1910's", test_linter());
    }

    // 1920s (3 examples)

    #[test]
    #[ignore = "wip"]
    fn fix_the_roaring_1920s() {
        assert_suggestion_result(
            "The \"Roaring 1920's\" wasn't just about the economy and technology.",
            test_linter(),
            "The \"Roaring 1920s\" wasn't just about the economy and technology.",
        );
    }

    #[test]
    #[ignore = "wip"]
    fn fix_special_1920s_touch() {
        assert_suggestion_result(
            "It is beautiful and easily readable with that special 1920's touch.",
            test_linter(),
            "It is beautiful and easily readable with that special 1920s touch.",
        );
    }

    #[test]
    fn fix_in_the_1920s() {
        assert_suggestion_result(
            "Sir Josiah Stamp, president of the Bank of England and the second richest man in Britain in the 1920's, speaking at the University of Texas in 1927.",
            test_linter(),
            "Sir Josiah Stamp, president of the Bank of England and the second richest man in Britain in the 1920s, speaking at the University of Texas in 1927.",
        );
    }

    // 1950s (7 examples)

    #[test]
    #[ignore = "Grammar would be correct but the computer is from 1951 so must be a mistake for 1950s"]
    fn dont_flag_ambiguous_1950s_npsg() {
        assert_no_lints(
            "Simulator for 1950's MIT Whirlwind Computer.",
            test_linter(),
        );
    }

    #[test]
    fn fix_in_the_1950s() {
        assert_suggestion_result(
            "Using the sandbox on the right, write and execute a query to return people born in the 1950's (1950 - 1959)",
            test_linter(),
            "Using the sandbox on the right, write and execute a query to return people born in the 1950s (1950 - 1959)",
        );
    }

    #[test]
    #[ignore = "wip"]
    fn fix_a_adj_1950s_npsg() {
        assert_suggestion_result(
            "Wave digital filter based emulation of a famous 1950's tube stereo limiter.",
            test_linter(),
            "Wave digital filter based emulation of a famous 1950s tube stereo limiter.",
        );
    }

    #[test]
    #[ignore = "wip"]
    fn fix_1950s_npsg() {
        assert_suggestion_result(
            "1950's elevator randomly gets stuck",
            test_linter(),
            "1950s elevator randomly gets stuck",
        );
    }

    #[test]
    #[ignore = "wip"]
    fn fix_from_1950s_npsg() {
        assert_suggestion_result(
            "documenting my family's camera business, from 1950's England, run by my father",
            test_linter(),
            "documenting my family's camera business, from 1950s England, run by my father",
        );
    }

    #[test]
    fn fix_from_1950() {
        assert_suggestion_result(
            "Plot the top ten most common baby names for New South Wales by year from the 1950's",
            test_linter(),
            "Plot the top ten most common baby names for New South Wales by year from the 1950s",
        );
    }

    #[test]
    #[ignore = "wip"]
    fn fix_1950s_brick_built_house() {
        assert_suggestion_result(
            "We live in a 3 bedroom 1950's brick built house in the UK.",
            test_linter(),
            "We live in a 3 bedroom 1950s brick built house in the UK.",
        );
    }

    // 1960s (4 examples)

    #[test]
    #[ignore = "wip"]
    fn fix_a_adj_1960s_npsg() {
        assert_suggestion_result(
            "Emulating a rare 1960's educational computer.",
            test_linter(),
            "Emulating a rare 1960s educational computer.",
        );
    }

    #[test]
    #[ignore = "Ambiguous - could be referring to the specific year 1960 or the decade 1960s"]
    fn ignore_ambiguous_1960s_npsg() {
        assert_no_lints("MyTheil - 1960's IP Sprint Hillarity.", test_linter());
    }

    #[test]
    #[ignore = "wip"]
    fn fix_1960s_npsg() {
        assert_suggestion_result(
            "Punchbag game inspired by 1960's TV Show Batman!",
            test_linter(),
            "Punchbag game inspired by 1960s TV Show Batman!",
        );
    }

    #[test]
    #[ignore = "ambiguous, not sure what it means"]
    fn ignore_in_1960s_aperture() {
        assert_no_lints(
            "Several \"SP entrances\" in 1960's Aperture have visible nodraw around entrance door",
            test_linter(),
        );
    }

    // 1970s (11 examples)

    #[test]
    #[ignore = "wip"]
    fn fix_1970s_npsg() {
        assert_suggestion_result(
            "1970's chess engine CHEKMO-II + UCI adapter.",
            test_linter(),
            "1970s chess engine CHEKMO-II + UCI adapter.",
        );
    }

    #[test]
    #[ignore = "wip"]
    fn fix_vprog_1970s_nppl_dates() {
        assert_suggestion_result(
            "listsockets printing 1970's dates.",
            test_linter(),
            "listsockets printing 1970s dates.",
        );
    }

    #[test]
    fn fix_in_a_1970s_style_npsg() {
        assert_suggestion_result(
            "I tried to create some catwalk in a 1970's style level.",
            test_linter(),
            "I tried to create some catwalk in a 1970s style level.",
        );
    }

    #[test]
    #[ignore = "Grammar would be correct but Pong is from 1972 so must be a mistake for 1970s"]
    fn fix_1970s_pong_game() {
        assert_suggestion_result(
            "1970's Pong game rewritten in C++.",
            test_linter(),
            "1970s Pong game rewritten in C++.",
        );
    }

    #[test]
    #[ignore = "wip"]
    fn fix_1970s_in_parens() {
        assert_suggestion_result(
            "Convert a MIDI file to a record compatible with vintage (1970's) Fisher Price music box record players",
            test_linter(),
            "Convert a MIDI file to a record compatible with vintage (1970s) Fisher Price music box record players",
        );
    }

    #[test]
    fn fix_in_the_1970s() {
        assert_suggestion_result(
            "may have begun, depending on when you start counting, in the 1970's.",
            test_linter(),
            "may have begun, depending on when you start counting, in the 1970s.",
        );
    }

    #[test]
    fn ignore_username_hyphen_1970s_gists() {
        assert_no_lints(
            "GitHub Gist: star and fork ricardo-reis-1970's gists by creating an account on GitHub.",
            test_linter(),
        );
    }

    #[test]
    #[ignore = "wip"]
    fn fix_after_1970s_nppl_minicomputers() {
        assert_suggestion_result(
            "I'm also working on extending it as my CPU is modelled after 1970's minicomputers",
            test_linter(),
            "I'm also working on extending it as my CPU is modelled after 1970s minicomputers",
        );
    }

    #[test]
    fn fix_of_the_1970s() {
        assert_suggestion_result(
            "I despise both as outdated, hard to use relics of the 1970's.",
            test_linter(),
            "I despise both as outdated, hard to use relics of the 1970s.",
        );
    }

    #[test]
    fn fix_couples_in_the_1970s() {
        assert_suggestion_result(
            "This visualization tracks a sample of couples in the 1970's to show how long they transition through relationship stages.",
            test_linter(),
            "This visualization tracks a sample of couples in the 1970s to show how long they transition through relationship stages.",
        );
    }

    #[test]
    fn fix_developed_in_early_1970s() {
        assert_suggestion_result(
            "GPS, originally developed in the early 1970's, is a unidirectional (broadcast only) system",
            test_linter(),
            "GPS, originally developed in the early 1970s, is a unidirectional (broadcast only) system",
        );
    }

    // 1980s (13 examples)

    #[test]
    fn fix_from_the_1980s_like() {
        assert_suggestion_result(
            "Old Stern tables from the 1980's like Flight 2000, Catacomb, etc. are playing audio samples twice, it seems.",
            test_linter(),
            "Old Stern tables from the 1980s like Flight 2000, Catacomb, etc. are playing audio samples twice, it seems.",
        );
    }

    #[test]
    #[ignore = "wip"]
    fn fix_its_the_1980s() {
        assert_suggestion_result(
            "Declarative Rapid Application Development like it's the 1980's again.",
            test_linter(),
            "Declarative Rapid Application Development like it's the 1980s again.",
        );
    }

    #[test]
    fn fix_from_the_1980s_end() {
        assert_suggestion_result(
            "Former countries from the 1980's",
            test_linter(),
            "Former countries from the 1980s",
        );
    }

    #[test]
    #[ignore = "wip"]
    fn fix_a_1980s_npsg() {
        assert_suggestion_result(
            "A re-imiplementation of a classic 1980's DOS game, but in D.",
            test_linter(),
            "A re-imiplementation of a classic 1980s DOS game, but in D.",
        );
    }

    #[test]
    fn fix_of_the_1980s() {
        assert_suggestion_result(
            "The Pugputer is a little labor of love, made as a tribute to the early home computers of the 1980's.",
            test_linter(),
            "The Pugputer is a little labor of love, made as a tribute to the early home computers of the 1980s.",
        );
    }

    #[test]
    fn fix_of_the_1980s_npsg() {
        assert_suggestion_result(
            "FPGA implementation of the 1980's \"Music 5000\" wavetable synthesiser",
            test_linter(),
            "FPGA implementation of the 1980s \"Music 5000\" wavetable synthesiser",
        );
    }

    #[test]
    fn fix_based_off_of_the_1980s_npsg() {
        assert_suggestion_result(
            "Space Fortress is based off of the 1980's vector-based arcade game by Cinematronics called Star Castle.",
            test_linter(),
            "Space Fortress is based off of the 1980s vector-based arcade game by Cinematronics called Star Castle.",
        );
    }

    #[test]
    #[ignore = "Ambiguous - could be referring to the specific year 1980 or the decade 1980s"]
    fn ignore_ambiguous_1980s() {
        assert_no_lints(
            "1980's Old aperture coop checkpoint uses the timer signage instead of checkmarks.",
            test_linter(),
        );
    }

    #[test]
    #[ignore = "wip"]
    fn fix_1980s_nppl() {
        assert_suggestion_result(
            "A project resurrecting the classic 1980's Usborne Computer Guide books, for a new generation of programmers.",
            test_linter(),
            "A project resurrecting the classic 1980s Usborne Computer Guide books, for a new generation of programmers.",
        );
    }

    #[test]
    #[ignore = "Missing determiner is out of the scope of the current version of this linter"]
    fn fix_the_end_of_missing_determiner_1980s() {
        assert_suggestion_result(
            "System software for TIM011, a school computer from the end of 1980's made in former Yugoslavia",
            test_linter(),
            "System software for TIM011, a school computer from the end of the 1980s made in former Yugoslavia",
        );
    }

    #[test]
    fn fix_in_the_1980s_for() {
        assert_suggestion_result(
            "HMSL was originally released in the 1980's for Mac Plus and Amiga",
            test_linter(),
            "HMSL was originally released in the 1980s for Mac Plus and Amiga",
        );
    }

    #[test]
    #[ignore = "wip"]
    fn fix_the_adj_1980s_npsg() {
        assert_suggestion_result(
            "Modern remake of Pole Position, the classic 1980's arcade racing game from Atari.",
            test_linter(),
            "Modern remake of Pole Position, the classic 1980s arcade racing game from Atari.",
        );
    }

    #[test]
    fn fix_since_the_1980s() {
        assert_suggestion_result(
            "Since the 1980's the most common way to interact with a computer is via the graphical user interface (GUI)",
            test_linter(),
            "Since the 1980s the most common way to interact with a computer is via the graphical user interface (GUI)",
        );
    }

    // 1990s (12 examples)

    #[test]
    #[ignore = "wip"]
    fn fix_the_adj_1990s_npsg() {
        assert_suggestion_result(
            "42 3d Graphics Project, recreating the classic 1990's game Wolfienstien 3d",
            test_linter(),
            "42 3d Graphics Project, recreating the classic 1990s game Wolfienstien 3d",
        );
    }

    #[test]
    #[ignore = "wip"]
    fn fix_a_1990s_npsg() {
        assert_suggestion_result(
            "A 1990's Retro linux-rice for Hyprland or Sway, based on Quickshell.",
            test_linter(),
            "A 1990s Retro linux-rice for Hyprland or Sway, based on Quickshell.",
        );
    }

    #[test]
    #[ignore = "Missing determiner is out of the scope of the current version of this linter"]
    fn lacks_determiner_stuck_in_1990s() {
        assert_suggestion_result(
            "Docs are stuck in 1990's - need AWS or Azure example",
            test_linter(),
            "Docs are stuck in the 1990s - need AWS or Azure example",
        );
    }

    #[test]
    #[ignore = "wip"]
    fn fix_the_1990s_npsg() {
        assert_suggestion_result(
            "This program recreates the 1990's arcade game \"Boulder Dash.\"",
            test_linter(),
            "This program recreates the 1990s arcade game \"Boulder Dash.\"",
        );
    }

    #[test]
    fn fix_in_the_1990s_comma() {
        assert_suggestion_result(
            "In the 1990's, Innovative Computer Solutions released multiple programs for the Newton MessagePad as shareware",
            test_linter(),
            "In the 1990s, Innovative Computer Solutions released multiple programs for the Newton MessagePad as shareware",
        );
    }

    #[test]
    fn fix_a_mid_1990s_npsg() {
        assert_suggestion_result(
            "This repository is a modernization of a mid 1990's implementation of the ZMODEM protocol called 'zmtx-zmrx'.",
            test_linter(),
            "This repository is a modernization of a mid 1990s implementation of the ZMODEM protocol called 'zmtx-zmrx'.",
        );
    }

    #[test]
    #[ignore = "Missing determiner is out of the scope of the current version of this linter"]
    fn lacks_determiner_written_in_java_in_1990s() {
        assert_suggestion_result(
            "JMud, mud server written in Java in 1990's.",
            test_linter(),
            "JMud, mud server written in Java in the 1990s.",
        );
    }

    #[test]
    #[ignore = "wip"]
    fn fix_a_adj_1990s_npsg() {
        assert_suggestion_result(
            "Port of a famous 1990's fighting game to MSX2.",
            test_linter(),
            "Port of a famous 1990s fighting game to MSX2.",
        );
    }

    #[test]
    #[ignore = "circa 1990s doesn't sound like natural English. changing to 'circa the 1990s' is out of scope for this linter"]
    fn fix_circa_1990s() {
        assert_suggestion_result(
            "Inspired by Digimon \"Digivices\" tamagotchis circa 1990's.",
            test_linter(),
            "Inspired by Digimon \"Digivices\" tamagotchis circa the 1990s.",
        );
    }

    #[test]
    #[ignore = "wip"]
    fn fix_for_1990s_nppl() {
        assert_suggestion_result(
            "Daughter-board for reprogramming 1990's Toyota ECUs",
            test_linter(),
            "Daughter-board for reprogramming 1990s Toyota ECUs",
        );
    }

    #[test]
    fn fix_the_1990s_classic_mario_hit() {
        assert_suggestion_result(
            "A remake of the 1990's classic mario hit.",
            test_linter(),
            "A remake of the 1990s classic mario hit.",
        );
    }

    #[test]
    fn fix_developed_in_early_1990s() {
        assert_suggestion_result(
            "The code was originally developed in the early 1990's",
            test_linter(),
            "The code was originally developed in the early 1990s",
        );
    }

    // 2000s (10 example)

    #[test]
    fn fix_2000s_style() {
        assert_suggestion_result(
            "2000's-style media library for vintage cellphones (Nokia, etc.)",
            test_linter(),
            "2000s-style media library for vintage cellphones (Nokia, etc.)",
        );
    }

    #[test]
    fn ignore_fork_username_hyphen_2000s_nppl() {
        assert_no_lints(
            "star and fork vishal-2000's gists by creating an account on GitHub.",
            test_linter(),
        );
    }

    #[test]
    fn fix_in_the_2000s() {
        assert_suggestion_result(
            "Simulator engine for reproducing LCD games made by McDonald's in the 2000's.",
            test_linter(),
            "Simulator engine for reproducing LCD games made by McDonald's in the 2000s.",
        );
    }

    #[test]
    #[ignore = "Missing determiner is out of the scope of the current version of this linter"]
    fn fix_in_the_early_2000s_missing_determiner() {
        assert_suggestion_result(
            "Silo was originally released in early 2000's using LLNL-home-grown license verbiage.",
            test_linter(),
            "Silo was originally released in the early 2000s using LLNL-home-grown license verbiage.",
        );
    }

    #[test]
    fn ignore_view_username_hyphen_2000s_npsg() {
        assert_no_lints("View lxw-2000's full-sized avatar.", test_linter());
    }

    #[test]
    fn fix_early_2000s_style_npsg() {
        assert_suggestion_result(
            "Early 2000's Style Personal Webpage.",
            test_linter(),
            "Early 2000s Style Personal Webpage.",
        );
    }

    #[test]
    fn fix_from_the_mid_2000s() {
        assert_suggestion_result(
            "Modeled after the now-defunct Geosense game from the mid 2000's",
            test_linter(),
            "Modeled after the now-defunct Geosense game from the mid 2000s",
        );
    }

    #[test]
    fn fix_for_the_early_2000s_games() {
        assert_suggestion_result(
            "GothicKit is a community-run organization hosting libraries and tools for the early 2000's games Gothic and Gothic II.",
            test_linter(),
            "GothicKit is a community-run organization hosting libraries and tools for the early 2000s games Gothic and Gothic II.",
        );
    }

    #[test]
    fn fix_back_in_the_2000s() {
        assert_suggestion_result(
            "A basic music player/organizer I created back in the 2000's - carderne/CAMO.",
            test_linter(),
            "A basic music player/organizer I created back in the 2000s - carderne/CAMO.",
        );
    }

    #[test]
    fn fix_mid_2000s() {
        assert_suggestion_result(
            "Things I wrote about RDF from the mid-2000's.",
            test_linter(),
            "Things I wrote about RDF from the mid-2000s.",
        );
    }

    // 2010s (4 examples)

    #[test]
    #[ignore = "Sinnemäki 2010 here refers to the author's publication from 2010"]
    fn ignore_author_2010s_publication_reference() {
        assert_no_lints(
            "CLDF version of Sinnemäki 2010's dataset on zero marking and word order",
            test_linter(),
        );
    }

    #[test]
    #[ignore = "Looks like a product name, not a decade"]
    fn ignore_bazel_calls_vs_2010s_cl() {
        assert_no_lints(
            "Bazel calls VS 2010's cl with /DEBUG:FASTLINK",
            test_linter(),
        );
    }

    #[test]
    fn fix_since_the_early_2010s() {
        assert_suggestion_result(
            "the degree of drop-off of CouchDB online community activity since the early-2010's NoSQL craze faded",
            test_linter(),
            "the degree of drop-off of CouchDB online community activity since the early-2010s NoSQL craze faded",
        );
    }

    #[test]
    fn fix_blog_posts_of_the_2010s() {
        assert_suggestion_result(
            "It's jumped off the esoteric analytics blog posts of the 2010's and on to your television screens and into your video games",
            test_linter(),
            "It's jumped off the esoteric analytics blog posts of the 2010s and on to your television screens and into your video games",
        );
    }

    // 2020s (10 examples)

    #[test]
    #[ignore = "Ambiguous. Looks like awkward wording for `the IEEE CEC's 2020 Strategy Card Game AI Competition"]
    fn ignore_ambiguous_2020s() {
        assert_no_lints(
            "This is a bot for Legends of Code and Magic submitted to the IEEE CEC 2020's Strategy Card Game AI Competition.",
            test_linter(),
        );
    }

    #[test]
    #[ignore = "Ambiguous. Maybe awkward wording for `CSSPI Fall 2020's frontend`??"]
    fn ignore_ambiguous_2020s_2() {
        assert_no_lints(
            "CSSPI Fall 2020's frontend mobile web application utilizing React Native.",
            test_linter(),
        );
    }

    #[test]
    #[ignore = "A human can tell from the comparison to `2024's` that `2020's` refers to a specific year/version/release. Harper has no way to tell."]
    fn ignore_ambiguous_2020s_2024s() {
        assert_no_lints(
            "App that converts MSFS 2020's DDS texture format to MSFS 2024's KTX2 format",
            test_linter(),
        );
    }

    #[test]
    #[ignore = "eRum 2020 is probably an event name, not a decade"]
    fn ignore_erum_2020s() {
        assert_no_lints(
            "A repository for purposes of eRum 2020's workshop \"Image processing and computer vision with R\", held on Saturday, June 20, 2020.",
            test_linter(),
        );
    }

    #[test]
    #[ignore = "Ambiguous. Not sure what it means"]
    fn ignore_ambiguous_2020s_3() {
        assert_no_lints("Crashing upon loading saved game in 2020's", test_linter());
    }

    #[test]
    #[ignore = "DEF CON 2020's is probably an event name, not a decade"]
    fn ignore_defcon_2020s_event() {
        assert_no_lints(
            "Reimplementation of DEF CON 2020's Pinboool Binary - pinboool.py.",
            test_linter(),
        );
    }

    #[test]
    fn ignore_username_hyphen_2020s_avatar() {
        assert_no_lints("View theredpill-2020's full-sized avatar.", test_linter());
    }

    #[test]
    #[ignore = "Ambiguous. Not sure what it means"]
    fn ignore_ambiguous_2020s_scipy() {
        assert_no_lints("No imread() in 2020's Scipy v1.5.x.", test_linter());
    }

    #[test]
    #[ignore = "ambiguous"]
    fn ignore_pull_into_2020s() {
        assert_no_lints("Pull into the 2020's.", test_linter());
    }

    #[test]
    #[ignore = "Odd. Looks like it should be `Blasas et al's 2020 \"FUT9-driven ...\"`"]
    fn ignore_blasas_et_al_2020s() {
        assert_no_lints(
            "Scripts that were used to create figure 5 in Blasas et al. 2020's \"FUT9-driven programming of colon cancer cells towards a stem cell-like state\"",
            test_linter(),
        );
    }

    // Multiple decades (11 examples)

    #[test]
    fn fix_in_the_1990s_and_the_2000s() {
        assert_suggestion_result(
            "NTXShape, a converter I developed in the 1990's and maintained through the 2000's",
            test_linter(),
            "NTXShape, a converter I developed in the 1990s and maintained through the 2000s",
        );
    }

    #[test]
    fn fix_early_2000s_early_1990s_early_1980s() {
        assert_suggestion_result(
            "CDISC since 2005, XML since the early 2000's, @SAS since the early 1990's, Programming since the early 1980's.",
            test_linter(),
            "CDISC since 2005, XML since the early 2000s, @SAS since the early 1990s, Programming since the early 1980s.",
        );
    }

    #[test]
    #[ignore = "wip"]
    fn fix_dates_in_the_1960s_comma_1950s_early_1900s() {
        assert_suggestion_result(
            "OK for dates in the 1960's, 1950's... Now... I expect 1939-12-31T23 ... Was Belgium ever not in UTC+1 timezone in early 1900's ?",
            test_linter(),
            "OK for dates in the 1960s, 1950s... Now... I expect 1939-12-31T23 ... Was Belgium ever not in UTC+1 timezone in early 1900s ?",
        );
    }

    #[test]
    fn fix_late_1970s_early_1980s() {
        assert_suggestion_result(
            "Late 1970's/Early 1980's Text Adventure Game from the Mainframe era",
            test_linter(),
            "Late 1970s/Early 1980s Text Adventure Game from the Mainframe era",
        );
    }

    #[test]
    fn fix_in_the_1970s_and_early_1980s() {
        assert_suggestion_result(
            "We modeled the gas mileage of 398 cars built in the 1970's and early 1980's",
            test_linter(),
            "We modeled the gas mileage of 398 cars built in the 1970s and early 1980s",
        );
    }

    #[test]
    fn fix_from_the_late_1970s_early_1980s() {
        assert_suggestion_result(
            "Europe Card Bus (ECB) is a Retro CPU Bus standard from the late 1970's / early 1980's.",
            test_linter(),
            "Europe Card Bus (ECB) is a Retro CPU Bus standard from the late 1970s / early 1980s.",
        );
    }

    #[test]
    #[ignore = "Unnatural English where 'on [decade]' should be 'in the [decade]'"]
    fn ignore_on_80s_on_90s_on_2000s_on_2010s_on_2020s() {
        assert_no_lints(
            "Developer on 80's, software engineer on 90's, tech lead on 2000's, manager from 2010's on and CIO/CDO/CTO on 2020's",
            test_linter(),
        );
    }

    #[test]
    #[ignore = "wip"]
    fn fix_in_the_80s_in_the_90s_2000s_2010s() {
        assert_suggestion_result(
            "UUCP BBS in the 80's Winternet / ABX Net Un Hacking in the 90's FOU 2000's CIS Inc. 2010's For Every Hacker there is an Equal and Opposite Hacker.",
            test_linter(),
            "UUCP BBS in the 80s Winternet / ABX Net Un Hacking in the 90s FOU 2000s CIS Inc. 2010s For Every Hacker there is an Equal and Opposite Hacker.",
        );
    }

    #[test]
    #[ignore = "wip"]
    fn fix_in_the_1990s_and_2000s() {
        assert_suggestion_result(
            "Edelman and his colleagues in the 1990's and 2000's.",
            test_linter(),
            "Edelman and his colleagues in the 1990s and 2000s.",
        );
    }

    #[test]
    #[ignore = "wip"]
    fn fix_1930s_ampersand_1940s() {
        assert_suggestion_result(
            "DJ and collector of quality tango music from the golden era (1930's & 1940's)",
            test_linter(),
            "DJ and collector of quality tango music from the golden era (1930s & 1940s)",
        );
    }

    #[test]
    #[ignore = "wip"]
    fn fix_80s_90s_2000s_2020s() {
        assert_suggestion_result(
            "Nerdy coder-kid on ZX81 and Oric 1 in the 80's, nerdy teen-tech writer on PC in 90's, nerdy engineer in the 2000's, 2020's MongoDB addict, Databricks fan now!",
            test_linter(),
            "Nerdy coder-kid on ZX81 and Oric 1 in the 80s, nerdy teen-tech writer on PC in 90s, nerdy engineer in the 2000s, 2020s MongoDB addict, Databricks fan now!",
        );
    }
}
