use std::sync::OnceLock;

type SanitizePatterns = Vec<(regex::Regex, &'static str)>;
static SANITIZE_PATTERNS: OnceLock<SanitizePatterns> = OnceLock::new();

fn sanitize_patterns() -> &'static SanitizePatterns {
    SANITIZE_PATTERNS.get_or_init(|| {
        [
            (r"--password[= ]\S+", "--password=***"),
            (r"--token[= ]\S+", "--token=***"),
            (r"--secret-key[= ]\S+", "--secret-key=***"),
            (r"--aws-secret[= ]\S+", "--aws-secret=***"),
            // curl -u user:pass  /  curl --user user:pass
            (r"(-u|--user)[= ]\S+:\S+", "$1=***:***"),
            // wget --password=xxx
            (r"(wget\s.*?)--password[= ]\S+", "$1--password=***"),
        ]
        .iter()
        .filter_map(|(pat, rep)| regex::Regex::new(pat).ok().map(|r| (r, *rep)))
        .collect()
    })
}

pub fn should_filter_command(cmd: &str) -> bool {
    let words: Vec<&str> = cmd.split_whitespace().collect();

    if let Some(first) = words.first() {
        if matches!(*first, "passwd" | "ssh-add" | "gpg" | "kinit" | "vault") {
            return true;
        }
    }

    // Inline env-var assignment with a sensitive name: TOKEN=xxx cargo run
    // Matches: SECRET_KEY=… API_KEY=… GITHUB_TOKEN=… AWS_SECRET_ACCESS_KEY=… etc.
    static INLINE_ENV: OnceLock<regex::Regex> = OnceLock::new();
    let inline_env = INLINE_ENV.get_or_init(|| {
        regex::Regex::new(
            r"(?i)^[A-Z][A-Z0-9_]*(SECRET|TOKEN|KEY|PASS(WORD)?|PWD|API)[A-Z0-9_]*=\S+",
        )
        .unwrap()
    });
    if inline_env.is_match(cmd) {
        return true;
    }

    let lower = cmd.to_lowercase();

    // git remote set-url with embedded credentials (http://user:pass@host)
    if lower.contains("git remote set-url") && lower.contains("http") && lower.contains('@') {
        return true;
    }

    false
}

pub fn sanitize_command(cmd: &str) -> String {
    let mut out = cmd.to_string();
    for (re, rep) in sanitize_patterns() {
        out = re.replace_all(&out, *rep).to_string();
    }
    out
}

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

    #[test]
    fn sanitize_password_flag() {
        assert_eq!(sanitize_command("mysql --password=secret123"), "mysql --password=***");
    }

    #[test]
    fn sanitize_token_flag() {
        assert_eq!(sanitize_command("curl --token=abc123"), "curl --token=***");
    }

    #[test]
    fn sanitize_curl_user() {
        let out = sanitize_command("curl -u user:pass https://example.com");
        assert!(out.contains("***"), "expected redaction, got: {}", out);
    }

    #[test]
    fn sanitize_leaves_normal_command_unchanged() {
        let cmd = "cargo build --release";
        assert_eq!(sanitize_command(cmd), cmd);
    }

    #[test]
    fn filter_passwd() {
        assert!(should_filter_command("passwd"));
    }

    #[test]
    fn filter_inline_token_env() {
        assert!(should_filter_command("GITHUB_TOKEN=abc123 curl https://api.github.com"));
    }

    #[test]
    fn filter_inline_secret_env() {
        assert!(should_filter_command("SECRET_KEY=xyz cargo run"));
    }

    #[test]
    fn filter_git_remote_with_credentials() {
        assert!(should_filter_command(
            "git remote set-url origin http://user:pass@github.com/repo"
        ));
    }

    #[test]
    fn no_filter_normal_command() {
        assert!(!should_filter_command("git status"));
        assert!(!should_filter_command("cargo test"));
        assert!(!should_filter_command("ls -la"));
    }
}
