use crate::expr::Expr;
use crate::expr::OwnedExprExt;
use crate::expr::SequenceExpr;
use crate::{
    Token,
    patterns::{NominalPhrase, Word},
};

use super::{ExprLinter, Lint, LintKind, Suggestion};
use crate::linting::expr_linter::Chunk;

pub struct ForNoun {
    expr: SequenceExpr,
}

impl Default for ForNoun {
    fn default() -> Self {
        let pattern = SequenceExpr::aco("fro")
            .then_whitespace()
            .then(NominalPhrase.or(Word::new("sure")));

        Self { expr: pattern }
    }
}

impl ExprLinter for ForNoun {
    type Unit = Chunk;

    fn expr(&self) -> &dyn Expr {
        &self.expr
    }

    fn match_to_lint(&self, matched_tokens: &[Token], source: &[char]) -> Option<Lint> {
        let span = matched_tokens.first()?.span;
        let problem_chars = span.get_content(source);

        Some(Lint {
            span,
            lint_kind: LintKind::WordChoice,
            suggestions: vec![Suggestion::replace_with_match_case_str(
                "for",
                problem_chars,
            )],
            message: "`For` is more common in this context.".to_owned(),
            priority: 31,
        })
    }

    fn description(&self) -> &'static str {
        "Corrects the archaic or mistaken `fro` to `for` when followed by a noun."
    }
}

#[cfg(test)]
mod tests {
    use super::ForNoun;
    use crate::linting::tests::{assert_lint_count, assert_suggestion_result};

    #[test]
    fn corrects_fro_basic_correction() {
        assert_suggestion_result(
            "I got a text fro Sarah.",
            ForNoun::default(),
            "I got a text for Sarah.",
        );
    }

    #[test]
    fn allows_for_clean() {
        assert_lint_count("I got a text for Sarah.", ForNoun::default(), 0);
    }

    #[test]
    fn corrects_fro_sure() {
        assert_suggestion_result(
            "He was away fro sure!",
            ForNoun::default(),
            "He was away for sure!",
        );
    }
}
