#![cfg(feature = "test-utils")]

mod prelude;
use prelude::*;
use zerokms_protocol::UnverifiedContext;

#[tokio::test]
#[ignore = "e2e"]
async fn create_keyset_e2e() {
    let (client, _, _) = setup_zerokms_client().await;

    let keyset_name: String = Faker.fake();

    let keyset = client
        .create_keyset(&keyset_name, "new keyset description")
        .await
        .expect("failed to create keyset");

    assert_eq!(keyset.description, "new keyset description");
    assert_eq!(keyset.name, keyset_name);
}

#[tokio::test]
#[ignore = "e2e"]
async fn list_keysets() {
    let (client, _, _) = setup_zerokms_client().await;

    let keyset_name: String = Faker.fake();

    let keyset = client
        .create_keyset(&keyset_name, "keyset to list description")
        .await
        .expect("failed to create keyset");

    let retrieved_keyset = client
        .list_keysets(false)
        .await
        .expect("failed to list keysets")
        .into_iter()
        .find(|x| x.id == keyset.id)
        .expect("could not find keyset in listed output");

    assert_eq!(keyset.description, retrieved_keyset.description);
    assert_eq!(keyset.name, retrieved_keyset.name);
}

#[tokio::test]
#[ignore = "e2e"]
async fn modify_keyset() {
    let (client, _, _) = setup_zerokms_client().await;

    let keyset_name: String = Faker.fake();

    let keyset = client
        .create_keyset(&keyset_name, "keyset to modify description")
        .await
        .expect("failed to create keyset");

    let modified_keyset_name: String = Faker.fake();

    client
        .modify_keyset(
            keyset.id,
            Some(&modified_keyset_name),
            Some("modified keyset description"),
        )
        .await
        .expect("failed to modify keyset");

    let retrieved_keyset = client
        .list_keysets(false)
        .await
        .expect("failed to list keysets")
        .into_iter()
        .find(|x| x.id == keyset.id)
        .expect("could not find modified keyset in listed output");

    assert_eq!(retrieved_keyset.description, "modified keyset description");
    assert_eq!(retrieved_keyset.name, modified_keyset_name);
}

#[tokio::test]
#[ignore = "e2e"]
async fn create_client_e2e() {
    let (zero_kms, _, _) = setup_zerokms_client().await;

    let keyset_name: String = Faker.fake();

    let keyset = zero_kms
        .create_keyset(&keyset_name, "keyset for client description")
        .await
        .expect("failed to create keyset");

    let client = zero_kms
        .create_client(
            "created client",
            "created client description",
            Some(keyset.id),
        )
        .await
        .expect("failed to create client");

    assert_eq!(client.name, "created client");
    assert_eq!(client.description, "created client description");
}

#[tokio::test]
#[ignore = "e2e"]
async fn list_clients() {
    let (zero_kms, _, _) = setup_zerokms_client().await;

    let keyset_name: String = Faker.fake();

    let keyset = zero_kms
        .create_keyset(&keyset_name, "keyset for client description")
        .await
        .expect("failed to create keyset");

    let client = zero_kms
        .create_client(
            "client to list",
            "client to list description",
            Some(keyset.id),
        )
        .await
        .expect("failed to create client");

    let retrieved_client = zero_kms
        .list_clients(None)
        .await
        .expect("failed to list clients")
        .into_iter()
        .find(|x| x.id == client.id)
        .expect("failed to find client in list output");

    assert_eq!(retrieved_client.name, client.name);
    assert_eq!(retrieved_client.description, client.description);
}

#[tokio::test]
#[ignore = "e2e"]
async fn encrypt_record() {
    let (zerokms_client, _, _, _) = setup_zerokms_client_with_client_key().await;

    let _ = zerokms_client
        .encrypt_single(
            EncryptPayload::new_with_descriptor(b"message to encrypt", "test-descriptor"),
            None,
        )
        .await
        .expect("failed to encrypt record");
}

#[tokio::test]
#[ignore = "e2e"]
async fn decrypt_record() {
    let (zerokms_client, _, _, _) = setup_zerokms_client_with_client_key().await;

    let record = zerokms_client
        .encrypt_single(
            EncryptPayload::new_with_descriptor(b"message to decrypt", "test-descriptor"),
            None,
        )
        .await
        .expect("failed to encrypt record");

    let message = zerokms_client
        .decrypt_single(record, None, None)
        .await
        .expect("failed to decrypt record");

    assert_eq!(message, b"message to decrypt")
}

#[tokio::test]
#[ignore = "e2e"]
async fn disable_keyset() {
    let (zerokms_client, _, keyset, _) = setup_zerokms_client_with_client_key().await;

    zerokms_client
        .disable_keyset(keyset.id)
        .await
        .expect("failed to disable keyset");
}

#[tokio::test]
#[ignore = "e2e"]
async fn encrypt_with_disabled_keyset() {
    let (zerokms_client, _, keyset, _) = setup_zerokms_client_with_client_key().await;

    zerokms_client
        .disable_keyset(keyset.id)
        .await
        .expect("failed to disable keyset");

    let _ = zerokms_client
        .encrypt_single(
            EncryptPayload::new_with_descriptor(b"message to fail to encrypt", "test-descriptor"),
            None,
        )
        .await
        .expect_err("encryption should fail against disabled keyset");
}

#[tokio::test]
#[ignore = "e2e"]
async fn decrypt_with_disabled_keyset() {
    let (zerokms_client, _, keyset, _) = setup_zerokms_client_with_client_key().await;

    let record = zerokms_client
        .encrypt_single(
            EncryptPayload::new_with_descriptor(b"message to fail to decrypt", "test-descriptor"),
            None,
        )
        .await
        .expect("failed to encrypt record");

    zerokms_client
        .disable_keyset(keyset.id)
        .await
        .expect("failed to disable keyset");

    let _ = zerokms_client
        .decrypt_single(record, None, None)
        .await
        .expect_err("decryption should fail against disabled keyset");
}

#[tokio::test]
#[ignore = "e2e"]
async fn enable_keyset() {
    let (zerokms_client, _, keyset, _) = setup_zerokms_client_with_client_key().await;

    zerokms_client
        .disable_keyset(keyset.id)
        .await
        .expect("failed to disable keyset");

    zerokms_client
        .enable_keyset(keyset.id)
        .await
        .expect("failed to enable keyset");
}

#[tokio::test]
#[ignore = "e2e"]
async fn encrypt_record_reenabled() {
    let (zerokms_client, _, keyset, _) = setup_zerokms_client_with_client_key().await;

    zerokms_client
        .disable_keyset(keyset.id)
        .await
        .expect("failed to disable keyset");

    zerokms_client
        .enable_keyset(keyset.id)
        .await
        .expect("failed to enable keyset");

    let _ = zerokms_client
        .encrypt_single(
            EncryptPayload::new_with_descriptor(b"message to encrypt", "test-descriptor"),
            None,
        )
        .await
        .expect("failed to encrypt record");
}

#[tokio::test]
#[ignore = "e2e"]
async fn decrypt_record_reenabled() {
    let (zerokms_client, _, keyset, _) = setup_zerokms_client_with_client_key().await;

    zerokms_client
        .disable_keyset(keyset.id)
        .await
        .expect("failed to disable keyset");

    zerokms_client
        .enable_keyset(keyset.id)
        .await
        .expect("failed to enable keyset");

    let record = zerokms_client
        .encrypt_single(
            EncryptPayload::new_with_descriptor(
                b"message to decrypt after re-enabling",
                "test-descriptor",
            ),
            None,
        )
        .await
        .expect("failed to encrypt record");

    let message = zerokms_client
        .decrypt_single(record, None, None)
        .await
        .expect("failed to decrypt record");

    assert_eq!(message, b"message to decrypt after re-enabling")
}

#[tokio::test]
#[ignore = "e2e"]
async fn decrypt_with_tenant_keyset() {
    let (zerokms_client, _, _, client_key) = setup_zerokms_client_with_client_key().await;
    let client_id = client_key.key_id;

    // Create and grant a tenant keyset
    let tenant_keyset_name: String = Faker.fake();
    let tenant_keyset = zerokms_client
        .create_keyset(&tenant_keyset_name, "tenant-description")
        .await
        .expect("failed to create tenant keyset");

    zerokms_client
        .grant_keyset(client_id, tenant_keyset.id)
        .await
        .expect("failed to grant client keyset access");

    let record = zerokms_client
        .encrypt_single(
            EncryptPayload::new_with_descriptor(b"message to fail to decrypt", "test-descriptor"),
            None,
        )
        .await
        .expect("failed to encrypt record");

    let _ = zerokms_client
        .decrypt_single(record, Some(tenant_keyset.id), None)
        .await
        .expect_err("decryption should fail when keyset id does not match record");
}

#[tokio::test]
#[ignore = "e2e"]
async fn decrypt_single_with_unverified_context() {
    let (zerokms_client, _, _, _) = setup_zerokms_client_with_client_key().await;

    let record = zerokms_client
        .encrypt_single(
            EncryptPayload::new_with_descriptor(b"message", "test-descriptor"),
            None,
        )
        .await
        .expect("failed to encrypt record");

    let unverified_context: UnverifiedContext = serde_json::from_value(json!({
        "sub": "user_123"
    }))
    .unwrap();

    let message = zerokms_client
        .decrypt_single(record, None, Some(&unverified_context))
        .await
        .expect("failed to decrypt record");

    assert_eq!(message, b"message");
}

#[tokio::test]
#[ignore = "e2e"]
async fn decrypt_with_unverified_context() {
    let (zerokms_client, _, _, _) = setup_zerokms_client_with_client_key().await;

    let record = zerokms_client
        .encrypt_single(
            EncryptPayload::new_with_descriptor(b"message", "test-descriptor"),
            None,
        )
        .await
        .expect("failed to encrypt record");

    let unverified_context: UnverifiedContext = serde_json::from_value(json!({
        "sub": "user_123"
    }))
    .unwrap();

    let message = zerokms_client
        .decrypt(vec![record], None, Some(&unverified_context))
        .await
        .expect("failed to decrypt record");

    assert_eq!(message.len(), 1);
    assert_eq!(message[0], b"message");
}

#[tokio::test]
#[ignore = "e2e"]
async fn decrypt_fallible_with_unverified_context() {
    let (zerokms_client, _, _, _) = setup_zerokms_client_with_client_key().await;

    let record = zerokms_client
        .encrypt_single(
            EncryptPayload::new_with_descriptor(b"message", "test-descriptor"),
            None,
        )
        .await
        .expect("failed to encrypt record");

    let unverified_context: Cow<UnverifiedContext> = serde_json::from_value(json!({
        "sub": "user_123"
    }))
    .unwrap();

    let message = zerokms_client
        .decrypt_fallible(vec![record], Some(unverified_context))
        .await
        .expect("failed to decrypt record");

    assert_eq!(message.len(), 1);
    assert_eq!(message[0].clone().unwrap(), b"message");
}
