//! Handlers for CloudFront Field-Level Encryption (configs + profiles)
//! and Realtime Log Configs.

use chrono::Utc;
use http::{HeaderMap, StatusCode};

use fakecloud_core::service::{AwsRequest, AwsResponse, AwsServiceError};

use crate::fle::{
    CreateRealtimeLogConfigRequest, FieldLevelEncryptionConfig, FieldLevelEncryptionProfileConfig,
    GetOrDeleteRealtimeLogConfigRequest, StoredFieldLevelEncryption,
    StoredFieldLevelEncryptionProfile, StoredRealtimeLogConfig, UpdateRealtimeLogConfigRequest,
};
use crate::policies::{
    not_found, precondition_failed, require_if_match, rfc3339, route_id, xml_with_etag,
};
use crate::router::Route;
use crate::service::{
    aws_error, esc, generate_id_with_prefix, invalid_argument, xml_response, CloudFrontService,
    DEFAULT_ACCOUNT,
};
use crate::xml_io;

const NS: &str = crate::NAMESPACE;
const XML_DECL: &str = r#"<?xml version="1.0" encoding="UTF-8"?>"#;

// ─── Field-Level Encryption Config ────────────────────────────────────

impl CloudFrontService {
    pub(crate) fn create_field_level_encryption_config(
        &self,
        req: &AwsRequest,
    ) -> Result<AwsResponse, AwsServiceError> {
        let cfg: FieldLevelEncryptionConfig = xml_io::from_xml_root(&req.body).map_err(|e| {
            invalid_argument(format!("invalid FieldLevelEncryptionConfig XML: {e}"))
        })?;
        if cfg.caller_reference.is_empty() {
            return Err(invalid_argument("CallerReference is required"));
        }
        let mut state = self.state.write();
        let account = state
            .accounts
            .entry(DEFAULT_ACCOUNT.to_string())
            .or_default();
        if let Some(existing) = account
            .field_level_encryptions
            .values()
            .find(|f| f.config.caller_reference == cfg.caller_reference)
        {
            return Err(aws_error(
                StatusCode::CONFLICT,
                "FieldLevelEncryptionConfigAlreadyExists",
                format!(
                    "FieldLevelEncryption with same CallerReference exists: {}",
                    existing.id
                ),
            ));
        }
        let id = generate_id_with_prefix("F");
        let etag = generate_id_with_prefix("E");
        let stored = StoredFieldLevelEncryption {
            id: id.clone(),
            etag: etag.clone(),
            last_modified_time: Utc::now(),
            config: cfg,
        };
        account
            .field_level_encryptions
            .insert(id.clone(), stored.clone());
        drop(state);
        let body = render_fle(&stored);
        Ok(xml_with_etag(StatusCode::CREATED, body, &etag, Some(&id)))
    }

    pub(crate) fn get_field_level_encryption(
        &self,
        route: &Route,
    ) -> Result<AwsResponse, AwsServiceError> {
        let id = route_id(route, "FieldLevelEncryption")?;
        let state = self.state.read();
        let f = state
            .accounts
            .get(DEFAULT_ACCOUNT)
            .and_then(|a| a.field_level_encryptions.get(&id).cloned())
            .ok_or_else(|| not_found("FieldLevelEncryption", &id))?;
        drop(state);
        let body = render_fle(&f);
        Ok(xml_with_etag(StatusCode::OK, body, &f.etag, None))
    }

    pub(crate) fn get_field_level_encryption_config(
        &self,
        route: &Route,
    ) -> Result<AwsResponse, AwsServiceError> {
        let id = route_id(route, "FieldLevelEncryption")?;
        let state = self.state.read();
        let f = state
            .accounts
            .get(DEFAULT_ACCOUNT)
            .and_then(|a| a.field_level_encryptions.get(&id).cloned())
            .ok_or_else(|| not_found("FieldLevelEncryption", &id))?;
        drop(state);
        let body = render_fle_config(&f.config);
        Ok(xml_with_etag(StatusCode::OK, body, &f.etag, None))
    }

    pub(crate) fn update_field_level_encryption_config(
        &self,
        req: &AwsRequest,
        route: &Route,
    ) -> Result<AwsResponse, AwsServiceError> {
        let id = route_id(route, "FieldLevelEncryption")?;
        let if_match = require_if_match(req)?;
        let cfg: FieldLevelEncryptionConfig = xml_io::from_xml_root(&req.body).map_err(|e| {
            invalid_argument(format!("invalid FieldLevelEncryptionConfig XML: {e}"))
        })?;
        let mut state = self.state.write();
        let account = state
            .accounts
            .get_mut(DEFAULT_ACCOUNT)
            .ok_or_else(|| not_found("FieldLevelEncryption", &id))?;
        let f = account
            .field_level_encryptions
            .get_mut(&id)
            .ok_or_else(|| not_found("FieldLevelEncryption", &id))?;
        if f.etag != if_match {
            return Err(precondition_failed());
        }
        if f.config.caller_reference != cfg.caller_reference {
            return Err(invalid_argument(
                "CallerReference cannot change on UpdateFieldLevelEncryptionConfig",
            ));
        }
        f.config = cfg;
        f.etag = generate_id_with_prefix("E");
        f.last_modified_time = Utc::now();
        let snap = f.clone();
        drop(state);
        let body = render_fle(&snap);
        Ok(xml_with_etag(StatusCode::OK, body, &snap.etag, None))
    }

    pub(crate) fn delete_field_level_encryption_config(
        &self,
        req: &AwsRequest,
        route: &Route,
    ) -> Result<AwsResponse, AwsServiceError> {
        let id = route_id(route, "FieldLevelEncryption")?;
        let if_match = require_if_match(req)?;
        let mut state = self.state.write();
        let account = state
            .accounts
            .get_mut(DEFAULT_ACCOUNT)
            .ok_or_else(|| not_found("FieldLevelEncryption", &id))?;
        let f = account
            .field_level_encryptions
            .get(&id)
            .ok_or_else(|| not_found("FieldLevelEncryption", &id))?;
        if f.etag != if_match {
            return Err(precondition_failed());
        }
        account.field_level_encryptions.remove(&id);
        drop(state);
        Ok(crate::policies::empty(StatusCode::NO_CONTENT))
    }

    pub(crate) fn list_field_level_encryption_configs(
        &self,
        _req: &AwsRequest,
    ) -> Result<AwsResponse, AwsServiceError> {
        let state = self.state.read();
        let mut items: Vec<StoredFieldLevelEncryption> = state
            .accounts
            .get(DEFAULT_ACCOUNT)
            .map(|a| a.field_level_encryptions.values().cloned().collect())
            .unwrap_or_default();
        drop(state);
        items.sort_by(|a, b| a.id.cmp(&b.id));

        let mut body = String::with_capacity(512);
        body.push_str(XML_DECL);
        body.push_str(&format!("<FieldLevelEncryptionList xmlns=\"{NS}\">"));
        body.push_str("<NextMarker></NextMarker>");
        body.push_str("<MaxItems>100</MaxItems>");
        body.push_str(&format!("<Quantity>{}</Quantity>", items.len()));
        body.push_str("<Items>");
        for f in &items {
            body.push_str("<FieldLevelEncryptionSummary>");
            body.push_str(&format!("<Id>{}</Id>", esc(&f.id)));
            body.push_str(&format!(
                "<LastModifiedTime>{}</LastModifiedTime>",
                rfc3339(&f.last_modified_time)
            ));
            if let Some(c) = &f.config.comment {
                body.push_str(&format!("<Comment>{}</Comment>", esc(c)));
            }
            body.push_str(&render_query_arg_profile_config(
                &f.config.query_arg_profile_config,
            ));
            body.push_str(&render_content_type_profile_config(
                &f.config.content_type_profile_config,
            ));
            body.push_str("</FieldLevelEncryptionSummary>");
        }
        body.push_str("</Items>");
        body.push_str("</FieldLevelEncryptionList>");
        Ok(xml_response(StatusCode::OK, body, HeaderMap::new()))
    }
}

// ─── Field-Level Encryption Profile ───────────────────────────────────

impl CloudFrontService {
    pub(crate) fn create_field_level_encryption_profile(
        &self,
        req: &AwsRequest,
    ) -> Result<AwsResponse, AwsServiceError> {
        let cfg: FieldLevelEncryptionProfileConfig =
            xml_io::from_xml_root(&req.body).map_err(|e| {
                invalid_argument(format!(
                    "invalid FieldLevelEncryptionProfileConfig XML: {e}"
                ))
            })?;
        if cfg.name.is_empty() {
            return Err(invalid_argument(
                "FieldLevelEncryptionProfileConfig.Name is required",
            ));
        }
        if cfg.caller_reference.is_empty() {
            return Err(invalid_argument("CallerReference is required"));
        }
        let mut state = self.state.write();
        let account = state
            .accounts
            .entry(DEFAULT_ACCOUNT.to_string())
            .or_default();
        if let Some(existing) = account
            .field_level_encryption_profiles
            .values()
            .find(|p| p.config.caller_reference == cfg.caller_reference)
        {
            return Err(aws_error(
                StatusCode::CONFLICT,
                "FieldLevelEncryptionProfileAlreadyExists",
                format!(
                    "FieldLevelEncryptionProfile with same CallerReference exists: {}",
                    existing.id
                ),
            ));
        }
        let id = generate_id_with_prefix("FP");
        let etag = generate_id_with_prefix("E");
        let stored = StoredFieldLevelEncryptionProfile {
            id: id.clone(),
            etag: etag.clone(),
            last_modified_time: Utc::now(),
            config: cfg,
        };
        account
            .field_level_encryption_profiles
            .insert(id.clone(), stored.clone());
        drop(state);
        let body = render_fle_profile(&stored);
        Ok(xml_with_etag(StatusCode::CREATED, body, &etag, Some(&id)))
    }

    pub(crate) fn get_field_level_encryption_profile(
        &self,
        route: &Route,
    ) -> Result<AwsResponse, AwsServiceError> {
        let id = route_id(route, "FieldLevelEncryptionProfile")?;
        let state = self.state.read();
        let p = state
            .accounts
            .get(DEFAULT_ACCOUNT)
            .and_then(|a| a.field_level_encryption_profiles.get(&id).cloned())
            .ok_or_else(|| not_found("FieldLevelEncryptionProfile", &id))?;
        drop(state);
        let body = render_fle_profile(&p);
        Ok(xml_with_etag(StatusCode::OK, body, &p.etag, None))
    }

    pub(crate) fn get_field_level_encryption_profile_config(
        &self,
        route: &Route,
    ) -> Result<AwsResponse, AwsServiceError> {
        let id = route_id(route, "FieldLevelEncryptionProfile")?;
        let state = self.state.read();
        let p = state
            .accounts
            .get(DEFAULT_ACCOUNT)
            .and_then(|a| a.field_level_encryption_profiles.get(&id).cloned())
            .ok_or_else(|| not_found("FieldLevelEncryptionProfile", &id))?;
        drop(state);
        let body = render_fle_profile_config(&p.config);
        Ok(xml_with_etag(StatusCode::OK, body, &p.etag, None))
    }

    pub(crate) fn update_field_level_encryption_profile(
        &self,
        req: &AwsRequest,
        route: &Route,
    ) -> Result<AwsResponse, AwsServiceError> {
        let id = route_id(route, "FieldLevelEncryptionProfile")?;
        let if_match = require_if_match(req)?;
        let cfg: FieldLevelEncryptionProfileConfig =
            xml_io::from_xml_root(&req.body).map_err(|e| {
                invalid_argument(format!(
                    "invalid FieldLevelEncryptionProfileConfig XML: {e}"
                ))
            })?;
        if cfg.name.is_empty() {
            return Err(invalid_argument(
                "FieldLevelEncryptionProfileConfig.Name is required",
            ));
        }
        let mut state = self.state.write();
        let account = state
            .accounts
            .get_mut(DEFAULT_ACCOUNT)
            .ok_or_else(|| not_found("FieldLevelEncryptionProfile", &id))?;
        let p = account
            .field_level_encryption_profiles
            .get_mut(&id)
            .ok_or_else(|| not_found("FieldLevelEncryptionProfile", &id))?;
        if p.etag != if_match {
            return Err(precondition_failed());
        }
        if p.config.caller_reference != cfg.caller_reference {
            return Err(invalid_argument(
                "CallerReference cannot change on UpdateFieldLevelEncryptionProfile",
            ));
        }
        p.config = cfg;
        p.etag = generate_id_with_prefix("E");
        p.last_modified_time = Utc::now();
        let snap = p.clone();
        drop(state);
        let body = render_fle_profile(&snap);
        Ok(xml_with_etag(StatusCode::OK, body, &snap.etag, None))
    }

    pub(crate) fn delete_field_level_encryption_profile(
        &self,
        req: &AwsRequest,
        route: &Route,
    ) -> Result<AwsResponse, AwsServiceError> {
        let id = route_id(route, "FieldLevelEncryptionProfile")?;
        let if_match = require_if_match(req)?;
        let mut state = self.state.write();
        let account = state
            .accounts
            .get_mut(DEFAULT_ACCOUNT)
            .ok_or_else(|| not_found("FieldLevelEncryptionProfile", &id))?;
        let p = account
            .field_level_encryption_profiles
            .get(&id)
            .ok_or_else(|| not_found("FieldLevelEncryptionProfile", &id))?;
        if p.etag != if_match {
            return Err(precondition_failed());
        }
        account.field_level_encryption_profiles.remove(&id);
        drop(state);
        Ok(crate::policies::empty(StatusCode::NO_CONTENT))
    }

    pub(crate) fn list_field_level_encryption_profiles(
        &self,
        _req: &AwsRequest,
    ) -> Result<AwsResponse, AwsServiceError> {
        let state = self.state.read();
        let mut items: Vec<StoredFieldLevelEncryptionProfile> = state
            .accounts
            .get(DEFAULT_ACCOUNT)
            .map(|a| {
                a.field_level_encryption_profiles
                    .values()
                    .cloned()
                    .collect()
            })
            .unwrap_or_default();
        drop(state);
        items.sort_by(|a, b| a.config.name.cmp(&b.config.name));

        let mut body = String::with_capacity(512);
        body.push_str(XML_DECL);
        body.push_str(&format!("<FieldLevelEncryptionProfileList xmlns=\"{NS}\">"));
        body.push_str("<NextMarker></NextMarker>");
        body.push_str("<MaxItems>100</MaxItems>");
        body.push_str(&format!("<Quantity>{}</Quantity>", items.len()));
        body.push_str("<Items>");
        for p in &items {
            body.push_str("<FieldLevelEncryptionProfileSummary>");
            body.push_str(&format!("<Id>{}</Id>", esc(&p.id)));
            body.push_str(&format!(
                "<LastModifiedTime>{}</LastModifiedTime>",
                rfc3339(&p.last_modified_time)
            ));
            body.push_str(&format!("<Name>{}</Name>", esc(&p.config.name)));
            body.push_str(&render_encryption_entities(&p.config.encryption_entities));
            if let Some(c) = &p.config.comment {
                body.push_str(&format!("<Comment>{}</Comment>", esc(c)));
            }
            body.push_str("</FieldLevelEncryptionProfileSummary>");
        }
        body.push_str("</Items>");
        body.push_str("</FieldLevelEncryptionProfileList>");
        Ok(xml_response(StatusCode::OK, body, HeaderMap::new()))
    }
}

// ─── Realtime Log Config ──────────────────────────────────────────────

impl CloudFrontService {
    pub(crate) fn create_realtime_log_config(
        &self,
        req: &AwsRequest,
    ) -> Result<AwsResponse, AwsServiceError> {
        let parsed: CreateRealtimeLogConfigRequest =
            xml_io::from_xml_root(&req.body).map_err(|e| {
                invalid_argument(format!("invalid CreateRealtimeLogConfigRequest XML: {e}"))
            })?;
        if parsed.name.is_empty() {
            return Err(invalid_argument("Name is required"));
        }
        let mut state = self.state.write();
        let account = state
            .accounts
            .entry(DEFAULT_ACCOUNT.to_string())
            .or_default();
        let arn = format!(
            "arn:aws:cloudfront::{}:realtime-log-config/{}",
            DEFAULT_ACCOUNT, parsed.name
        );
        if account.realtime_log_configs.contains_key(&arn) {
            return Err(aws_error(
                StatusCode::CONFLICT,
                "RealtimeLogConfigAlreadyExists",
                format!("RealtimeLogConfig {} already exists", parsed.name),
            ));
        }
        let stored = StoredRealtimeLogConfig {
            arn: arn.clone(),
            name: parsed.name,
            sampling_rate: parsed.sampling_rate,
            end_points: parsed.end_points,
            fields: parsed.fields,
        };
        account
            .realtime_log_configs
            .insert(arn.clone(), stored.clone());
        drop(state);
        let body = render_realtime_log(&stored, "CreateRealtimeLogConfigResult");
        Ok(xml_response(StatusCode::CREATED, body, HeaderMap::new()))
    }

    pub(crate) fn get_realtime_log_config(
        &self,
        req: &AwsRequest,
    ) -> Result<AwsResponse, AwsServiceError> {
        let parsed: GetOrDeleteRealtimeLogConfigRequest = xml_io::from_xml_root(&req.body)
            .map_err(|e| {
                invalid_argument(format!("invalid GetRealtimeLogConfigRequest XML: {e}"))
            })?;
        let key = self.resolve_rtl_key(&parsed)?;
        let state = self.state.read();
        let r = state
            .accounts
            .get(DEFAULT_ACCOUNT)
            .and_then(|a| a.realtime_log_configs.get(&key).cloned())
            .ok_or_else(|| not_found("RealtimeLogConfig", &key))?;
        drop(state);
        let body = render_realtime_log(&r, "GetRealtimeLogConfigResult");
        Ok(xml_response(StatusCode::OK, body, HeaderMap::new()))
    }

    pub(crate) fn update_realtime_log_config(
        &self,
        req: &AwsRequest,
    ) -> Result<AwsResponse, AwsServiceError> {
        let parsed: UpdateRealtimeLogConfigRequest =
            xml_io::from_xml_root(&req.body).map_err(|e| {
                invalid_argument(format!("invalid UpdateRealtimeLogConfigRequest XML: {e}"))
            })?;
        if parsed.arn.is_empty() {
            return Err(invalid_argument("ARN is required"));
        }
        let mut state = self.state.write();
        let account = state
            .accounts
            .get_mut(DEFAULT_ACCOUNT)
            .ok_or_else(|| not_found("RealtimeLogConfig", &parsed.arn))?;
        let r = account
            .realtime_log_configs
            .get_mut(&parsed.arn)
            .ok_or_else(|| not_found("RealtimeLogConfig", &parsed.arn))?;
        r.sampling_rate = parsed.sampling_rate;
        r.end_points = parsed.end_points;
        r.fields = parsed.fields;
        let snap = r.clone();
        drop(state);
        let body = render_realtime_log(&snap, "UpdateRealtimeLogConfigResult");
        Ok(xml_response(StatusCode::OK, body, HeaderMap::new()))
    }

    pub(crate) fn delete_realtime_log_config(
        &self,
        req: &AwsRequest,
    ) -> Result<AwsResponse, AwsServiceError> {
        let parsed: GetOrDeleteRealtimeLogConfigRequest = xml_io::from_xml_root(&req.body)
            .map_err(|e| {
                invalid_argument(format!("invalid DeleteRealtimeLogConfigRequest XML: {e}"))
            })?;
        let key = self.resolve_rtl_key(&parsed)?;
        let mut state = self.state.write();
        let account = state
            .accounts
            .get_mut(DEFAULT_ACCOUNT)
            .ok_or_else(|| not_found("RealtimeLogConfig", &key))?;
        if account.realtime_log_configs.remove(&key).is_none() {
            return Err(not_found("RealtimeLogConfig", &key));
        }
        drop(state);
        Ok(crate::policies::empty(StatusCode::NO_CONTENT))
    }

    pub(crate) fn list_realtime_log_configs(
        &self,
        _req: &AwsRequest,
    ) -> Result<AwsResponse, AwsServiceError> {
        let state = self.state.read();
        let mut items: Vec<StoredRealtimeLogConfig> = state
            .accounts
            .get(DEFAULT_ACCOUNT)
            .map(|a| a.realtime_log_configs.values().cloned().collect())
            .unwrap_or_default();
        drop(state);
        items.sort_by(|a, b| a.name.cmp(&b.name));

        let mut body = String::with_capacity(512);
        body.push_str(XML_DECL);
        body.push_str(&format!("<RealtimeLogConfigs xmlns=\"{NS}\">"));
        body.push_str("<MaxItems>100</MaxItems>");
        body.push_str(&format!("<IsTruncated>{}</IsTruncated>", false));
        body.push_str("<Marker></Marker>");
        body.push_str("<Items>");
        for r in &items {
            body.push_str("<member>");
            push_realtime_log_inner(&mut body, r);
            body.push_str("</member>");
        }
        body.push_str("</Items>");
        body.push_str("</RealtimeLogConfigs>");
        Ok(xml_response(StatusCode::OK, body, HeaderMap::new()))
    }

    fn resolve_rtl_key(
        &self,
        parsed: &GetOrDeleteRealtimeLogConfigRequest,
    ) -> Result<String, AwsServiceError> {
        if let Some(arn) = &parsed.arn {
            if !arn.is_empty() {
                return Ok(arn.clone());
            }
        }
        if let Some(name) = &parsed.name {
            if !name.is_empty() {
                return Ok(format!(
                    "arn:aws:cloudfront::{}:realtime-log-config/{}",
                    DEFAULT_ACCOUNT, name
                ));
            }
        }
        Err(invalid_argument("Either Name or ARN must be specified"))
    }
}

// ─── XML render helpers ───────────────────────────────────────────────

fn render_fle(f: &StoredFieldLevelEncryption) -> String {
    let mut out = String::with_capacity(512);
    out.push_str(XML_DECL);
    out.push_str(&format!("<FieldLevelEncryption xmlns=\"{NS}\">"));
    out.push_str(&format!("<Id>{}</Id>", esc(&f.id)));
    out.push_str(&format!(
        "<LastModifiedTime>{}</LastModifiedTime>",
        rfc3339(&f.last_modified_time)
    ));
    out.push_str(&render_fle_config_inner(&f.config));
    out.push_str("</FieldLevelEncryption>");
    out
}

fn render_fle_config(cfg: &FieldLevelEncryptionConfig) -> String {
    let mut out = String::with_capacity(512);
    out.push_str(XML_DECL);
    out.push_str(&format!("<FieldLevelEncryptionConfig xmlns=\"{NS}\">"));
    out.push_str(&render_fle_config_body(cfg));
    out.push_str("</FieldLevelEncryptionConfig>");
    out
}

fn render_fle_config_inner(cfg: &FieldLevelEncryptionConfig) -> String {
    let mut out = String::with_capacity(512);
    out.push_str("<FieldLevelEncryptionConfig>");
    out.push_str(&render_fle_config_body(cfg));
    out.push_str("</FieldLevelEncryptionConfig>");
    out
}

fn render_fle_config_body(cfg: &FieldLevelEncryptionConfig) -> String {
    let mut out = String::with_capacity(512);
    out.push_str(&format!(
        "<CallerReference>{}</CallerReference>",
        esc(&cfg.caller_reference)
    ));
    if let Some(c) = &cfg.comment {
        out.push_str(&format!("<Comment>{}</Comment>", esc(c)));
    }
    out.push_str(&render_query_arg_profile_config(
        &cfg.query_arg_profile_config,
    ));
    out.push_str(&render_content_type_profile_config(
        &cfg.content_type_profile_config,
    ));
    out
}

fn render_query_arg_profile_config(cfg: &crate::fle::QueryArgProfileConfig) -> String {
    let mut out = String::with_capacity(128);
    out.push_str("<QueryArgProfileConfig>");
    out.push_str(&format!(
        "<ForwardWhenQueryArgProfileIsUnknown>{}</ForwardWhenQueryArgProfileIsUnknown>",
        cfg.forward_when_query_arg_profile_is_unknown
    ));
    if let Some(qp) = &cfg.query_arg_profiles {
        out.push_str("<QueryArgProfiles>");
        out.push_str(&format!("<Quantity>{}</Quantity>", qp.quantity));
        if let Some(items) = &qp.items {
            out.push_str("<Items>");
            for q in &items.query_arg_profile {
                out.push_str("<QueryArgProfile>");
                out.push_str(&format!("<QueryArg>{}</QueryArg>", esc(&q.query_arg)));
                out.push_str(&format!("<ProfileId>{}</ProfileId>", esc(&q.profile_id)));
                out.push_str("</QueryArgProfile>");
            }
            out.push_str("</Items>");
        }
        out.push_str("</QueryArgProfiles>");
    }
    out.push_str("</QueryArgProfileConfig>");
    out
}

fn render_content_type_profile_config(cfg: &crate::fle::ContentTypeProfileConfig) -> String {
    let mut out = String::with_capacity(128);
    out.push_str("<ContentTypeProfileConfig>");
    out.push_str(&format!(
        "<ForwardWhenContentTypeIsUnknown>{}</ForwardWhenContentTypeIsUnknown>",
        cfg.forward_when_content_type_is_unknown
    ));
    if let Some(ct) = &cfg.content_type_profiles {
        out.push_str("<ContentTypeProfiles>");
        out.push_str(&format!("<Quantity>{}</Quantity>", ct.quantity));
        if let Some(items) = &ct.items {
            out.push_str("<Items>");
            for c in &items.content_type_profile {
                out.push_str("<ContentTypeProfile>");
                out.push_str(&format!("<Format>{}</Format>", esc(&c.format)));
                if let Some(p) = &c.profile_id {
                    out.push_str(&format!("<ProfileId>{}</ProfileId>", esc(p)));
                }
                out.push_str(&format!(
                    "<ContentType>{}</ContentType>",
                    esc(&c.content_type)
                ));
                out.push_str("</ContentTypeProfile>");
            }
            out.push_str("</Items>");
        }
        out.push_str("</ContentTypeProfiles>");
    }
    out.push_str("</ContentTypeProfileConfig>");
    out
}

fn render_fle_profile(p: &StoredFieldLevelEncryptionProfile) -> String {
    let mut out = String::with_capacity(512);
    out.push_str(XML_DECL);
    out.push_str(&format!("<FieldLevelEncryptionProfile xmlns=\"{NS}\">"));
    out.push_str(&format!("<Id>{}</Id>", esc(&p.id)));
    out.push_str(&format!(
        "<LastModifiedTime>{}</LastModifiedTime>",
        rfc3339(&p.last_modified_time)
    ));
    out.push_str(&render_fle_profile_config_inner(&p.config));
    out.push_str("</FieldLevelEncryptionProfile>");
    out
}

fn render_fle_profile_config(cfg: &FieldLevelEncryptionProfileConfig) -> String {
    let mut out = String::with_capacity(512);
    out.push_str(XML_DECL);
    out.push_str(&format!(
        "<FieldLevelEncryptionProfileConfig xmlns=\"{NS}\">"
    ));
    out.push_str(&render_fle_profile_config_body(cfg));
    out.push_str("</FieldLevelEncryptionProfileConfig>");
    out
}

fn render_fle_profile_config_inner(cfg: &FieldLevelEncryptionProfileConfig) -> String {
    let mut out = String::with_capacity(512);
    out.push_str("<FieldLevelEncryptionProfileConfig>");
    out.push_str(&render_fle_profile_config_body(cfg));
    out.push_str("</FieldLevelEncryptionProfileConfig>");
    out
}

fn render_fle_profile_config_body(cfg: &FieldLevelEncryptionProfileConfig) -> String {
    let mut out = String::with_capacity(512);
    out.push_str(&format!("<Name>{}</Name>", esc(&cfg.name)));
    out.push_str(&format!(
        "<CallerReference>{}</CallerReference>",
        esc(&cfg.caller_reference)
    ));
    if let Some(c) = &cfg.comment {
        out.push_str(&format!("<Comment>{}</Comment>", esc(c)));
    }
    out.push_str(&render_encryption_entities(&cfg.encryption_entities));
    out
}

fn render_encryption_entities(ee: &crate::fle::EncryptionEntities) -> String {
    let mut out = String::with_capacity(128);
    out.push_str("<EncryptionEntities>");
    out.push_str(&format!("<Quantity>{}</Quantity>", ee.quantity));
    if let Some(items) = &ee.items {
        out.push_str("<Items>");
        for e in &items.encryption_entity {
            out.push_str("<EncryptionEntity>");
            out.push_str(&format!(
                "<PublicKeyId>{}</PublicKeyId>",
                esc(&e.public_key_id)
            ));
            out.push_str(&format!("<ProviderId>{}</ProviderId>", esc(&e.provider_id)));
            out.push_str("<FieldPatterns>");
            out.push_str(&format!(
                "<Quantity>{}</Quantity>",
                e.field_patterns.quantity
            ));
            if let Some(it) = &e.field_patterns.items {
                out.push_str("<Items>");
                for fp in &it.field_pattern {
                    out.push_str(&format!("<FieldPattern>{}</FieldPattern>", esc(fp)));
                }
                out.push_str("</Items>");
            }
            out.push_str("</FieldPatterns>");
            out.push_str("</EncryptionEntity>");
        }
        out.push_str("</Items>");
    }
    out.push_str("</EncryptionEntities>");
    out
}

fn render_realtime_log(r: &StoredRealtimeLogConfig, root: &str) -> String {
    let mut out = String::with_capacity(512);
    out.push_str(XML_DECL);
    out.push_str(&format!("<{root} xmlns=\"{NS}\">"));
    out.push_str("<RealtimeLogConfig>");
    push_realtime_log_inner(&mut out, r);
    out.push_str("</RealtimeLogConfig>");
    out.push_str(&format!("</{root}>"));
    out
}

fn push_realtime_log_inner(out: &mut String, r: &StoredRealtimeLogConfig) {
    out.push_str(&format!("<ARN>{}</ARN>", esc(&r.arn)));
    out.push_str(&format!("<Name>{}</Name>", esc(&r.name)));
    out.push_str(&format!("<SamplingRate>{}</SamplingRate>", r.sampling_rate));
    out.push_str("<EndPoints>");
    for ep in &r.end_points.member {
        out.push_str("<member>");
        out.push_str(&format!(
            "<StreamType>{}</StreamType>",
            esc(&ep.stream_type)
        ));
        out.push_str("<KinesisStreamConfig>");
        out.push_str(&format!(
            "<RoleARN>{}</RoleARN>",
            esc(&ep.kinesis_stream_config.role_arn)
        ));
        out.push_str(&format!(
            "<StreamARN>{}</StreamARN>",
            esc(&ep.kinesis_stream_config.stream_arn)
        ));
        out.push_str("</KinesisStreamConfig>");
        out.push_str("</member>");
    }
    out.push_str("</EndPoints>");
    out.push_str("<Fields>");
    for f in &r.fields.field {
        out.push_str(&format!("<Field>{}</Field>", esc(f)));
    }
    out.push_str("</Fields>");
}
