seamless-glance 1.0.0

Fast, read-only AWS TUI for cloud infrastructure visibility and triage
use crate::{
    app::App,
    aws::clients::AwsClients,
    models::{
        lambda::{LambdaFunctionInfo, LambdaSummary},
        service_status::ServiceStatus,
    },
};
use aws_types::region::Region;
use futures::future::join_all;

async fn clients_for_region(region: &Region) -> AwsClients {
    let sdk_config = aws_config::defaults(aws_config::BehaviorVersion::v2026_01_12())
        .region(region.clone())
        .load()
        .await;

    AwsClients::new(&sdk_config)
}

async fn fetch_lambda_for_region(region: Region) -> Result<Vec<LambdaFunctionInfo>, ServiceStatus> {
    let aws = clients_for_region(&region).await;

    let resp = match aws.lambda.list_functions().send().await {
        Ok(r) => r,
        Err(err) => {
            let msg = err.to_string();
            let status = if msg.contains("AccessDenied") {
                ServiceStatus::AccessDenied
            } else {
                ServiceStatus::Unavailable(msg)
            };

            return Err(status);
        }
    };

    let functions = resp
        .functions()
        .iter()
        .map(|f| LambdaFunctionInfo {
            name: f.function_name().unwrap_or("unknown").to_string(),
            region: region.as_ref().to_string(),
            runtime: f
                .runtime()
                .map(|r| format!("{:?}", r))
                .unwrap_or("unknown".into()),
            memory_mb: f.memory_size().unwrap_or(0),
            timeout_sec: f.timeout().unwrap_or(0),
            last_modified: f.last_modified().unwrap_or("-").to_string(),
        })
        .collect();

    Ok(functions)
}

pub async fn fetch_lambda_functions(app: &App) -> Vec<LambdaFunctionInfo> {
    let mut functions = if app.is_global_region_selected() {
        let futures = app.regions.iter().cloned().map(fetch_lambda_for_region);

        let results = join_all(futures).await;

        let mut all = Vec::new();

        for result in results {
            match result {
                Ok(mut funcs) => all.append(&mut funcs),
                Err(ServiceStatus::AccessDenied) => {
                    eprintln!("Access denied to Lambda in one region");
                }
                Err(ServiceStatus::Unavailable(msg)) => {
                    eprintln!("Lambda unavailable in one region: {}", msg);
                }
                Err(_) => {}
            }
        }

        all
    } else {
        fetch_lambda_for_region(app.current_region().clone())
            .await
            .unwrap_or_default()
    };

    functions.sort_by(|a, b| a.region.cmp(&b.region).then_with(|| a.name.cmp(&b.name)));

    functions
}

pub async fn fetch_lambda_summary(app: &App) -> LambdaSummary {
    if !app.is_global_region_selected() {
        return match fetch_lambda_for_region(app.current_region().clone()).await {
            Ok(functions) => LambdaSummary {
                function_count: functions.len() as u32,
                status: ServiceStatus::Ok,
            },
            Err(status) => LambdaSummary {
                function_count: 0,
                status,
            },
        };
    }

    let futures = app.regions.iter().cloned().map(fetch_lambda_for_region);

    let results = join_all(futures).await;

    let mut function_count = 0u32;
    let mut any_success = false;
    let mut saw_access_denied = false;
    let mut saw_unavailable_msg: Option<String> = None;

    for result in results {
        match result {
            Ok(functions) => {
                any_success = true;
                function_count += functions.len() as u32;
            }
            Err(ServiceStatus::AccessDenied) => {
                saw_access_denied = true;
            }
            Err(ServiceStatus::Unavailable(msg)) => {
                if saw_unavailable_msg.is_none() {
                    saw_unavailable_msg = Some(msg);
                }
            }
            Err(_) => {}
        }
    }

    let status = if any_success {
        ServiceStatus::Ok
    } else if saw_access_denied {
        ServiceStatus::AccessDenied
    } else {
        ServiceStatus::Unavailable(
            saw_unavailable_msg.unwrap_or_else(|| "Lambda unavailable in all regions".to_string()),
        )
    };

    LambdaSummary {
        function_count,
        status,
    }
}