use crate::engine::deep_merge_insert;
pub type DpRouting =
std::collections::HashMap<String, Vec<serde_json::Map<String, serde_json::Value>>>;
pub fn route_nad_dp_to_lokation(
result: &mut serde_json::Map<String, serde_json::Value>,
) -> DpRouting {
let mut dp_routing: DpRouting = std::collections::HashMap::new();
let mts = match result.get_mut("marktteilnehmer") {
Some(serde_json::Value::Array(arr)) => arr,
_ => return dp_routing,
};
let mut dp_entries: Vec<(usize, serde_json::Value)> = Vec::new();
let mut idx = 0;
let mut original_idx = 0usize;
while idx < mts.len() {
let is_dp = mts[idx]
.get("marktrolle")
.and_then(|v| {
v.get("code")
.and_then(|c| c.as_str())
.or_else(|| v.as_str())
})
.map(|s| s == "DP")
.unwrap_or(false);
if is_dp {
dp_entries.push((original_idx, mts.remove(idx)));
} else {
idx += 1;
}
original_idx += 1;
}
if dp_entries.is_empty() {
return dp_routing;
}
if mts.is_empty() {
result.remove("marktteilnehmer");
}
let mut lokation_objs: Vec<(
String,
serde_json::Value,
serde_json::Map<String, serde_json::Value>,
)> = Vec::new();
for (orig_idx, dp) in dp_entries {
lokation_objs.push(build_lokation_from_dp(dp, orig_idx));
}
type Grouped = std::collections::BTreeMap<
String,
(
Vec<serde_json::Value>,
Vec<serde_json::Map<String, serde_json::Value>>,
),
>;
let mut grouped: Grouped = std::collections::BTreeMap::new();
for (k, v, meta) in lokation_objs {
let entry = grouped.entry(k).or_default();
entry.0.push(v);
entry.1.push(meta);
}
for (key, (mut values, meta)) in grouped {
let v = if values.len() == 1 {
values.remove(0)
} else {
serde_json::Value::Array(values)
};
deep_merge_insert(result, &key, v);
dp_routing.insert(key, meta);
}
dp_routing
}
fn build_lokation_from_dp(
dp: serde_json::Value,
original_idx: usize,
) -> (
String,
serde_json::Value,
serde_json::Map<String, serde_json::Value>,
) {
let dp_obj = match dp {
serde_json::Value::Object(m) => m,
other => return ("marktlokation".to_string(), other, serde_json::Map::new()),
};
let meldepunkt_id = dp_obj
.get("meldepunktId")
.and_then(|v| v.as_str())
.unwrap_or("")
.to_string();
let target = classify_dp_id(&meldepunkt_id);
let mut out = serde_json::Map::new();
out.insert(
"_type".to_string(),
serde_json::Value::String(target.bo4e_type().to_string()),
);
out.insert(
"boTyp".to_string(),
serde_json::Value::String(target.bo4e_type().to_uppercase()),
);
if !meldepunkt_id.is_empty() {
out.insert(
target.id_field().to_string(),
serde_json::Value::String(meldepunkt_id),
);
}
let mut adresse = serde_json::Map::new();
for (src, dst) in [
("strasse", "strasse"),
("strasse2", "strasse2"),
("hausnummer", "hausnummer"),
("strasse4", "strasse4"),
("postleitzahl", "postleitzahl"),
("ort", "ort"),
("land", "land"),
("zusatzinfo", "adresszusatz"),
] {
if let Some(v) = dp_obj.get(src) {
if !v.is_null() {
adresse.insert(dst.to_string(), v.clone());
}
}
}
if !adresse.is_empty() {
out.insert("adresse".to_string(), serde_json::Value::Object(adresse));
}
let mut dp_metadata = serde_json::Map::new();
dp_metadata.insert(
"originalIdx".to_string(),
serde_json::Value::Number(serde_json::Number::from(original_idx)),
);
if let Some(v) = dp_obj.get("marktrolle") {
dp_metadata.insert("marktrolle".to_string(), v.clone());
}
if let Some(v) = dp_obj.get("ortQualifier") {
dp_metadata.insert("ortQualifier".to_string(), v.clone());
}
if let Some(v) = dp_obj.get("versionStruktur") {
dp_metadata.insert("versionStruktur".to_string(), v.clone());
}
for (k, v) in dp_obj.iter() {
if matches!(
k.as_str(),
"boTyp"
| "marktrolle"
| "ortQualifier"
| "versionStruktur"
| "meldepunktId"
| "strasse"
| "strasse2"
| "hausnummer"
| "strasse4"
| "postleitzahl"
| "ort"
| "land"
| "zusatzinfo"
) {
continue;
}
dp_metadata.insert(k.clone(), v.clone());
}
(
target.entity_key().to_string(),
serde_json::Value::Object(out),
dp_metadata,
)
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum DpTarget {
Marktlokation,
Messlokation,
}
impl DpTarget {
fn entity_key(self) -> &'static str {
match self {
DpTarget::Marktlokation => "marktlokation",
DpTarget::Messlokation => "messlokation",
}
}
fn id_field(self) -> &'static str {
match self {
DpTarget::Marktlokation => "marktlokationsId",
DpTarget::Messlokation => "messlokationsId",
}
}
fn bo4e_type(self) -> &'static str {
match self {
DpTarget::Marktlokation => "Marktlokation",
DpTarget::Messlokation => "Messlokation",
}
}
}
fn classify_dp_id(id: &str) -> DpTarget {
if id.len() == 11 && id.chars().all(|c| c.is_ascii_digit()) {
DpTarget::Marktlokation
} else if id.len() == 33 && id.starts_with("DE") {
DpTarget::Messlokation
} else {
DpTarget::Marktlokation
}
}
pub fn unroute_lokation_to_nad_dp(
entities: &mut serde_json::Map<String, serde_json::Value>,
dp_routing: &DpRouting,
) {
if dp_routing.is_empty() {
return;
}
for (key, meta_list) in dp_routing {
if meta_list.is_empty() {
continue;
}
let lokation_value = match entities.remove(key) {
Some(v) => v,
None => continue,
};
let lokation_objs: Vec<serde_json::Value> = match lokation_value {
serde_json::Value::Array(arr) => arr,
other => vec![other],
};
if lokation_objs.len() != meta_list.len() {
let restored = if lokation_objs.len() == 1 {
lokation_objs.into_iter().next().unwrap()
} else {
serde_json::Value::Array(lokation_objs)
};
entities.insert(key.clone(), restored);
continue;
}
let mut indexed: Vec<(usize, serde_json::Value)> = lokation_objs
.into_iter()
.zip(meta_list.iter())
.map(|(lokation, meta)| {
let idx = meta
.get("originalIdx")
.and_then(|n| n.as_u64())
.map(|n| n as usize)
.unwrap_or(usize::MAX);
(idx, rebuild_dp_marktteilnehmer(lokation, meta))
})
.collect();
indexed.sort_by_key(|(idx, _)| *idx);
let mts = entities
.entry("marktteilnehmer".to_string())
.or_insert_with(|| serde_json::Value::Array(Vec::new()));
if !mts.is_array() {
let single = std::mem::replace(mts, serde_json::Value::Array(Vec::new()));
if let serde_json::Value::Array(a) = mts {
a.push(single);
}
}
if let serde_json::Value::Array(arr) = mts {
for (idx, dp) in indexed {
let pos = idx.min(arr.len());
arr.insert(pos, dp);
}
}
}
}
fn rebuild_dp_marktteilnehmer(
lokation: serde_json::Value,
metadata: &serde_json::Map<String, serde_json::Value>,
) -> serde_json::Value {
let mut obj = match lokation {
serde_json::Value::Object(m) => m,
other => return other,
};
let mut out = serde_json::Map::new();
out.insert(
"boTyp".to_string(),
serde_json::Value::String("MARKTTEILNEHMER".to_string()),
);
if let Some(v) = obj
.remove("marktlokationsId")
.or_else(|| obj.remove("messlokationsId"))
{
out.insert("meldepunktId".to_string(), v);
}
if let Some(serde_json::Value::Object(adresse)) = obj.remove("adresse") {
for (k, v) in adresse {
let target = match k.as_str() {
"strasse" => "strasse",
"strasse2" => "strasse2",
"hausnummer" => "hausnummer",
"strasse4" => "strasse4",
"postleitzahl" => "postleitzahl",
"ort" => "ort",
"land" => "land",
"adresszusatz" => "zusatzinfo",
other => other,
};
out.insert(target.to_string(), v);
}
}
for (k, v) in metadata.iter() {
if k == "originalIdx" {
continue;
}
out.entry(k.clone()).or_insert_with(|| v.clone());
}
serde_json::Value::Object(out)
}