use relicario_core::{
    generate_org_key, wrap_org_key, unwrap_org_key,
    encrypt_org_manifest, decrypt_org_manifest,
    OrgManifest, OrgManifestEntry, OrgMember, OrgMembers, OrgRole,
    MemberId, ItemId,
};
use relicario_core::item_types::ItemType;
use rand::rngs::OsRng;
use rand::RngCore;
use zeroize::Zeroizing;

fn make_member_keypair() -> (Zeroizing<[u8; 32]>, String) {
    let mut seed = [0u8; 32];
    OsRng.fill_bytes(&mut seed);
    let signing_key = ed25519_dalek::SigningKey::from_bytes(&seed);
    let pubkey_openssh = ssh_key::PrivateKey::from(
        ssh_key::private::Ed25519Keypair::from(&signing_key),
    )
    .public_key()
    .to_openssh()
    .expect("openssh");
    (Zeroizing::new(seed), pubkey_openssh)
}

#[test]
fn org_key_wrap_unwrap_round_trip() {
    let (seed, pubkey) = make_member_keypair();
    let org_key = generate_org_key();
    let wrapped = wrap_org_key(&org_key, &pubkey).expect("wrap");
    let unwrapped = unwrap_org_key(&wrapped, &seed).expect("unwrap");
    assert_eq!(*org_key, *unwrapped);
}

#[test]
fn revoked_member_cannot_decrypt_after_rotation() {
    // Alice and Bob both get access
    let (alice_seed, alice_pubkey) = make_member_keypair();
    let (_bob_seed, bob_pubkey) = make_member_keypair();

    let org_key = generate_org_key();
    let _alice_wrapped = wrap_org_key(&org_key, &alice_pubkey).expect("wrap alice");
    let _bob_wrapped = wrap_org_key(&org_key, &bob_pubkey).expect("wrap bob");

    // Rotate: new key, only Bob gets re-wrapped
    let new_org_key = generate_org_key();
    let new_bob_wrapped = wrap_org_key(&new_org_key, &bob_pubkey).expect("wrap bob new");

    // Alice tries to use old org_key — she can still decrypt old items,
    // but new_bob_wrapped was encrypted with new_org_key, not org_key.
    // Verify: unwrapping new_bob_wrapped with Alice's seed fails.
    let result = unwrap_org_key(&new_bob_wrapped, &alice_seed);
    assert!(result.is_err(), "Alice should not be able to unwrap Bob's new key blob");
}

#[test]
fn org_manifest_filter_restricts_to_granted_collections() {
    let mut manifest = OrgManifest::new();
    for (title, collection) in &[("A", "prod"), ("B", "dev"), ("C", "prod")] {
        manifest.entries.push(OrgManifestEntry {
            id: ItemId::new(),
            r#type: ItemType::SecureNote,
            title: title.to_string(),
            tags: vec![],
            modified: 0,
            trashed_at: None,
            collection: collection.to_string(),
        });
    }

    let member = OrgMember {
        member_id: MemberId::new(),
        display_name: "Alice".into(),
        role: OrgRole::Member,
        ed25519_pubkey: String::new(),
        collections: vec!["prod".into()],
        added_at: 0,
        added_by: MemberId::new(),
    };

    let filtered = manifest.filter_for_member(&member);
    assert_eq!(filtered.entries.len(), 2);
    assert!(filtered.entries.iter().all(|e| e.collection == "prod"));
}

#[test]
fn org_manifest_encrypt_decrypt_round_trip() {
    let key = generate_org_key();
    let mut manifest = OrgManifest::new();
    manifest.entries.push(OrgManifestEntry {
        id: ItemId::new(),
        r#type: ItemType::Login,
        title: "GitHub".into(),
        tags: vec!["work".into()],
        modified: 1748000000,
        trashed_at: None,
        collection: "eng-tools".into(),
    });

    let encrypted = encrypt_org_manifest(&manifest, &key).expect("encrypt");
    let decrypted = decrypt_org_manifest(&encrypted, &key).expect("decrypt");

    assert_eq!(decrypted.entries.len(), 1);
    assert_eq!(decrypted.entries[0].title, "GitHub");
    assert_eq!(decrypted.entries[0].collection, "eng-tools");
}

#[test]
fn members_validation_rejects_invalid_id() {
    let mut members = OrgMembers::new();
    members.members.push(OrgMember {
        member_id: MemberId("not-hex-lol!!".to_string()),
        display_name: "Bad".into(),
        role: OrgRole::Member,
        ed25519_pubkey: String::new(),
        collections: vec![],
        added_at: 0,
        added_by: MemberId::new(),
    });
    assert!(members.validate().is_err());
}
