use arboard::Clipboard;

use crate::commands::security::ensure_window_in_set;

/// Refuse-to-set-clipboard limit. 1 MiB is comfortably above any
/// dictation output and any sensible "copy raw transcript" — anything
/// larger is a memory-pressure footgun if the clipboard backend
/// (arboard → X11 INCR / Wayland data device / NSPasteboard) tries to
/// materialise the full string for every paste target. The same cap is
/// used by `commands::paste::paste_text` so the two surfaces refuse
/// identically.
pub(crate) const MAX_CLIPBOARD_BYTES: usize = 1024 * 1024;

/// Windows that are allowed to call `copy_to_clipboard`. The transcript
/// viewer and transcription preview both have legitimate "copy raw text"
/// buttons; mirror the secondary-windows capability grant in
/// `src-tauri/capabilities/secondary-windows.json` so the IPC trust
/// boundary and the permission set stay in lock-step. The mirror
/// invariant is pinned by
/// `commands::security::tests_capability_mirror::allowlists_match_capability_jsons`.
pub(crate) const CLIPBOARD_ALLOWED_WINDOWS: &[&str] =
    &["main", "transcript-viewer", "transcription-preview"];

/// Copy text to the system clipboard via arboard. Restricted to the
/// documented "clipboard-capable" windows (main + transcript-viewer +
/// transcription-preview) and capped at `MAX_CLIPBOARD_BYTES` to keep the
/// IPC surface symmetric with the rest of the codebase (Trust-6,
/// 2026-05-12 code-atomiser fix; broadened on 2026-05-13 to cover the
/// in-app preview/viewer copy buttons that were collateral-damaged by the
/// original main-only restriction).
#[tauri::command]
pub fn copy_to_clipboard(window: tauri::WebviewWindow, text: String) -> Result<(), String> {
    ensure_window_in_set(&window, CLIPBOARD_ALLOWED_WINDOWS)?;
    if text.len() > MAX_CLIPBOARD_BYTES {
        return Err(format!(
            "Clipboard payload too large ({} bytes; limit {} bytes).",
            text.len(),
            MAX_CLIPBOARD_BYTES
        ));
    }
    let mut clipboard = Clipboard::new().map_err(|e| format!("Clipboard init failed: {e}"))?;
    clipboard
        .set_text(&text)
        .map_err(|e| format!("Clipboard write failed: {e}"))?;
    Ok(())
}

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

    /// Pure helper that mirrors the size-cap branch in
    /// `copy_to_clipboard`, factored so we can test it without a real
    /// `tauri::WebviewWindow`. The command itself wires this into
    /// `ensure_main_window` + arboard.
    fn size_check(text: &str) -> Result<(), String> {
        if text.len() > MAX_CLIPBOARD_BYTES {
            Err(format!(
                "Clipboard payload too large ({} bytes; limit {} bytes).",
                text.len(),
                MAX_CLIPBOARD_BYTES
            ))
        } else {
            Ok(())
        }
    }

    #[test]
    fn accepts_normal_payload() {
        assert!(size_check("hello world").is_ok());
    }

    #[test]
    fn rejects_payload_above_cap() {
        let big = "a".repeat(MAX_CLIPBOARD_BYTES + 1);
        let err = size_check(&big).expect_err("expected size-cap rejection");
        assert!(err.contains("too large"), "unexpected error: {err}");
    }

    #[test]
    fn accepts_payload_exactly_at_cap() {
        let exact = "b".repeat(MAX_CLIPBOARD_BYTES);
        assert!(size_check(&exact).is_ok());
    }
}
