//! Diagnostics I/O — the ONLY place in `service::diagnostics/` that may perform side effects.
//!
//! Side effects here include: reading and writing `.claude/settings.json` via `std::fs`.
//! Pure permission-rule logic (pattern derivation, wildcard domination checks) lives in
//! `permission.rs`.

use serde_json::{Value, json};
use std::fs;
use std::path::Path;

/// Add a permission rule to `.claude/settings.json` in the project root.
///
/// Does nothing if the rule already exists (exact match) or is already covered
/// by a wildcard pattern in the allow list. Creates the file and any missing
/// parent directories if they do not yet exist.
///
/// # Errors
/// Returns `Err(String)` if the directory cannot be created, the file cannot be
/// read or written, or the JSON cannot be parsed or serialised.
pub fn add_permission_rule(project_root: &Path, rule: &str) -> Result<(), String> {
    let claude_dir = project_root.join(".claude");
    fs::create_dir_all(&claude_dir)
        .map_err(|e| format!("Failed to create .claude/ directory: {e}"))?;

    let settings_path = claude_dir.join("settings.json");
    let mut settings: Value = if settings_path.exists() {
        let content = fs::read_to_string(&settings_path)
            .map_err(|e| format!("Failed to read settings.json: {e}"))?;
        serde_json::from_str(&content).map_err(|e| format!("Failed to parse settings.json: {e}"))?
    } else {
        json!({ "permissions": { "allow": [] } })
    };

    let allow_arr = settings
        .pointer_mut("/permissions/allow")
        .and_then(|v| v.as_array_mut());

    let allow = match allow_arr {
        Some(arr) => arr,
        None => {
            settings
                .as_object_mut()
                .unwrap()
                .entry("permissions")
                .or_insert(json!({ "allow": [] }));
            settings
                .pointer_mut("/permissions/allow")
                .unwrap()
                .as_array_mut()
                .unwrap()
        }
    };

    let rule_value = Value::String(rule.to_string());

    // Exact duplicate check.
    if allow.contains(&rule_value) {
        return Ok(());
    }

    // Wildcard-coverage check: if "mcp__huskies__*" exists, skip more-specific rules.
    if super::permission::is_dominated_by_wildcard(rule, allow) {
        return Ok(());
    }

    allow.push(rule_value);

    let pretty =
        serde_json::to_string_pretty(&settings).map_err(|e| format!("Failed to serialize: {e}"))?;
    fs::write(&settings_path, pretty).map_err(|e| format!("Failed to write settings.json: {e}"))?;
    Ok(())
}
