mod common;

use common::{
    api::Secret, cleanup_container, conn, create_base_container, init_runtime, models, opts,
    Podman, StreamExt, DEFAULT_IMAGE,
};

//####################################################################################################

async fn create_base_secret(
    podman: &Podman,
    name: &str,
    value: &str,
    opts: Option<opts::SecretCreateOpts>,
) -> Secret {
    let secrets = podman.secrets();
    let _ = secrets.get(name).delete().await;

    let opts = opts.unwrap_or_else(|| opts::SecretCreateOpts::builder(name).build());

    secrets.create(&opts, value).await.expect("created secret")
}

//####################################################################################################

#[tokio::test]
async fn secret_create_remove() {
    let podman = init_runtime();

    let secret = create_base_secret(&podman, "test-create-secret", "test-value", None).await;
    assert!(secret.delete().await.is_ok());
}

#[tokio::test]
async fn secret_inspect() {
    let podman = init_runtime();

    let secret_name = "test-inspect-secret";
    let secret = create_base_secret(&podman, secret_name, "test-value", None).await;

    let inspect_result = secret.inspect().await;
    assert!(inspect_result.is_ok());
    let inspect_data = inspect_result.unwrap();
    let spec = inspect_data.spec.unwrap();
    assert_eq!(spec.name.unwrap(), secret_name);
    assert_eq!(spec.driver.unwrap().name.unwrap(), "file");
    assert!(secret.delete().await.is_ok());
}

#[tokio::test]
async fn secret_create_read_value() {
    let podman = init_runtime();

    let secret_name = "test-create-read-secret";
    let secret_value = "test-value";
    let secret = create_base_secret(&podman, secret_name, secret_value, None).await;

    let secret_target: String = "/tmp/my-secret-value".into();
    let secret_def = models::Secret {
        gid: None,
        uid: None,
        mode: None,
        source: Some(secret_name.to_string()),
        target: Some(secret_target.clone()),
    };
    let container_name = "test-secret-read-container";
    let opts = opts::ContainerCreateOpts::builder()
        .name(container_name)
        .secrets([secret_def])
        .image(DEFAULT_IMAGE)
        .command(["cat", &secret_target])
        .build();
    let container = create_base_container(&podman, container_name, Some(opts)).await;
    container.start(None).await.expect("started container");

    tokio::time::sleep(std::time::Duration::from_secs(1)).await;

    let mut logs_stream = container.logs(
        &opts::ContainerLogsOpts::builder()
            .stdout(true)
            .stderr(true)
            .build(),
    );
    let chunk = logs_stream.next().await;
    assert!(chunk.is_some());
    let chunk = chunk.unwrap();
    assert!(chunk.is_ok());
    let chunk = chunk.unwrap();
    assert!(matches!(chunk, conn::TtyChunk::StdOut(_)));
    if let conn::TtyChunk::StdOut(data) = chunk {
        let s = String::from_utf8_lossy(&data);
        assert_eq!(s, format!("\"{secret_value}\""));
    } else {
        unreachable!();
    }
    assert!(secret.delete().await.is_ok());
    cleanup_container(&podman, container_name).await;
}

#[tokio::test]
async fn secret_list() {
    let podman = init_runtime();
    let secrets = podman.secrets();

    let name_a = "test-list-secret";
    let name_b = "test-list-secret2";
    let value_a = "value_a";
    let value_b = "value_b";

    let secret_a = create_base_secret(&podman, name_a, value_a, None).await;
    let secret_b = create_base_secret(&podman, name_b, value_b, None).await;

    let list_result = secrets.list().await;
    assert!(list_result.is_ok());
    let list_data = list_result.unwrap();
    assert!(!list_data.is_empty());
    assert!(list_data
        .iter()
        .any(|data| data.spec.as_ref().unwrap().name.as_ref().unwrap() == name_a));
    assert!(list_data
        .iter()
        .any(|data| data.spec.as_ref().unwrap().name.as_ref().unwrap() == name_b));

    let _ = secret_a.delete().await;
    let _ = secret_b.delete().await;
}
