// client/src/paste.rs
#![forbid(unsafe_code)]

use anyhow::{Context, Result};
use chacha20poly1305::aead::{Aead, KeyInit, OsRng, rand_core::RngCore};
use chacha20poly1305::{ChaCha20Poly1305, Key, Nonce};
use std::path::PathBuf;

use lesavka_common::lesavka::PasteRequest;
use lesavka_common::paste::{decode_shared_key, truncate_text};

/// Build an encrypted clipboard request for the server's paste RPC.
///
/// Inputs: the raw clipboard text captured on the desktop client.
/// Outputs: a `PasteRequest` whose payload is truncated to policy, encrypted,
/// and marked as such for the server-side validator.
/// Why: the client owns nonce generation and pre-flight truncation so oversized
/// clipboard content fails predictably before any RPC is attempted.
pub fn build_paste_request(text: &str) -> Result<PasteRequest> {
    let max = std::env::var("LESAVKA_PASTE_MAX")
        .ok()
        .and_then(|v| v.parse::<usize>().ok())
        .unwrap_or(4096);
    let text = truncate_text(text, max);
    let key = load_key()?;
    let cipher = ChaCha20Poly1305::new(Key::from_slice(&key));

    let mut nonce_bytes = [0u8; 12];
    OsRng.fill_bytes(&mut nonce_bytes);
    let nonce = Nonce::from_slice(&nonce_bytes);

    let ciphertext = cipher
        .encrypt(nonce, text.as_bytes())
        .map_err(|e| anyhow::anyhow!("paste encrypt failed: {e}"))?;

    Ok(PasteRequest {
        nonce: nonce_bytes.to_vec(),
        data: ciphertext,
        encrypted: true,
    })
}

fn load_key() -> Result<[u8; 32]> {
    let raw = load_key_material()?;
    decode_shared_key(&raw)
}

fn load_key_material() -> Result<String> {
    if let Some(raw) = std::env::var("LESAVKA_PASTE_KEY")
        .ok()
        .map(|value| value.trim().to_string())
        .filter(|value| !value.is_empty())
    {
        return Ok(raw);
    }

    if let Some(path) = std::env::var("LESAVKA_PASTE_KEY_FILE")
        .ok()
        .map(PathBuf::from)
        .or_else(default_paste_key_path)
    {
        let raw = std::fs::read_to_string(&path)
            .with_context(|| format!("reading paste key file {}", path.display()))?;
        let trimmed = raw.trim().to_string();
        if !trimmed.is_empty() {
            return Ok(trimmed);
        }
        anyhow::bail!("paste key file {} is empty", path.display());
    }

    anyhow::bail!(
        "LESAVKA_PASTE_KEY not set (or no paste key file present at ~/.config/lesavka/paste-key)"
    )
}

fn default_paste_key_path() -> Option<PathBuf> {
    std::env::var_os("HOME").map(|home| {
        let mut path = PathBuf::from(home);
        path.push(".config/lesavka/paste-key");
        path
    })
}
