#![allow(warnings, clippy::all, unexpected_cfgs)]
// Vendored from ast-grep 0.41.0 (MIT)
// Modified: SgLang → Lang

use super::OverwriteArgs;
use crate::ast_grep::lang::Lang;
use crate::ast_grep::utils::ErrorContext as EC;

use anyhow::Result;
use ast_grep_config::{RuleConfig, Severity};
use ast_grep_core::Language;
use regex::Regex;

use std::collections::HashMap;

#[derive(Default)]
pub struct RuleOverwrite {
    default_severity: Option<Severity>,
    by_rule_id: HashMap<String, Severity>,
    rule_filter: Option<Regex>,
}

fn read_severity(
    severity: Severity,
    ids: &Option<Vec<String>>,
    by_rule_id: &mut HashMap<String, Severity>,
    default_severity: &mut Option<Severity>,
) {
    let Some(ids) = ids.as_ref() else { return };
    if ids.is_empty() {
        *default_severity = Some(severity);
        return;
    }
    for id in ids {
        by_rule_id.insert(id.clone(), severity.clone());
    }
}

impl RuleOverwrite {
    pub fn new_for_verify(filter: Option<&Regex>, include_off: bool) -> Self {
        Self {
            default_severity: if include_off {
                Some(Severity::Hint)
            } else {
                None
            },
            by_rule_id: HashMap::new(),
            rule_filter: filter.cloned(),
        }
    }
    pub fn new(cli: &OverwriteArgs) -> Result<Self> {
        let mut default_severity = None;
        let mut by_rule_id = HashMap::new();
        read_severity(
            Severity::Error,
            &cli.error,
            &mut by_rule_id,
            &mut default_severity,
        );
        read_severity(
            Severity::Warning,
            &cli.warning,
            &mut by_rule_id,
            &mut default_severity,
        );
        read_severity(
            Severity::Info,
            &cli.info,
            &mut by_rule_id,
            &mut default_severity,
        );
        read_severity(
            Severity::Hint,
            &cli.hint,
            &mut by_rule_id,
            &mut default_severity,
        );
        read_severity(
            Severity::Off,
            &cli.off,
            &mut by_rule_id,
            &mut default_severity,
        );
        Ok(Self {
            default_severity,
            by_rule_id,
            rule_filter: cli.filter.clone(),
        })
    }

    pub fn process_configs(&self, configs: Vec<RuleConfig<Lang>>) -> Result<Vec<RuleConfig<Lang>>> {
        let mut configs = if let Some(filter) = &self.rule_filter {
            filter_rule_by_regex(configs, filter)?
        } else {
            configs
        };
        for config in &mut configs {
            let overwrite = self.find(&config.id);
            overwrite.overwrite(config);
        }
        Ok(configs)
    }

    pub fn find(&self, id: &str) -> OverwriteResult {
        let severity = self
            .by_rule_id
            .get(id)
            .cloned()
            .or_else(|| self.default_severity.clone());
        OverwriteResult { severity }
    }
}

fn filter_rule_by_regex(
    configs: Vec<RuleConfig<Lang>>,
    filter: &Regex,
) -> Result<Vec<RuleConfig<Lang>>> {
    let selected: Vec<_> = configs
        .into_iter()
        .filter(|c| filter.is_match(&c.id))
        .collect();

    if selected.is_empty() {
        Err(anyhow::anyhow!(EC::RuleNotFound(filter.to_string())))
    } else {
        Ok(selected)
    }
}

pub struct OverwriteResult {
    pub severity: Option<Severity>,
}

impl OverwriteResult {
    fn overwrite<L>(&self, rule: &mut RuleConfig<L>)
    where
        L: Language,
    {
        if let Some(severity) = &self.severity {
            rule.severity = severity.clone();
        }
    }
}
