// Integration coverage for the client paste-request contract.
//
// Scope: validate key loading, encryption metadata, and truncation behavior
// through the public paste helper.
// Targets: `client/src/paste.rs`.
// Why: these checks are pure policy and crypto framing, so they belong in the
// centralized `tests` contract suite.

use chacha20poly1305::aead::Aead;
use chacha20poly1305::{ChaCha20Poly1305, Key, KeyInit, Nonce};
use lesavka_client::paste::build_paste_request;
use serial_test::serial;
use temp_env::with_var;
use tempfile::tempdir;

const TEST_KEY_HEX: &str = "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f";
const TEST_KEY_RAW: &str = "0123456789abcdef0123456789abcdef";

#[test]
#[serial]
fn build_paste_request_requires_a_shared_key() {
    let dir = tempdir().expect("tempdir");
    with_var("LESAVKA_PASTE_KEY", None::<&str>, || {
        with_var("LESAVKA_PASTE_KEY_FILE", None::<&str>, || {
            with_var("HOME", Some(dir.path().as_os_str()), || {
                let err = build_paste_request("hello").expect_err("missing key should fail");
                let rendered = format!("{err:#}");
                assert!(
                    rendered.contains("paste key file") || rendered.contains("LESAVKA_PASTE_KEY")
                );
            });
        });
    });
}

#[test]
#[serial]
fn build_paste_request_requires_explicit_key_when_home_is_unset() {
    with_var("LESAVKA_PASTE_KEY", None::<&str>, || {
        with_var("LESAVKA_PASTE_KEY_FILE", None::<&str>, || {
            with_var("HOME", None::<&str>, || {
                let err = build_paste_request("hello").expect_err("missing key should fail");
                let rendered = format!("{err:#}");
                assert!(rendered.contains("LESAVKA_PASTE_KEY"));
            });
        });
    });
}

#[test]
#[serial]
fn build_paste_request_sets_encryption_fields() {
    with_var("LESAVKA_PASTE_KEY", Some(TEST_KEY_HEX), || {
        let req = build_paste_request("hello world").expect("build request");
        assert!(req.encrypted);
        assert_eq!(req.nonce.len(), 12);
        assert!(!req.data.is_empty());
    });
}

#[test]
#[serial]
fn build_paste_request_accepts_raw_32_byte_shared_key() {
    with_var("LESAVKA_PASTE_KEY", Some(TEST_KEY_RAW), || {
        let req = build_paste_request("hello raw key").expect("build request");
        assert!(req.encrypted);
        assert_eq!(req.nonce.len(), 12);
        assert!(!req.data.is_empty());
    });
}

#[test]
#[serial]
fn build_paste_request_truncates_plaintext_before_encryption() {
    with_var("LESAVKA_PASTE_KEY", Some(TEST_KEY_HEX), || {
        with_var("LESAVKA_PASTE_MAX", Some("5"), || {
            let req = build_paste_request("hello-from-lesavka").expect("build truncated request");
            let key = lesavka_common::paste::decode_shared_key(TEST_KEY_HEX).expect("decode key");
            let cipher = ChaCha20Poly1305::new(Key::from_slice(&key));
            let nonce = Nonce::from_slice(&req.nonce);
            let plaintext = cipher
                .decrypt(nonce, req.data.as_ref())
                .expect("decrypt request data");
            assert_eq!(std::str::from_utf8(&plaintext).expect("utf8"), "hello");
        });
    });
}

#[test]
#[serial]
fn build_paste_request_loads_shared_key_from_file() {
    let dir = tempdir().expect("tempdir");
    let path = dir.path().join("paste-key");
    std::fs::write(&path, TEST_KEY_HEX).expect("write key");
    with_var("LESAVKA_PASTE_KEY", None::<&str>, || {
        with_var("LESAVKA_PASTE_KEY_FILE", Some(path.as_os_str()), || {
            let req = build_paste_request("hello world").expect("build request");
            assert!(req.encrypted);
            assert_eq!(req.nonce.len(), 12);
            assert!(!req.data.is_empty());
        });
    });
}

#[test]
#[serial]
fn build_paste_request_uses_default_key_path_under_home() {
    let dir = tempdir().expect("tempdir");
    let path = dir.path().join(".config/lesavka/paste-key");
    std::fs::create_dir_all(path.parent().expect("paste key dir")).expect("create config dir");
    std::fs::write(&path, TEST_KEY_HEX).expect("write key");

    with_var("LESAVKA_PASTE_KEY", None::<&str>, || {
        with_var("LESAVKA_PASTE_KEY_FILE", None::<&str>, || {
            with_var("HOME", Some(dir.path().as_os_str()), || {
                let req = build_paste_request("hello default path").expect("build request");
                assert!(req.encrypted);
                assert_eq!(req.nonce.len(), 12);
                assert!(!req.data.is_empty());
            });
        });
    });
}

#[test]
#[serial]
fn build_paste_request_rejects_empty_default_key_file() {
    let dir = tempdir().expect("tempdir");
    let path = dir.path().join(".config/lesavka/paste-key");
    std::fs::create_dir_all(path.parent().expect("paste key dir")).expect("create config dir");
    std::fs::write(&path, "").expect("write empty key");

    with_var("LESAVKA_PASTE_KEY", None::<&str>, || {
        with_var("LESAVKA_PASTE_KEY_FILE", None::<&str>, || {
            with_var("HOME", Some(dir.path().as_os_str()), || {
                let err = build_paste_request("hello").expect_err("empty key file should fail");
                assert!(format!("{err:#}").contains("is empty"));
            });
        });
    });
}
