use std::collections::HashSet;

use dotenv_core::LineEntry;

use super::Check;
use crate::{LintKind, Warning};

pub(crate) struct DuplicatedKeyChecker<'a> {
    template: &'a str,
    keys: HashSet<String>,
}

impl DuplicatedKeyChecker<'_> {
    fn message(&self, key: &str) -> String {
        self.template.replace("{}", key)
    }
}

impl Default for DuplicatedKeyChecker<'_> {
    fn default() -> Self {
        Self {
            keys: HashSet::new(),
            template: "The {} key is duplicated",
        }
    }
}

impl Check for DuplicatedKeyChecker<'_> {
    fn run(&mut self, line: &LineEntry) -> Option<Warning> {
        let key = line.get_key()?;

        if self.keys.contains(key) {
            return Some(Warning::new(line.number, self.name(), self.message(key)));
        }

        self.keys.insert(key.to_string());
        None
    }

    fn name(&self) -> LintKind {
        LintKind::DuplicatedKey
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::tests::check_test;

    #[test]
    fn with_one_duplicated_key_test() {
        check_test(
            &mut DuplicatedKeyChecker::default(),
            [
                ("FOO=BAR", None),
                ("FOO=BAR", Some("The FOO key is duplicated")),
            ],
        );
    }

    #[test]
    fn with_two_unique_keys_test() {
        check_test(
            &mut DuplicatedKeyChecker::default(),
            [("FOO=BAR", None), ("BAR=FOO", None)],
        );
    }

    #[test]
    fn with_two_unique_keys_case_sensitive_test() {
        check_test(
            &mut DuplicatedKeyChecker::default(),
            [("FOO=BAR", None), ("Foo=FOO", None)],
        );
    }

    #[test]
    fn with_two_duplicated_keys_test() {
        check_test(
            &mut DuplicatedKeyChecker::default(),
            [
                ("FOO=BAR", None),
                ("FOO=BAR", Some("The FOO key is duplicated")),
                ("BAR=FOO", None),
                ("BAR=FOO", Some("The BAR key is duplicated")),
            ],
        );
    }

    #[test]
    fn one_duplicated_and_one_unique_key_test() {
        check_test(
            &mut DuplicatedKeyChecker::default(),
            [
                ("FOO=BAR", None),
                ("FOO=BAR", Some("The FOO key is duplicated")),
                ("BAR=FOO", None),
            ],
        );
    }
}
