//! Pure permission-rule generation for `service::diagnostics`.
//!
//! These functions produce Claude Code permission-rule strings from tool call
//! metadata. No I/O: they take `&str` / `&Value` and return `String`.

use serde_json::Value;

/// Generate a Claude Code permission rule string for the given tool name and input.
///
/// - `Bash` tools → `Bash(first_word *)` derived from the `command` field.
/// - All other tools → the tool name verbatim (e.g. `Edit`, `mcp__huskies__create_story`).
pub fn generate_permission_rule(tool_name: &str, tool_input: &Value) -> String {
    if tool_name == "Bash" {
        let command_str = tool_input
            .get("command")
            .and_then(|v| v.as_str())
            .unwrap_or("");
        let first_word = command_str.split_whitespace().next().unwrap_or("unknown");
        format!("Bash({first_word} *)")
    } else {
        tool_name.to_string()
    }
}

/// Return `true` if `rule` is already covered by an existing wildcard in `allow_list`.
///
/// For example, if `allow_list` contains `"mcp__huskies__*"`, then the more
/// specific rule `"mcp__huskies__create_story"` is already covered.
pub fn is_dominated_by_wildcard(rule: &str, allow_list: &[Value]) -> bool {
    allow_list.iter().any(|existing| {
        if let Some(pat) = existing.as_str()
            && let Some(prefix) = pat.strip_suffix('*')
        {
            return rule.starts_with(prefix);
        }
        false
    })
}

// ── Tests ─────────────────────────────────────────────────────────────────────

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

    #[test]
    fn generate_rule_for_edit_tool() {
        let rule = generate_permission_rule("Edit", &json!({}));
        assert_eq!(rule, "Edit");
    }

    #[test]
    fn generate_rule_for_write_tool() {
        let rule = generate_permission_rule("Write", &json!({}));
        assert_eq!(rule, "Write");
    }

    #[test]
    fn generate_rule_for_bash_git() {
        let rule = generate_permission_rule("Bash", &json!({"command": "git status"}));
        assert_eq!(rule, "Bash(git *)");
    }

    #[test]
    fn generate_rule_for_bash_cargo() {
        let rule = generate_permission_rule("Bash", &json!({"command": "cargo test --all"}));
        assert_eq!(rule, "Bash(cargo *)");
    }

    #[test]
    fn generate_rule_for_bash_empty_command() {
        let rule = generate_permission_rule("Bash", &json!({}));
        assert_eq!(rule, "Bash(unknown *)");
    }

    #[test]
    fn generate_rule_for_mcp_tool() {
        let rule = generate_permission_rule("mcp__huskies__create_story", &json!({"name": "foo"}));
        assert_eq!(rule, "mcp__huskies__create_story");
    }

    #[test]
    fn is_dominated_by_exact_wildcard() {
        let allow = vec![json!("mcp__huskies__*")];
        assert!(is_dominated_by_wildcard(
            "mcp__huskies__create_story",
            &allow
        ));
    }

    #[test]
    fn is_not_dominated_by_different_prefix() {
        let allow = vec![json!("mcp__other__*")];
        assert!(!is_dominated_by_wildcard(
            "mcp__huskies__create_story",
            &allow
        ));
    }

    #[test]
    fn is_not_dominated_when_list_is_empty() {
        assert!(!is_dominated_by_wildcard("Edit", &[]));
    }
}
