//! Async S3 object listing via `ListObjectsV2` paginator.

use aws_sdk_s3::Client;
use tokio::sync::mpsc;
use tracing::debug;

use crate::error::S3boltError;
use crate::types::{ObjectInfo, S3Uri};

/// List all objects under an S3 prefix and send them into a bounded channel.
///
/// This runs as an async task — it streams pages from S3 and sends each
/// object's metadata through the channel. Backpressure is automatic:
/// when the channel buffer is full, listing pauses until consumers catch up.
pub async fn list_objects(
    client: &Client,
    uri: &S3Uri,
    tx: mpsc::Sender<ObjectInfo>,
) -> Result<u64, S3boltError> {
    let mut total = 0u64;

    let mut paginator = client
        .list_objects_v2()
        .bucket(&uri.bucket)
        .prefix(&uri.key)
        .into_paginator()
        .send();

    while let Some(page) = paginator.next().await {
        let page = page.map_err(|e| S3boltError::S3Api {
            operation: "ListObjectsV2".to_owned(),
            message: e.to_string(),
            is_retryable: true,
        })?;

        if let Some(contents) = page.contents {
            for obj in contents {
                let info = ObjectInfo {
                    key: obj.key.unwrap_or_default(),
                    size: obj.size.unwrap_or(0) as u64,
                    e_tag: obj.e_tag,
                    last_modified: obj.last_modified.map(|t| {
                        chrono::DateTime::from_timestamp(t.secs(), t.subsec_nanos())
                            .unwrap_or_default()
                    }),
                    storage_class: obj.storage_class.map(|s| s.to_string()),
                };

                total += 1;
                debug!(key = %info.key, size = info.size, "discovered object");

                if tx.send(info).await.is_err() {
                    // Receiver dropped — abort listing.
                    debug!("listing channel closed, stopping");
                    return Ok(total);
                }
            }
        }
    }

    debug!(total, "listing complete");
    Ok(total)
}
