use crate::expr::Expr;
use crate::expr::OwnedExprExt;
use crate::expr::SequenceExpr;
use crate::linting::expr_linter::Chunk;
use crate::{
    Token,
    linting::{ExprLinter, Lint, LintKind, Suggestion},
    patterns::WordSet,
};

pub struct ElsePossessive {
    expr: SequenceExpr,
}

impl Default for ElsePossessive {
    fn default() -> Self {
        let pronouns = WordSet::new(&[
            "somebody",
            "someone",
            "anybody",
            "anyone",
            "everybody",
            "everyone",
            "nobody",
        ])
        .or(SequenceExpr::word_seq(&["no", "one"]));

        let pattern = SequenceExpr::with(pronouns)
            .then_whitespace()
            .t_aco("elses");

        Self { expr: pattern }
    }
}

impl ExprLinter for ElsePossessive {
    type Unit = Chunk;

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

    fn match_to_lint(&self, toks: &[Token], _src: &[char]) -> Option<Lint> {
        let offender = toks.last()?;
        Some(Lint {
            span: offender.span,
            lint_kind: LintKind::Miscellaneous,
            suggestions: vec![Suggestion::ReplaceWith("else's".chars().collect())],
            message: "Add the missing possessive apostrophe: use `else’s`.".to_owned(),
            priority: 60,
        })
    }

    fn description(&self) -> &str {
        "Detects missing apostrophes in phrases like `someone elses book` and suggests the correct possessive form `else’s`."
    }
}

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

    #[test]
    fn fixes_no_one_elses() {
        assert_suggestion_result(
            "It's no one elses problem.",
            ElsePossessive::default(),
            "It's no one else's problem.",
        );
    }

    #[test]
    fn fixes_someone_elses() {
        assert_suggestion_result(
            "It's someone elses problem.",
            ElsePossessive::default(),
            "It's someone else's problem.",
        );
    }

    #[test]
    fn fixes_anybody_elses() {
        assert_suggestion_result(
            "Was that anybody elses idea?",
            ElsePossessive::default(),
            "Was that anybody else's idea?",
        );
    }

    #[test]
    fn fixes_everyone_elses() {
        assert_suggestion_result(
            "He echoed everyone elses concerns.",
            ElsePossessive::default(),
            "He echoed everyone else's concerns.",
        );
    }

    #[test]
    fn ignores_correct_form() {
        assert_lint_count(
            "She borrowed someone else's notes.",
            ElsePossessive::default(),
            0,
        );
    }
}
