use crate::expr::Expr;
use crate::expr::OwnedExprExt;
use crate::expr::SequenceExpr;
use crate::linting::expr_linter::Chunk;
use crate::{Token, patterns::Word};

use super::{ExprLinter, Lint, LintKind, Suggestion};

pub struct Confident {
    expr: SequenceExpr,
}

impl Default for Confident {
    fn default() -> Self {
        let pattern = SequenceExpr::with(
            SequenceExpr::from(|tok: &Token, _source: &[char]| {
                tok.kind.is_verb() || tok.kind.is_determiner()
            })
            .or(Word::new("very")),
        )
        .then_whitespace()
        .t_aco("confidant");

        Self { expr: pattern }
    }
}

impl ExprLinter for Confident {
    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.last()?.span;

        Some(Lint {
            span,
            lint_kind: LintKind::WordChoice,
            suggestions: vec![Suggestion::ReplaceWith("confident".chars().collect())],
            message: "Use the adjective.".to_owned(),
            priority: 127,
        })
    }

    fn description(&self) -> &'static str {
        "This linter detects instances where the noun `confidant` is incorrectly used in place of the adjective `confident`. `Confidant` refers to a trusted person, whereas `confident` describes certainty or self-assurance. The rule suggests replacing `confidant` with `confident` when used in an adjectival context."
    }
}

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

    #[test]
    fn describing_person_incorrect() {
        assert_suggestion_result(
            "She felt confidant about her presentation.",
            Confident::default(),
            "She felt confident about her presentation.",
        );
    }

    #[test]
    fn describing_person_correct() {
        assert_lint_count(
            "She felt confident about her presentation.",
            Confident::default(),
            0,
        );
    }

    #[test]
    fn certainty_incorrect() {
        assert_suggestion_result(
            "I am confidant the test results are accurate.",
            Confident::default(),
            "I am confident the test results are accurate.",
        );
    }

    #[test]
    fn certainty_correct() {
        assert_lint_count(
            "I am confident the test results are accurate.",
            Confident::default(),
            0,
        );
    }

    #[test]
    fn demeanor_incorrect() {
        assert_suggestion_result(
            "He walked to the stage with a confidant stride.",
            Confident::default(),
            "He walked to the stage with a confident stride.",
        );
    }

    #[test]
    fn demeanor_correct() {
        assert_lint_count(
            "He walked to the stage with a confident stride.",
            Confident::default(),
            0,
        );
    }

    #[test]
    fn professional_incorrect() {
        assert_suggestion_result(
            "You should sound confidant during job interviews.",
            Confident::default(),
            "You should sound confident during job interviews.",
        );
    }

    #[test]
    fn professional_correct() {
        assert_lint_count(
            "You should sound confident during job interviews.",
            Confident::default(),
            0,
        );
    }

    #[test]
    fn assured_tone_incorrect() {
        assert_suggestion_result(
            "Present your argument in a confidant, persuasive manner.",
            Confident::default(),
            "Present your argument in a confident, persuasive manner.",
        );
    }

    #[test]
    fn assured_tone_correct() {
        assert_lint_count(
            "Present your argument in a confident, persuasive manner.",
            Confident::default(),
            0,
        );
    }

    #[test]
    fn extra_text_between() {
        assert_suggestion_result(
            "She felt very confidant about her presentation.",
            Confident::default(),
            "She felt very confident about her presentation.",
        );
    }

    #[test]
    fn linking_verb_was_confidant() {
        assert_suggestion_result(
            "She was confidant about her presentation.",
            Confident::default(),
            "She was confident about her presentation.",
        );
    }
}
