pub fn ensure_main_window(window: &tauri::WebviewWindow) -> Result<(), String> {
    ensure_main_window_label(window.label())
}

pub fn ensure_main_window_label(label: &str) -> Result<(), String> {
    if label == "main" {
        Ok(())
    } else {
        Err(format!(
            "This command is only available from the main window (got {label})."
        ))
    }
}

/// Reject the call unless the invoking window's label is one of `allowed`.
///
/// Use this for commands that are legitimately reachable from a small set of
/// documented secondary windows (e.g. the transcript viewer or the
/// transcription preview), where `ensure_main_window` would be over-broad
/// and break in-app flows. The allowed set should always be a fixed
/// `&[&'static str]` and should mirror an entry in
/// `src-tauri/capabilities/*.json` — keeping the IPC trust boundary and the
/// permission grant in lock-step.
pub fn ensure_window_in_set(window: &tauri::WebviewWindow, allowed: &[&str]) -> Result<(), String> {
    ensure_window_in_set_label(window.label(), allowed)
}

pub fn ensure_window_in_set_label(label: &str, allowed: &[&str]) -> Result<(), String> {
    if allowed.contains(&label) {
        Ok(())
    } else {
        Err(format!(
            "This command is only available from {allowed:?} (got {label})."
        ))
    }
}

#[cfg(test)]
mod tests {
    use super::{ensure_main_window_label, ensure_window_in_set_label};

    #[test]
    fn accepts_main_window() {
        assert!(ensure_main_window_label("main").is_ok());
    }

    #[test]
    fn rejects_secondary_windows() {
        for label in ["tasks-float", "transcript-viewer", "transcription-preview"] {
            assert!(ensure_main_window_label(label).is_err());
        }
    }

    #[test]
    fn ensure_window_in_set_accepts_listed_labels() {
        let allowed = &["main", "transcript-viewer", "transcription-preview"];
        for label in allowed {
            assert!(ensure_window_in_set_label(label, allowed).is_ok());
        }
    }

    #[test]
    fn ensure_window_in_set_rejects_unlisted_label() {
        let allowed = &["main", "transcript-viewer"];
        assert!(ensure_window_in_set_label("tasks-float", allowed).is_err());
        assert!(ensure_window_in_set_label("attacker-popup", allowed).is_err());
    }
}

/// Pins the invariant flagged in the 12b413d commit message and the
/// Phase B.6 audit (2026-05-14): every label that the Rust IPC layer
/// allow-lists for clipboard / paste-replacing must also appear in one
/// of the Tauri capability JSONs' "windows" arrays. If the two halves
/// drift, the IPC trust boundary silently disagrees with the permission
/// grant — either the IPC layer permits a window the capability system
/// rejects (call dies with a Tauri permission error), or the capability
/// system permits a window the IPC layer rejects (call dies with the
/// Lumotia rejection message). Either is a maintenance footgun the
/// commit message claimed to address but did not actually pin.
#[cfg(test)]
mod tests_capability_mirror {
    use std::collections::HashSet;
    use std::path::Path;

    /// Read all `"windows"` labels declared across the capability JSONs
    /// at `src-tauri/capabilities/`. The Tauri permission system uses
    /// these arrays to scope capability grants per window; the Rust IPC
    /// `ensure_window_in_set` callers must reference the same labels.
    fn declared_window_labels() -> HashSet<String> {
        let manifest_dir = env!("CARGO_MANIFEST_DIR");
        let capabilities_dir = Path::new(manifest_dir).join("capabilities");

        let mut labels: HashSet<String> = HashSet::new();
        for filename in ["main.json", "secondary-windows.json"] {
            let body = std::fs::read_to_string(capabilities_dir.join(filename))
                .unwrap_or_else(|e| panic!("read {filename}: {e}"));
            let json: serde_json::Value =
                serde_json::from_str(&body).unwrap_or_else(|e| panic!("parse {filename}: {e}"));
            let windows = json["windows"]
                .as_array()
                .unwrap_or_else(|| panic!("{filename} missing 'windows' array"));
            for w in windows {
                if let Some(label) = w.as_str() {
                    labels.insert(label.to_string());
                }
            }
        }
        labels
    }

    #[test]
    fn allowlists_match_capability_jsons() {
        let declared = declared_window_labels();

        for label in crate::commands::clipboard::CLIPBOARD_ALLOWED_WINDOWS {
            assert!(
                declared.contains(*label),
                "CLIPBOARD_ALLOWED_WINDOWS label {label:?} is not declared in any \
                 capabilities JSON ({}). Either remove the label from the Rust const \
                 or add the window to capabilities/secondary-windows.json so the IPC \
                 trust boundary and the capability grant stay in lock-step.",
                declared.iter().cloned().collect::<Vec<_>>().join(", ")
            );
        }

        for label in crate::commands::paste::PASTE_REPLACING_ALLOWED_WINDOWS {
            assert!(
                declared.contains(*label),
                "PASTE_REPLACING_ALLOWED_WINDOWS label {label:?} is not declared in any \
                 capabilities JSON ({}). Either remove the label from the Rust const \
                 or add the window to capabilities/secondary-windows.json.",
                declared.iter().cloned().collect::<Vec<_>>().join(", ")
            );
        }
    }
}
