use anyhow::{Context, Result};
use aws_config::meta::region::RegionProviderChain;
use aws_config::profile::ProfileFileCredentialsProvider;
use aws_config::retry::RetryConfig;
use aws_config::{BehaviorVersion, Region};
use aws_sdk_ssm::error::ProvideErrorMetadata;
use aws_sdk_ssm::Client;
use log::debug;

pub struct AwsClient {
    ssm_client: Client,
}

impl AwsClient {
    pub async fn new(region: Option<&str>, profile: Option<&str>) -> Result<Self> {
        let mut config_loader = aws_config::defaults(BehaviorVersion::latest())
            // Adaptive retry adds client-side rate limiting on top of exponential
            // backoff + jitter, which is well suited to bulk parameter fetches
            // where we often hit SSM's per-account TPS limit. 8 attempts covers
            // transient throttling / 5xx without blocking the caller too long.
            .retry_config(RetryConfig::adaptive().with_max_attempts(8));

        // Set region if provided
        if let Some(region) = region {
            let region_provider = RegionProviderChain::first_try(Region::new(region.to_string()));
            config_loader = config_loader.region(region_provider);
        }

        // Set profile if provided
        if let Some(profile) = profile {
            let credentials_provider = ProfileFileCredentialsProvider::builder()
                .profile_name(profile)
                .build();
            config_loader = config_loader.credentials_provider(credentials_provider);
        }

        let config = config_loader.load().await;

        debug!("Initialized AWS config with region: {:?}", config.region());

        let ssm_client = Client::new(&config);

        Ok(AwsClient { ssm_client })
    }

    pub async fn get_parameter(&self, name: &str) -> Result<Option<String>> {
        debug!("Getting parameter: {}", name);

        match self
            .ssm_client
            .get_parameter()
            .name(name)
            .with_decryption(true)
            .send()
            .await
        {
            Ok(result) => {
                if let Some(parameter) = result.parameter {
                    if let Some(value) = parameter.value {
                        debug!("Successfully retrieved parameter: {}", name);
                        Ok(Some(value))
                    } else {
                        debug!("Parameter {} has no value", name);
                        Ok(None)
                    }
                } else {
                    debug!("Parameter {} not found", name);
                    Ok(None)
                }
            }
            Err(err) => {
                // Capture the AWS error code (e.g. "ThrottlingException") before
                // consuming the error — it's otherwise swallowed by anyhow's
                // default Display, which hides the inner cause.
                let code = err.code().unwrap_or("Unknown").to_string();
                let message = err.message().map(|m| m.to_string());
                let service_err = err.into_service_error();
                match service_err {
                    aws_sdk_ssm::operation::get_parameter::GetParameterError::ParameterNotFound(_) => {
                        debug!("Parameter not found: {}", name);
                        Ok(None)
                    }
                    _ => {
                        let detail = match &message {
                            Some(m) if !m.is_empty() => format!("{} - {}", code, m),
                            _ => format!("{} ({})", code, service_err),
                        };
                        Err(anyhow::anyhow!("AWS SSM error: {}", detail))
                            .with_context(|| format!("Failed to get parameter: {}", name))
                    }
                }
            }
        }
    }
}