//! Typed wrappers around `crypto::{encrypt, decrypt}` for the new typed-item
//! data model. Each function does JSON-serialize → encrypt or decrypt → JSON-parse.
//!
//! v1 helpers (encrypt_entry / decrypt_entry / encrypt_manifest with the old
//! Manifest type) are intentionally NOT carried forward. The CLI rewrite in
//! Plan 1B switches to the new helpers.

use zeroize::Zeroizing;

use crate::crypto::{decrypt, encrypt};
use crate::error::Result;
use crate::item::Item;
use crate::manifest::Manifest;
use crate::org::OrgManifest;
use crate::settings::VaultSettings;

pub fn encrypt_item(item: &Item, master_key: &Zeroizing<[u8; 32]>) -> Result<Vec<u8>> {
    let json = serde_json::to_vec(item)?;
    let plaintext = Zeroizing::new(json);
    encrypt(master_key, plaintext.as_slice())
}

pub fn decrypt_item(encrypted: &[u8], master_key: &Zeroizing<[u8; 32]>) -> Result<Item> {
    let plaintext = decrypt(master_key, encrypted)?;
    let plaintext = Zeroizing::new(plaintext);
    let item: Item = serde_json::from_slice(&plaintext)?;
    Ok(item)
}

pub fn encrypt_manifest(manifest: &Manifest, master_key: &Zeroizing<[u8; 32]>) -> Result<Vec<u8>> {
    let json = serde_json::to_vec(manifest)?;
    let plaintext = Zeroizing::new(json);
    encrypt(master_key, plaintext.as_slice())
}

pub fn decrypt_manifest(encrypted: &[u8], master_key: &Zeroizing<[u8; 32]>) -> Result<Manifest> {
    let plaintext = decrypt(master_key, encrypted)?;
    let plaintext = Zeroizing::new(plaintext);
    let manifest: Manifest = serde_json::from_slice(&plaintext)?;
    Ok(manifest)
}

pub fn encrypt_settings(settings: &VaultSettings, master_key: &Zeroizing<[u8; 32]>) -> Result<Vec<u8>> {
    let json = serde_json::to_vec(settings)?;
    let plaintext = Zeroizing::new(json);
    encrypt(master_key, plaintext.as_slice())
}

pub fn decrypt_settings(encrypted: &[u8], master_key: &Zeroizing<[u8; 32]>) -> Result<VaultSettings> {
    let plaintext = decrypt(master_key, encrypted)?;
    let plaintext = Zeroizing::new(plaintext);
    let settings: VaultSettings = serde_json::from_slice(&plaintext)?;
    Ok(settings)
}

pub fn encrypt_org_manifest(manifest: &OrgManifest, org_key: &Zeroizing<[u8; 32]>) -> Result<Vec<u8>> {
    let json = serde_json::to_vec(manifest)?;
    let plaintext = Zeroizing::new(json);
    encrypt(org_key, plaintext.as_slice())
}

pub fn decrypt_org_manifest(encrypted: &[u8], org_key: &Zeroizing<[u8; 32]>) -> Result<OrgManifest> {
    let plaintext = decrypt(org_key, encrypted)?;
    let plaintext = Zeroizing::new(plaintext);
    let manifest: OrgManifest = serde_json::from_slice(&plaintext)?;
    Ok(manifest)
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::item_types::{ItemCore, SecureNoteCore};

    fn key() -> Zeroizing<[u8; 32]> { Zeroizing::new([0x33u8; 32]) }

    #[test]
    fn item_round_trip() {
        let item = Item::new("note".into(), ItemCore::SecureNote(SecureNoteCore {
            body: Zeroizing::new("hello".into()),
        }));
        let bytes = encrypt_item(&item, &key()).unwrap();
        let decoded = decrypt_item(&bytes, &key()).unwrap();
        assert_eq!(decoded.title, "note");
    }

    #[test]
    fn manifest_round_trip() {
        let mut m = Manifest::new();
        let item = Item::new("x".into(), ItemCore::SecureNote(SecureNoteCore::default()));
        m.upsert(&item);
        let bytes = encrypt_manifest(&m, &key()).unwrap();
        let decoded = decrypt_manifest(&bytes, &key()).unwrap();
        assert_eq!(decoded.items.len(), 1);
    }

    #[test]
    fn settings_round_trip() {
        let s = VaultSettings::default();
        let bytes = encrypt_settings(&s, &key()).unwrap();
        let decoded = decrypt_settings(&bytes, &key()).unwrap();
        assert_eq!(decoded.attachment_caps.per_attachment_max_bytes,
                   s.attachment_caps.per_attachment_max_bytes);
    }

    #[test]
    fn org_manifest_round_trip() {
        use crate::org::{OrgManifest, OrgManifestEntry};
        use crate::ids::ItemId;
        use crate::item_types::ItemType;

        let mut m = OrgManifest::new();
        m.entries.push(OrgManifestEntry {
            id: ItemId::new(),
            r#type: ItemType::SecureNote,
            title: "test".into(),
            tags: vec![],
            modified: 0,
            trashed_at: None,
            collection: "prod".into(),
        });
        let key = key();
        let bytes = encrypt_org_manifest(&m, &key).unwrap();
        let decoded = decrypt_org_manifest(&bytes, &key).unwrap();
        assert_eq!(decoded.entries.len(), 1);
        assert_eq!(decoded.entries[0].collection, "prod");
    }
}
