//! `xml_renderers` concerns from rds/extras.rs (audit-2026-05-19).

use super::*;

/// Render a JSON string array as an AWS query `<Wrapper><member>..</member></Wrapper>`
/// list, or a self-closing `<Wrapper/>` when empty/absent.
pub(super) fn json_str_array_xml(v: &Value, wrapper: &str) -> String {
    let members: Vec<&str> = v
        .as_array()
        .map(|a| a.iter().filter_map(|x| x.as_str()).collect())
        .unwrap_or_default();
    if members.is_empty() {
        return format!("<{wrapper}/>");
    }
    let body: String = members
        .iter()
        .map(|m| format!("<member>{}</member>", xml_escape(m)))
        .collect();
    format!("<{wrapper}>{body}</{wrapper}>")
}

pub(super) fn db_cluster_member_xml(v: &Value) -> String {
    let mut out = String::new();
    out.push_str(&format!(
        "          <DBClusterIdentifier>{}</DBClusterIdentifier>\n",
        xml_escape(v["DBClusterIdentifier"].as_str().unwrap_or(""))
    ));
    out.push_str(&format!(
        "          <DBClusterArn>{}</DBClusterArn>\n",
        xml_escape(v["DBClusterArn"].as_str().unwrap_or(""))
    ));
    out.push_str(&format!(
        "          <Status>{}</Status>\n",
        xml_escape(v["Status"].as_str().unwrap_or("available"))
    ));
    if let Some(s) = v["Engine"].as_str() {
        out.push_str(&format!("          <Engine>{}</Engine>\n", xml_escape(s)));
    }
    if let Some(s) = v["EngineVersion"].as_str() {
        out.push_str(&format!(
            "          <EngineVersion>{}</EngineVersion>\n",
            xml_escape(s)
        ));
    }
    if let Some(s) = v["MasterUsername"].as_str() {
        out.push_str(&format!(
            "          <MasterUsername>{}</MasterUsername>\n",
            xml_escape(s)
        ));
    }
    if let Some(s) = v["DatabaseName"].as_str() {
        out.push_str(&format!(
            "          <DatabaseName>{}</DatabaseName>\n",
            xml_escape(s)
        ));
    }
    if let Some(s) = v["Endpoint"].as_str() {
        out.push_str(&format!(
            "          <Endpoint>{}</Endpoint>\n",
            xml_escape(s)
        ));
    }
    if let Some(s) = v["ReaderEndpoint"].as_str() {
        out.push_str(&format!(
            "          <ReaderEndpoint>{}</ReaderEndpoint>\n",
            xml_escape(s)
        ));
    }
    if let Some(n) = v["Port"].as_i64() {
        out.push_str(&format!("          <Port>{}</Port>\n", n));
    }
    if let Some(n) = v["AllocatedStorage"].as_i64() {
        out.push_str(&format!(
            "          <AllocatedStorage>{}</AllocatedStorage>\n",
            n
        ));
    }
    if let Some(n) = v["BackupRetentionPeriod"].as_i64() {
        out.push_str(&format!(
            "          <BackupRetentionPeriod>{}</BackupRetentionPeriod>\n",
            n
        ));
    }
    if let Some(b) = v["StorageEncrypted"].as_bool() {
        out.push_str(&format!(
            "          <StorageEncrypted>{}</StorageEncrypted>\n",
            b
        ));
    }
    if let Some(s) = v["KmsKeyId"].as_str() {
        out.push_str(&format!(
            "          <KmsKeyId>{}</KmsKeyId>\n",
            xml_escape(s)
        ));
    }
    if let Some(b) = v["DeletionProtection"].as_bool() {
        out.push_str(&format!(
            "          <DeletionProtection>{}</DeletionProtection>\n",
            b
        ));
    }
    if let Some(s) = v["DBSubnetGroup"].as_str() {
        out.push_str(&format!(
            "          <DBSubnetGroup>{}</DBSubnetGroup>\n",
            xml_escape(s)
        ));
    }
    if let Some(s) = v["DbClusterResourceId"].as_str() {
        out.push_str(&format!(
            "          <DbClusterResourceId>{}</DbClusterResourceId>\n",
            xml_escape(s)
        ));
    }
    if let Some(s) = v["ClusterCreateTime"].as_str() {
        out.push_str(&format!(
            "          <ClusterCreateTime>{}</ClusterCreateTime>\n",
            xml_escape(s)
        ));
    }
    if let Some(s) = v["WriterDBInstanceIdentifier"].as_str() {
        out.push_str(&format!(
            "          <WriterDBInstanceIdentifier>{}</WriterDBInstanceIdentifier>\n",
            xml_escape(s)
        ));
    }
    if let Some(s) = v["BacktrackTo"].as_str() {
        out.push_str(&format!(
            "          <BacktrackTo>{}</BacktrackTo>\n",
            xml_escape(s)
        ));
    }
    if let Some(n) = v["BacktrackConsumedChangeRecords"].as_i64() {
        out.push_str(&format!(
            "          <BacktrackConsumedChangeRecords>{}</BacktrackConsumedChangeRecords>\n",
            n
        ));
    }
    if let Some(s) = v["EarliestRestorableTime"].as_str() {
        out.push_str(&format!(
            "          <EarliestRestorableTime>{}</EarliestRestorableTime>\n",
            xml_escape(s)
        ));
    }
    if let Some(s) = v["LatestRestorableTime"].as_str() {
        out.push_str(&format!(
            "          <LatestRestorableTime>{}</LatestRestorableTime>\n",
            xml_escape(s)
        ));
    }
    if let Some(s) = v["PreferredBackupWindow"].as_str() {
        out.push_str(&format!(
            "          <PreferredBackupWindow>{}</PreferredBackupWindow>\n",
            xml_escape(s)
        ));
    }
    if let Some(s) = v["PreferredMaintenanceWindow"].as_str() {
        out.push_str(&format!(
            "          <PreferredMaintenanceWindow>{}</PreferredMaintenanceWindow>\n",
            xml_escape(s)
        ));
    }
    if let Some(s) = v["DBClusterParameterGroup"].as_str() {
        out.push_str(&format!(
            "          <DBClusterParameterGroup>{}</DBClusterParameterGroup>\n",
            xml_escape(s)
        ));
    }
    if let Some(s) = v["DBClusterParameterGroupName"].as_str() {
        out.push_str(&format!(
            "          <DBClusterParameterGroup>{}</DBClusterParameterGroup>\n",
            xml_escape(s)
        ));
    }
    if let Some(arr) = v["VpcSecurityGroupIds"].as_array() {
        out.push_str("          <VpcSecurityGroups>\n");
        for sg in arr {
            if let Some(s) = sg.as_str() {
                out.push_str(&format!(
                    "            <VpcSecurityGroupMembership>\n              <VpcSecurityGroupId>{}</VpcSecurityGroupId>\n              <Status>active</Status>\n            </VpcSecurityGroupMembership>\n",
                    xml_escape(s)
                ));
            }
        }
        out.push_str("          </VpcSecurityGroups>\n");
    }
    if let Some(arr) = v["EnabledCloudwatchLogsExports"].as_array() {
        out.push_str("          <EnabledCloudwatchLogsExports>\n");
        for t in arr {
            if let Some(s) = t.as_str() {
                out.push_str(&format!("            <member>{}</member>\n", xml_escape(s)));
            }
        }
        out.push_str("          </EnabledCloudwatchLogsExports>\n");
    }
    if let Some(arr) = v["DBClusterMembers"].as_array() {
        out.push_str("          <DBClusterMembers>\n");
        for m in arr {
            let inst = m["DBInstanceIdentifier"].as_str().unwrap_or("");
            let writer = m["IsClusterWriter"].as_bool().unwrap_or(false);
            let pg = m["DBClusterParameterGroupStatus"]
                .as_str()
                .unwrap_or("in-sync");
            let promotion = m["PromotionTier"].as_i64().unwrap_or(1);
            out.push_str(&format!(
                "            <DBClusterMember>\n              <DBInstanceIdentifier>{}</DBInstanceIdentifier>\n              <IsClusterWriter>{}</IsClusterWriter>\n              <DBClusterParameterGroupStatus>{}</DBClusterParameterGroupStatus>\n              <PromotionTier>{}</PromotionTier>\n            </DBClusterMember>\n",
                xml_escape(inst),
                writer,
                xml_escape(pg),
                promotion
            ));
        }
        out.push_str("          </DBClusterMembers>\n");
    }

    // Scalar fields ModifyDBCluster (and CreateDBCluster) persist that the
    // hand-written renderer above omitted, so DescribeDBClusters reflects a
    // modify instead of always echoing the create-time defaults (bug-audit
    // 2026-06-20, 1.17). Keys already rendered above are deliberately excluded.
    for key in [
        "StorageType",
        "DBClusterInstanceClass",
        "EngineMode",
        "NetworkType",
        "MonitoringRoleArn",
        "PerformanceInsightsKMSKeyId",
        "Domain",
        "DomainIAMRoleName",
        "CACertificateIdentifier",
        "MasterUserSecretKmsKeyId",
        "AwsBackupRecoveryPointArn",
        "GlobalWriteForwardingStatus",
        "LocalWriteForwardingStatus",
    ] {
        if let Some(s) = v[key].as_str() {
            out.push_str(&format!("          <{key}>{}</{key}>\n", xml_escape(s)));
        }
    }
    for key in [
        "Iops",
        "BacktrackWindow",
        "MonitoringInterval",
        "PerformanceInsightsRetentionPeriod",
    ] {
        if let Some(n) = v[key].as_i64() {
            out.push_str(&format!("          <{key}>{n}</{key}>\n"));
        }
    }
    for key in [
        "IAMDatabaseAuthenticationEnabled",
        "CopyTagsToSnapshot",
        "AutoMinorVersionUpgrade",
        "HttpEndpointEnabled",
        "PerformanceInsightsEnabled",
        "ManageMasterUserPassword",
    ] {
        if let Some(b) = v[key].as_bool() {
            out.push_str(&format!("          <{key}>{b}</{key}>\n"));
        }
    }
    // ServerlessV2ScalingConfiguration is stored as flattened
    // `ServerlessV2ScalingConfiguration.{Min,Max}Capacity` keys; render it back
    // as the nested element AWS returns.
    let v2_min = v["ServerlessV2ScalingConfiguration.MinCapacity"].as_str();
    let v2_max = v["ServerlessV2ScalingConfiguration.MaxCapacity"].as_str();
    if v2_min.is_some() || v2_max.is_some() {
        out.push_str("          <ServerlessV2ScalingConfiguration>\n");
        if let Some(m) = v2_min {
            out.push_str(&format!(
                "            <MinCapacity>{}</MinCapacity>\n",
                xml_escape(m)
            ));
        }
        if let Some(m) = v2_max {
            out.push_str(&format!(
                "            <MaxCapacity>{}</MaxCapacity>\n",
                xml_escape(m)
            ));
        }
        out.push_str("          </ServerlessV2ScalingConfiguration>\n");
    }
    out
}

pub(super) fn cluster_snapshot_member_xml(v: &Value) -> String {
    format!(
        "          <DBClusterSnapshotIdentifier>{}</DBClusterSnapshotIdentifier>\n          <DBClusterSnapshotArn>{}</DBClusterSnapshotArn>\n          <DBClusterIdentifier>{}</DBClusterIdentifier>\n          <Status>{}</Status>",
        xml_escape(v["DBClusterSnapshotIdentifier"].as_str().unwrap_or("")),
        xml_escape(v["DBClusterSnapshotArn"].as_str().unwrap_or("")),
        xml_escape(v["DBClusterIdentifier"].as_str().unwrap_or("")),
        xml_escape(v["Status"].as_str().unwrap_or("available")),
    )
}

pub(super) fn cluster_pg_xml(name: &str, arn: &str, family: &str, description: &str) -> String {
    format!(
        "    <DBClusterParameterGroup>\n      <DBClusterParameterGroupName>{}</DBClusterParameterGroupName>\n      <DBClusterParameterGroupArn>{}</DBClusterParameterGroupArn>\n      <DBParameterGroupFamily>{}</DBParameterGroupFamily>\n      <Description>{}</Description>\n    </DBClusterParameterGroup>",
        xml_escape(name), xml_escape(arn), xml_escape(family), xml_escape(description),
    )
}

pub(super) fn cluster_pg_member_xml(v: &Value) -> String {
    format!(
        "          <DBClusterParameterGroupName>{}</DBClusterParameterGroupName>\n          <DBClusterParameterGroupArn>{}</DBClusterParameterGroupArn>\n          <DBParameterGroupFamily>{}</DBParameterGroupFamily>\n          <Description>{}</Description>",
        xml_escape(v["DBClusterParameterGroupName"].as_str().unwrap_or("")),
        xml_escape(v["DBClusterParameterGroupArn"].as_str().unwrap_or("")),
        xml_escape(v["DBParameterGroupFamily"].as_str().unwrap_or("")),
        xml_escape(v["Description"].as_str().unwrap_or("")),
    )
}

pub(super) fn cluster_endpoint_xml(v: &Value) -> String {
    format!(
        "          <DBClusterEndpointIdentifier>{}</DBClusterEndpointIdentifier>\n          <DBClusterIdentifier>{}</DBClusterIdentifier>\n          <Endpoint>{}</Endpoint>\n          <EndpointType>{}</EndpointType>\n          <Status>{}</Status>",
        xml_escape(v["DBClusterEndpointIdentifier"].as_str().unwrap_or("")),
        xml_escape(v["DBClusterIdentifier"].as_str().unwrap_or("")),
        xml_escape(v["Endpoint"].as_str().unwrap_or("")),
        xml_escape(v["EndpointType"].as_str().unwrap_or("")),
        xml_escape(v["Status"].as_str().unwrap_or("available")),
    )
}

pub(super) fn proxy_xml(v: &Value) -> String {
    format!(
        "          <DBProxyName>{}</DBProxyName>\n          <DBProxyArn>{}</DBProxyArn>\n          <Status>{}</Status>\n          <EngineFamily>{}</EngineFamily>",
        xml_escape(v["DBProxyName"].as_str().unwrap_or("")),
        xml_escape(v["DBProxyArn"].as_str().unwrap_or("")),
        xml_escape(v["Status"].as_str().unwrap_or("available")),
        xml_escape(v["EngineFamily"].as_str().unwrap_or("POSTGRESQL")),
    )
}

pub(super) fn security_group_xml(v: &Value) -> String {
    format!(
        "          <DBSecurityGroupName>{}</DBSecurityGroupName>\n          <DBSecurityGroupDescription>{}</DBSecurityGroupDescription>\n          <OwnerId>{}</OwnerId>",
        xml_escape(v["DBSecurityGroupName"].as_str().unwrap_or("")),
        xml_escape(v["DBSecurityGroupDescription"].as_str().unwrap_or("")),
        xml_escape(v["OwnerId"].as_str().unwrap_or("000000000000")),
    )
}

pub(super) fn option_group_xml(v: &Value) -> String {
    let options = v["Options"]
        .as_array()
        .map(|opts| {
            opts.iter()
                .map(|o| {
                    let mut inner = format!(
                        "<OptionName>{}</OptionName>",
                        xml_escape(o["OptionName"].as_str().unwrap_or(""))
                    );
                    let port = o.get("Port").and_then(|p| {
                        p.as_i64()
                            .or_else(|| p.as_str().and_then(|s| s.parse().ok()))
                    });
                    if let Some(p) = port {
                        inner.push_str(&format!("<Port>{p}</Port>"));
                    }
                    if let Some(ver) = o.get("OptionVersion").and_then(|x| x.as_str()) {
                        inner.push_str(&format!(
                            "<OptionVersion>{}</OptionVersion>",
                            xml_escape(ver)
                        ));
                    }
                    format!("<Option>{inner}</Option>")
                })
                .collect::<String>()
        })
        .unwrap_or_default();
    let options_xml = if options.is_empty() {
        "<Options/>".to_string()
    } else {
        format!("<Options>{options}</Options>")
    };
    format!(
        "          <OptionGroupName>{}</OptionGroupName>\n          <OptionGroupArn>{}</OptionGroupArn>\n          <EngineName>{}</EngineName>\n          <MajorEngineVersion>{}</MajorEngineVersion>\n          <OptionGroupDescription>{}</OptionGroupDescription>\n          <AllowsVpcAndNonVpcInstanceMemberships>true</AllowsVpcAndNonVpcInstanceMemberships>\n          {}",
        xml_escape(v["OptionGroupName"].as_str().unwrap_or("")),
        xml_escape(v["OptionGroupArn"].as_str().unwrap_or("")),
        xml_escape(v["EngineName"].as_str().unwrap_or("")),
        xml_escape(v["MajorEngineVersion"].as_str().unwrap_or("")),
        xml_escape(v["OptionGroupDescription"].as_str().unwrap_or("")),
        options_xml,
    )
}

pub(super) fn event_sub_xml(v: &Value) -> String {
    format!(
        "          <CustSubscriptionId>{}</CustSubscriptionId>\n          <CustomerAwsId>{}</CustomerAwsId>\n          <EventSubscriptionArn>{}</EventSubscriptionArn>\n          <SnsTopicArn>{}</SnsTopicArn>\n          <SourceType>{}</SourceType>\n          <Status>{}</Status>\n          <Enabled>{}</Enabled>\n          {}\n          {}",
        xml_escape(v["CustSubscriptionId"].as_str().unwrap_or("")),
        xml_escape(v["CustomerAwsId"].as_str().unwrap_or("")),
        xml_escape(v["EventSubscriptionArn"].as_str().unwrap_or("")),
        xml_escape(v["SnsTopicArn"].as_str().unwrap_or("")),
        xml_escape(v["SourceType"].as_str().unwrap_or("")),
        xml_escape(v["Status"].as_str().unwrap_or("active")),
        v["Enabled"].as_bool().unwrap_or(true),
        json_str_array_xml(&v["EventCategoriesList"], "EventCategoriesList"),
        json_str_array_xml(&v["SourceIdsList"], "SourceIdsList"),
    )
}

pub(super) fn global_cluster_xml(v: &Value) -> String {
    format!(
        "          <GlobalClusterIdentifier>{}</GlobalClusterIdentifier>\n          <GlobalClusterArn>{}</GlobalClusterArn>\n          <GlobalClusterResourceId>{}</GlobalClusterResourceId>\n          <Endpoint>{}</Endpoint>\n          <Status>{}</Status>\n          <Engine>{}</Engine>\n          <EngineVersion>{}</EngineVersion>\n          <EngineLifecycleSupport>{}</EngineLifecycleSupport>\n          <DatabaseName>{}</DatabaseName>\n          <DeletionProtection>{}</DeletionProtection>\n          <StorageEncrypted>{}</StorageEncrypted>",
        xml_escape(v["GlobalClusterIdentifier"].as_str().unwrap_or("")),
        xml_escape(v["GlobalClusterArn"].as_str().unwrap_or("")),
        xml_escape(v["GlobalClusterResourceId"].as_str().unwrap_or("")),
        xml_escape(v["Endpoint"].as_str().unwrap_or("")),
        xml_escape(v["Status"].as_str().unwrap_or("available")),
        xml_escape(v["Engine"].as_str().unwrap_or("aurora-postgresql")),
        xml_escape(v["EngineVersion"].as_str().unwrap_or("")),
        xml_escape(v["EngineLifecycleSupport"].as_str().unwrap_or("open-source-rds-extended-support")),
        xml_escape(v["DatabaseName"].as_str().unwrap_or("")),
        v["DeletionProtection"].as_bool().unwrap_or(false),
        v["StorageEncrypted"].as_bool().unwrap_or(false),
    )
}

pub(super) fn db_proxy_target_xml(v: &Value) -> String {
    format!(
        "<member><RdsResourceId>{}</RdsResourceId><Type>{}</Type><Port>{}</Port><Endpoint>{}</Endpoint><TargetHealth><State>AVAILABLE</State></TargetHealth></member>",
        xml_escape(v["RdsResourceId"].as_str().unwrap_or("")),
        xml_escape(v["Type"].as_str().unwrap_or("RDS_INSTANCE")),
        v["Port"].as_i64().unwrap_or(3306),
        xml_escape(v["Endpoint"].as_str().unwrap_or("")),
    )
}

pub(super) fn integration_xml(v: &Value) -> String {
    format!(
        "          <IntegrationName>{}</IntegrationName>\n          <IntegrationArn>{}</IntegrationArn>\n          <Status>{}</Status>",
        xml_escape(v["IntegrationName"].as_str().unwrap_or("")),
        xml_escape(v["IntegrationArn"].as_str().unwrap_or("")),
        xml_escape(v["Status"].as_str().unwrap_or("active")),
    )
}

pub(super) fn blue_green_xml(v: &Value) -> String {
    format!(
        "          <BlueGreenDeploymentIdentifier>{}</BlueGreenDeploymentIdentifier>\n          <BlueGreenDeploymentName>{}</BlueGreenDeploymentName>\n          <Status>{}</Status>",
        xml_escape(v["BlueGreenDeploymentIdentifier"].as_str().unwrap_or("")),
        xml_escape(v["BlueGreenDeploymentName"].as_str().unwrap_or("")),
        xml_escape(v["Status"].as_str().unwrap_or("AVAILABLE")),
    )
}

pub(super) fn shard_group_xml(v: &Value) -> String {
    format!(
        "          <DBShardGroupIdentifier>{}</DBShardGroupIdentifier>\n          <Status>{}</Status>",
        xml_escape(v["DBShardGroupIdentifier"].as_str().unwrap_or("")),
        xml_escape(v["Status"].as_str().unwrap_or("available")),
    )
}

pub(super) fn engine_version_xml(v: &Value) -> String {
    format!(
        "    <DBEngineVersion>\n      <Engine>{}</Engine>\n      <EngineVersion>{}</EngineVersion>\n      <Status>{}</Status>\n    </DBEngineVersion>",
        xml_escape(v["Engine"].as_str().unwrap_or("")),
        xml_escape(v["EngineVersion"].as_str().unwrap_or("")),
        xml_escape(v["Status"].as_str().unwrap_or("available")),
    )
}

pub(super) fn tenant_db_xml(v: &Value) -> String {
    format!(
        "          <TenantDBName>{}</TenantDBName>\n          <Status>{}</Status>",
        xml_escape(v["TenantDBName"].as_str().unwrap_or("")),
        xml_escape(v["Status"].as_str().unwrap_or("available")),
    )
}

pub(super) fn export_task_xml(v: &Value) -> String {
    format!(
        "          <ExportTaskIdentifier>{}</ExportTaskIdentifier>\n          <Status>{}</Status>",
        xml_escape(v["ExportTaskIdentifier"].as_str().unwrap_or("")),
        xml_escape(v["Status"].as_str().unwrap_or("STARTING")),
    )
}
