use harper_brill::UPOS;

use crate::Token;
use crate::expr::Expr;
use crate::expr::SequenceExpr;
use crate::patterns::UPOSSet;

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

pub struct TooTo {
    expr: Box<dyn Expr>,
}

impl Default for TooTo {
    fn default() -> Self {
        let expr = SequenceExpr::aco("too").t_ws().then(UPOSSet::new(&[
            UPOS::NOUN,
            UPOS::PRON,
            UPOS::PROPN,
            UPOS::VERB,
            UPOS::DET,
        ]));
        Self {
            expr: Box::new(expr),
        }
    }
}

impl ExprLinter for TooTo {
    type Unit = Chunk;

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

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

        if let Some(next_tok) = matched_tokens.get(2)
            && next_tok.kind.is_upos(UPOS::VERB)
            && !next_tok.kind.is_verb_lemma()
        {
            return None;
        }

        Some(Lint {
            span,
            lint_kind: LintKind::WordChoice,
            suggestions: vec![
                Suggestion::replace_with_match_case("to".chars().collect(), text)
            ],
            message: "Use the infinitive marker `to` here instead of the adverb `too`, which indicates excess degree.".to_owned(),
            priority: 31,
        })
    }

    fn description(&self) -> &'static str {
        "Handles the transition from `too` -> `to`."
    }
}
