Skip to main content

automapper_validation/generated/fv2510/
utilmd_strom_conditions_fv2510.rs

1// <auto-generated>
2// Generated by automapper-generator generate-conditions
3// AHB: xml-migs-and-ahbs/FV2510/UTILMD_AHB_Strom_2_1_außerordentliche_20251211.xml
4// Generated: 2026-03-12T11:07:23Z
5// </auto-generated>
6
7#[allow(unused_imports)]
8use crate::eval::format_validators::*;
9use crate::eval::{ConditionEvaluator, ConditionResult, EvaluationContext};
10
11/// Generated condition evaluator for UTILMD_Strom FV2510.
12pub struct UtilmdStromConditionEvaluatorFV2510 {
13    // External condition IDs that require runtime context.
14    external_conditions: std::collections::HashSet<u32>,
15}
16
17impl Default for UtilmdStromConditionEvaluatorFV2510 {
18    fn default() -> Self {
19        let mut external_conditions = std::collections::HashSet::new();
20        external_conditions.insert(1);
21        external_conditions.insert(3);
22        external_conditions.insert(4);
23        external_conditions.insert(5);
24        external_conditions.insert(6);
25        external_conditions.insert(8);
26        external_conditions.insert(9);
27        external_conditions.insert(14);
28        external_conditions.insert(31);
29        external_conditions.insert(33);
30        external_conditions.insert(36);
31        external_conditions.insert(38);
32        external_conditions.insert(39);
33        external_conditions.insert(40);
34        external_conditions.insert(75);
35        external_conditions.insert(98);
36        external_conditions.insert(99);
37        external_conditions.insert(151);
38        external_conditions.insert(233);
39        external_conditions.insert(241);
40        external_conditions.insert(251);
41        external_conditions.insert(292);
42        external_conditions.insert(314);
43        external_conditions.insert(315);
44        external_conditions.insert(323);
45        external_conditions.insert(326);
46        external_conditions.insert(328);
47        external_conditions.insert(412);
48        external_conditions.insert(425);
49        external_conditions.insert(426);
50        external_conditions.insert(427);
51        external_conditions.insert(428);
52        external_conditions.insert(429);
53        external_conditions.insert(433);
54        external_conditions.insert(435);
55        external_conditions.insert(453);
56        external_conditions.insert(459);
57        external_conditions.insert(460);
58        external_conditions.insert(706);
59        external_conditions.insert(717);
60        Self {
61            external_conditions,
62        }
63    }
64}
65
66impl ConditionEvaluator for UtilmdStromConditionEvaluatorFV2510 {
67    fn message_type(&self) -> &str {
68        "UTILMD_Strom"
69    }
70
71    fn format_version(&self) -> &str {
72        "FV2510"
73    }
74
75    fn evaluate(&self, condition: u32, ctx: &EvaluationContext) -> ConditionResult {
76        match condition {
77            1 => self.evaluate_1(ctx),
78            2 => self.evaluate_2(ctx),
79            3 => self.evaluate_3(ctx),
80            4 => self.evaluate_4(ctx),
81            5 => self.evaluate_5(ctx),
82            6 => self.evaluate_6(ctx),
83            7 => self.evaluate_7(ctx),
84            8 => self.evaluate_8(ctx),
85            9 => self.evaluate_9(ctx),
86            10 => self.evaluate_10(ctx),
87            11 => self.evaluate_11(ctx),
88            12 => self.evaluate_12(ctx),
89            13 => self.evaluate_13(ctx),
90            14 => self.evaluate_14(ctx),
91            15 => self.evaluate_15(ctx),
92            16 => self.evaluate_16(ctx),
93            17 => self.evaluate_17(ctx),
94            18 => self.evaluate_18(ctx),
95            19 => self.evaluate_19(ctx),
96            20 => self.evaluate_20(ctx),
97            21 => self.evaluate_21(ctx),
98            22 => self.evaluate_22(ctx),
99            23 => self.evaluate_23(ctx),
100            24 => self.evaluate_24(ctx),
101            25 => self.evaluate_25(ctx),
102            27 => self.evaluate_27(ctx),
103            28 => self.evaluate_28(ctx),
104            29 => self.evaluate_29(ctx),
105            30 => self.evaluate_30(ctx),
106            31 => self.evaluate_31(ctx),
107            32 => self.evaluate_32(ctx),
108            33 => self.evaluate_33(ctx),
109            34 => self.evaluate_34(ctx),
110            35 => self.evaluate_35(ctx),
111            36 => self.evaluate_36(ctx),
112            37 => self.evaluate_37(ctx),
113            38 => self.evaluate_38(ctx),
114            39 => self.evaluate_39(ctx),
115            40 => self.evaluate_40(ctx),
116            41 => self.evaluate_41(ctx),
117            42 => self.evaluate_42(ctx),
118            43 => self.evaluate_43(ctx),
119            44 => self.evaluate_44(ctx),
120            45 => self.evaluate_45(ctx),
121            46 => self.evaluate_46(ctx),
122            47 => self.evaluate_47(ctx),
123            48 => self.evaluate_48(ctx),
124            49 => self.evaluate_49(ctx),
125            50 => self.evaluate_50(ctx),
126            51 => self.evaluate_51(ctx),
127            52 => self.evaluate_52(ctx),
128            53 => self.evaluate_53(ctx),
129            54 => self.evaluate_54(ctx),
130            55 => self.evaluate_55(ctx),
131            56 => self.evaluate_56(ctx),
132            57 => self.evaluate_57(ctx),
133            58 => self.evaluate_58(ctx),
134            60 => self.evaluate_60(ctx),
135            61 => self.evaluate_61(ctx),
136            62 => self.evaluate_62(ctx),
137            63 => self.evaluate_63(ctx),
138            64 => self.evaluate_64(ctx),
139            65 => self.evaluate_65(ctx),
140            66 => self.evaluate_66(ctx),
141            67 => self.evaluate_67(ctx),
142            68 => self.evaluate_68(ctx),
143            69 => self.evaluate_69(ctx),
144            70 => self.evaluate_70(ctx),
145            71 => self.evaluate_71(ctx),
146            72 => self.evaluate_72(ctx),
147            73 => self.evaluate_73(ctx),
148            74 => self.evaluate_74(ctx),
149            75 => self.evaluate_75(ctx),
150            76 => self.evaluate_76(ctx),
151            77 => self.evaluate_77(ctx),
152            78 => self.evaluate_78(ctx),
153            79 => self.evaluate_79(ctx),
154            83 => self.evaluate_83(ctx),
155            84 => self.evaluate_84(ctx),
156            85 => self.evaluate_85(ctx),
157            86 => self.evaluate_86(ctx),
158            87 => self.evaluate_87(ctx),
159            88 => self.evaluate_88(ctx),
160            89 => self.evaluate_89(ctx),
161            90 => self.evaluate_90(ctx),
162            91 => self.evaluate_91(ctx),
163            92 => self.evaluate_92(ctx),
164            93 => self.evaluate_93(ctx),
165            94 => self.evaluate_94(ctx),
166            95 => self.evaluate_95(ctx),
167            96 => self.evaluate_96(ctx),
168            97 => self.evaluate_97(ctx),
169            98 => self.evaluate_98(ctx),
170            99 => self.evaluate_99(ctx),
171            100 => self.evaluate_100(ctx),
172            101 => self.evaluate_101(ctx),
173            102 => self.evaluate_102(ctx),
174            103 => self.evaluate_103(ctx),
175            104 => self.evaluate_104(ctx),
176            105 => self.evaluate_105(ctx),
177            106 => self.evaluate_106(ctx),
178            107 => self.evaluate_107(ctx),
179            108 => self.evaluate_108(ctx),
180            110 => self.evaluate_110(ctx),
181            111 => self.evaluate_111(ctx),
182            112 => self.evaluate_112(ctx),
183            113 => self.evaluate_113(ctx),
184            114 => self.evaluate_114(ctx),
185            115 => self.evaluate_115(ctx),
186            116 => self.evaluate_116(ctx),
187            117 => self.evaluate_117(ctx),
188            118 => self.evaluate_118(ctx),
189            119 => self.evaluate_119(ctx),
190            120 => self.evaluate_120(ctx),
191            121 => self.evaluate_121(ctx),
192            122 => self.evaluate_122(ctx),
193            123 => self.evaluate_123(ctx),
194            124 => self.evaluate_124(ctx),
195            125 => self.evaluate_125(ctx),
196            126 => self.evaluate_126(ctx),
197            127 => self.evaluate_127(ctx),
198            128 => self.evaluate_128(ctx),
199            129 => self.evaluate_129(ctx),
200            130 => self.evaluate_130(ctx),
201            131 => self.evaluate_131(ctx),
202            132 => self.evaluate_132(ctx),
203            133 => self.evaluate_133(ctx),
204            134 => self.evaluate_134(ctx),
205            135 => self.evaluate_135(ctx),
206            136 => self.evaluate_136(ctx),
207            137 => self.evaluate_137(ctx),
208            138 => self.evaluate_138(ctx),
209            139 => self.evaluate_139(ctx),
210            140 => self.evaluate_140(ctx),
211            141 => self.evaluate_141(ctx),
212            142 => self.evaluate_142(ctx),
213            143 => self.evaluate_143(ctx),
214            144 => self.evaluate_144(ctx),
215            145 => self.evaluate_145(ctx),
216            146 => self.evaluate_146(ctx),
217            147 => self.evaluate_147(ctx),
218            148 => self.evaluate_148(ctx),
219            149 => self.evaluate_149(ctx),
220            150 => self.evaluate_150(ctx),
221            151 => self.evaluate_151(ctx),
222            152 => self.evaluate_152(ctx),
223            153 => self.evaluate_153(ctx),
224            156 => self.evaluate_156(ctx),
225            157 => self.evaluate_157(ctx),
226            161 => self.evaluate_161(ctx),
227            162 => self.evaluate_162(ctx),
228            163 => self.evaluate_163(ctx),
229            164 => self.evaluate_164(ctx),
230            165 => self.evaluate_165(ctx),
231            166 => self.evaluate_166(ctx),
232            167 => self.evaluate_167(ctx),
233            168 => self.evaluate_168(ctx),
234            169 => self.evaluate_169(ctx),
235            170 => self.evaluate_170(ctx),
236            172 => self.evaluate_172(ctx),
237            173 => self.evaluate_173(ctx),
238            174 => self.evaluate_174(ctx),
239            175 => self.evaluate_175(ctx),
240            176 => self.evaluate_176(ctx),
241            177 => self.evaluate_177(ctx),
242            178 => self.evaluate_178(ctx),
243            179 => self.evaluate_179(ctx),
244            180 => self.evaluate_180(ctx),
245            184 => self.evaluate_184(ctx),
246            190 => self.evaluate_190(ctx),
247            191 => self.evaluate_191(ctx),
248            192 => self.evaluate_192(ctx),
249            193 => self.evaluate_193(ctx),
250            194 => self.evaluate_194(ctx),
251            195 => self.evaluate_195(ctx),
252            196 => self.evaluate_196(ctx),
253            197 => self.evaluate_197(ctx),
254            198 => self.evaluate_198(ctx),
255            199 => self.evaluate_199(ctx),
256            201 => self.evaluate_201(ctx),
257            202 => self.evaluate_202(ctx),
258            203 => self.evaluate_203(ctx),
259            204 => self.evaluate_204(ctx),
260            205 => self.evaluate_205(ctx),
261            206 => self.evaluate_206(ctx),
262            209 => self.evaluate_209(ctx),
263            210 => self.evaluate_210(ctx),
264            212 => self.evaluate_212(ctx),
265            213 => self.evaluate_213(ctx),
266            215 => self.evaluate_215(ctx),
267            216 => self.evaluate_216(ctx),
268            217 => self.evaluate_217(ctx),
269            219 => self.evaluate_219(ctx),
270            220 => self.evaluate_220(ctx),
271            221 => self.evaluate_221(ctx),
272            223 => self.evaluate_223(ctx),
273            224 => self.evaluate_224(ctx),
274            227 => self.evaluate_227(ctx),
275            229 => self.evaluate_229(ctx),
276            232 => self.evaluate_232(ctx),
277            233 => self.evaluate_233(ctx),
278            234 => self.evaluate_234(ctx),
279            237 => self.evaluate_237(ctx),
280            238 => self.evaluate_238(ctx),
281            239 => self.evaluate_239(ctx),
282            240 => self.evaluate_240(ctx),
283            241 => self.evaluate_241(ctx),
284            242 => self.evaluate_242(ctx),
285            243 => self.evaluate_243(ctx),
286            244 => self.evaluate_244(ctx),
287            248 => self.evaluate_248(ctx),
288            249 => self.evaluate_249(ctx),
289            251 => self.evaluate_251(ctx),
290            252 => self.evaluate_252(ctx),
291            253 => self.evaluate_253(ctx),
292            254 => self.evaluate_254(ctx),
293            255 => self.evaluate_255(ctx),
294            256 => self.evaluate_256(ctx),
295            257 => self.evaluate_257(ctx),
296            258 => self.evaluate_258(ctx),
297            259 => self.evaluate_259(ctx),
298            261 => self.evaluate_261(ctx),
299            262 => self.evaluate_262(ctx),
300            265 => self.evaluate_265(ctx),
301            266 => self.evaluate_266(ctx),
302            267 => self.evaluate_267(ctx),
303            268 => self.evaluate_268(ctx),
304            269 => self.evaluate_269(ctx),
305            270 => self.evaluate_270(ctx),
306            273 => self.evaluate_273(ctx),
307            279 => self.evaluate_279(ctx),
308            280 => self.evaluate_280(ctx),
309            282 => self.evaluate_282(ctx),
310            284 => self.evaluate_284(ctx),
311            285 => self.evaluate_285(ctx),
312            286 => self.evaluate_286(ctx),
313            287 => self.evaluate_287(ctx),
314            288 => self.evaluate_288(ctx),
315            291 => self.evaluate_291(ctx),
316            292 => self.evaluate_292(ctx),
317            293 => self.evaluate_293(ctx),
318            294 => self.evaluate_294(ctx),
319            295 => self.evaluate_295(ctx),
320            296 => self.evaluate_296(ctx),
321            297 => self.evaluate_297(ctx),
322            298 => self.evaluate_298(ctx),
323            300 => self.evaluate_300(ctx),
324            301 => self.evaluate_301(ctx),
325            302 => self.evaluate_302(ctx),
326            303 => self.evaluate_303(ctx),
327            304 => self.evaluate_304(ctx),
328            305 => self.evaluate_305(ctx),
329            306 => self.evaluate_306(ctx),
330            307 => self.evaluate_307(ctx),
331            309 => self.evaluate_309(ctx),
332            312 => self.evaluate_312(ctx),
333            313 => self.evaluate_313(ctx),
334            314 => self.evaluate_314(ctx),
335            315 => self.evaluate_315(ctx),
336            316 => self.evaluate_316(ctx),
337            317 => self.evaluate_317(ctx),
338            318 => self.evaluate_318(ctx),
339            321 => self.evaluate_321(ctx),
340            322 => self.evaluate_322(ctx),
341            323 => self.evaluate_323(ctx),
342            326 => self.evaluate_326(ctx),
343            327 => self.evaluate_327(ctx),
344            328 => self.evaluate_328(ctx),
345            329 => self.evaluate_329(ctx),
346            332 => self.evaluate_332(ctx),
347            333 => self.evaluate_333(ctx),
348            334 => self.evaluate_334(ctx),
349            335 => self.evaluate_335(ctx),
350            336 => self.evaluate_336(ctx),
351            337 => self.evaluate_337(ctx),
352            338 => self.evaluate_338(ctx),
353            339 => self.evaluate_339(ctx),
354            340 => self.evaluate_340(ctx),
355            341 => self.evaluate_341(ctx),
356            342 => self.evaluate_342(ctx),
357            344 => self.evaluate_344(ctx),
358            345 => self.evaluate_345(ctx),
359            346 => self.evaluate_346(ctx),
360            347 => self.evaluate_347(ctx),
361            348 => self.evaluate_348(ctx),
362            349 => self.evaluate_349(ctx),
363            350 => self.evaluate_350(ctx),
364            351 => self.evaluate_351(ctx),
365            352 => self.evaluate_352(ctx),
366            355 => self.evaluate_355(ctx),
367            356 => self.evaluate_356(ctx),
368            357 => self.evaluate_357(ctx),
369            358 => self.evaluate_358(ctx),
370            359 => self.evaluate_359(ctx),
371            360 => self.evaluate_360(ctx),
372            363 => self.evaluate_363(ctx),
373            365 => self.evaluate_365(ctx),
374            366 => self.evaluate_366(ctx),
375            367 => self.evaluate_367(ctx),
376            368 => self.evaluate_368(ctx),
377            370 => self.evaluate_370(ctx),
378            371 => self.evaluate_371(ctx),
379            372 => self.evaluate_372(ctx),
380            373 => self.evaluate_373(ctx),
381            375 => self.evaluate_375(ctx),
382            376 => self.evaluate_376(ctx),
383            377 => self.evaluate_377(ctx),
384            378 => self.evaluate_378(ctx),
385            379 => self.evaluate_379(ctx),
386            380 => self.evaluate_380(ctx),
387            384 => self.evaluate_384(ctx),
388            386 => self.evaluate_386(ctx),
389            387 => self.evaluate_387(ctx),
390            388 => self.evaluate_388(ctx),
391            391 => self.evaluate_391(ctx),
392            392 => self.evaluate_392(ctx),
393            393 => self.evaluate_393(ctx),
394            394 => self.evaluate_394(ctx),
395            395 => self.evaluate_395(ctx),
396            396 => self.evaluate_396(ctx),
397            397 => self.evaluate_397(ctx),
398            398 => self.evaluate_398(ctx),
399            399 => self.evaluate_399(ctx),
400            401 => self.evaluate_401(ctx),
401            402 => self.evaluate_402(ctx),
402            403 => self.evaluate_403(ctx),
403            404 => self.evaluate_404(ctx),
404            405 => self.evaluate_405(ctx),
405            406 => self.evaluate_406(ctx),
406            407 => self.evaluate_407(ctx),
407            408 => self.evaluate_408(ctx),
408            409 => self.evaluate_409(ctx),
409            410 => self.evaluate_410(ctx),
410            411 => self.evaluate_411(ctx),
411            412 => self.evaluate_412(ctx),
412            413 => self.evaluate_413(ctx),
413            414 => self.evaluate_414(ctx),
414            415 => self.evaluate_415(ctx),
415            416 => self.evaluate_416(ctx),
416            417 => self.evaluate_417(ctx),
417            419 => self.evaluate_419(ctx),
418            420 => self.evaluate_420(ctx),
419            421 => self.evaluate_421(ctx),
420            425 => self.evaluate_425(ctx),
421            426 => self.evaluate_426(ctx),
422            427 => self.evaluate_427(ctx),
423            428 => self.evaluate_428(ctx),
424            429 => self.evaluate_429(ctx),
425            430 => self.evaluate_430(ctx),
426            431 => self.evaluate_431(ctx),
427            432 => self.evaluate_432(ctx),
428            433 => self.evaluate_433(ctx),
429            435 => self.evaluate_435(ctx),
430            436 => self.evaluate_436(ctx),
431            437 => self.evaluate_437(ctx),
432            438 => self.evaluate_438(ctx),
433            440 => self.evaluate_440(ctx),
434            441 => self.evaluate_441(ctx),
435            442 => self.evaluate_442(ctx),
436            444 => self.evaluate_444(ctx),
437            445 => self.evaluate_445(ctx),
438            446 => self.evaluate_446(ctx),
439            447 => self.evaluate_447(ctx),
440            448 => self.evaluate_448(ctx),
441            449 => self.evaluate_449(ctx),
442            450 => self.evaluate_450(ctx),
443            451 => self.evaluate_451(ctx),
444            452 => self.evaluate_452(ctx),
445            453 => self.evaluate_453(ctx),
446            454 => self.evaluate_454(ctx),
447            455 => self.evaluate_455(ctx),
448            456 => self.evaluate_456(ctx),
449            457 => self.evaluate_457(ctx),
450            458 => self.evaluate_458(ctx),
451            459 => self.evaluate_459(ctx),
452            460 => self.evaluate_460(ctx),
453            461 => self.evaluate_461(ctx),
454            462 => self.evaluate_462(ctx),
455            463 => self.evaluate_463(ctx),
456            465 => self.evaluate_465(ctx),
457            466 => self.evaluate_466(ctx),
458            467 => self.evaluate_467(ctx),
459            468 => self.evaluate_468(ctx),
460            469 => self.evaluate_469(ctx),
461            470 => self.evaluate_470(ctx),
462            471 => self.evaluate_471(ctx),
463            472 => self.evaluate_472(ctx),
464            473 => self.evaluate_473(ctx),
465            474 => self.evaluate_474(ctx),
466            475 => self.evaluate_475(ctx),
467            476 => self.evaluate_476(ctx),
468            477 => self.evaluate_477(ctx),
469            478 => self.evaluate_478(ctx),
470            479 => self.evaluate_479(ctx),
471            480 => self.evaluate_480(ctx),
472            481 => self.evaluate_481(ctx),
473            483 => self.evaluate_483(ctx),
474            484 => self.evaluate_484(ctx),
475            487 => self.evaluate_487(ctx),
476            489 => self.evaluate_489(ctx),
477            490 => self.evaluate_490(ctx),
478            491 => self.evaluate_491(ctx),
479            494 => self.evaluate_494(ctx),
480            500 => self.evaluate_500(ctx),
481            501 => self.evaluate_501(ctx),
482            502 => self.evaluate_502(ctx),
483            503 => self.evaluate_503(ctx),
484            504 => self.evaluate_504(ctx),
485            505 => self.evaluate_505(ctx),
486            506 => self.evaluate_506(ctx),
487            507 => self.evaluate_507(ctx),
488            508 => self.evaluate_508(ctx),
489            509 => self.evaluate_509(ctx),
490            510 => self.evaluate_510(ctx),
491            511 => self.evaluate_511(ctx),
492            512 => self.evaluate_512(ctx),
493            513 => self.evaluate_513(ctx),
494            514 => self.evaluate_514(ctx),
495            515 => self.evaluate_515(ctx),
496            516 => self.evaluate_516(ctx),
497            517 => self.evaluate_517(ctx),
498            518 => self.evaluate_518(ctx),
499            519 => self.evaluate_519(ctx),
500            520 => self.evaluate_520(ctx),
501            521 => self.evaluate_521(ctx),
502            522 => self.evaluate_522(ctx),
503            523 => self.evaluate_523(ctx),
504            524 => self.evaluate_524(ctx),
505            525 => self.evaluate_525(ctx),
506            526 => self.evaluate_526(ctx),
507            527 => self.evaluate_527(ctx),
508            528 => self.evaluate_528(ctx),
509            529 => self.evaluate_529(ctx),
510            530 => self.evaluate_530(ctx),
511            531 => self.evaluate_531(ctx),
512            532 => self.evaluate_532(ctx),
513            533 => self.evaluate_533(ctx),
514            534 => self.evaluate_534(ctx),
515            555 => self.evaluate_555(ctx),
516            556 => self.evaluate_556(ctx),
517            557 => self.evaluate_557(ctx),
518            558 => self.evaluate_558(ctx),
519            559 => self.evaluate_559(ctx),
520            560 => self.evaluate_560(ctx),
521            561 => self.evaluate_561(ctx),
522            562 => self.evaluate_562(ctx),
523            563 => self.evaluate_563(ctx),
524            564 => self.evaluate_564(ctx),
525            565 => self.evaluate_565(ctx),
526            566 => self.evaluate_566(ctx),
527            567 => self.evaluate_567(ctx),
528            568 => self.evaluate_568(ctx),
529            569 => self.evaluate_569(ctx),
530            570 => self.evaluate_570(ctx),
531            572 => self.evaluate_572(ctx),
532            576 => self.evaluate_576(ctx),
533            577 => self.evaluate_577(ctx),
534            578 => self.evaluate_578(ctx),
535            579 => self.evaluate_579(ctx),
536            580 => self.evaluate_580(ctx),
537            581 => self.evaluate_581(ctx),
538            586 => self.evaluate_586(ctx),
539            590 => self.evaluate_590(ctx),
540            594 => self.evaluate_594(ctx),
541            599 => self.evaluate_599(ctx),
542            601 => self.evaluate_601(ctx),
543            606 => self.evaluate_606(ctx),
544            609 => self.evaluate_609(ctx),
545            611 => self.evaluate_611(ctx),
546            614 => self.evaluate_614(ctx),
547            617 => self.evaluate_617(ctx),
548            618 => self.evaluate_618(ctx),
549            619 => self.evaluate_619(ctx),
550            621 => self.evaluate_621(ctx),
551            622 => self.evaluate_622(ctx),
552            623 => self.evaluate_623(ctx),
553            630 => self.evaluate_630(ctx),
554            631 => self.evaluate_631(ctx),
555            632 => self.evaluate_632(ctx),
556            637 => self.evaluate_637(ctx),
557            638 => self.evaluate_638(ctx),
558            639 => self.evaluate_639(ctx),
559            640 => self.evaluate_640(ctx),
560            641 => self.evaluate_641(ctx),
561            642 => self.evaluate_642(ctx),
562            643 => self.evaluate_643(ctx),
563            645 => self.evaluate_645(ctx),
564            646 => self.evaluate_646(ctx),
565            647 => self.evaluate_647(ctx),
566            648 => self.evaluate_648(ctx),
567            651 => self.evaluate_651(ctx),
568            653 => self.evaluate_653(ctx),
569            655 => self.evaluate_655(ctx),
570            659 => self.evaluate_659(ctx),
571            660 => self.evaluate_660(ctx),
572            662 => self.evaluate_662(ctx),
573            663 => self.evaluate_663(ctx),
574            664 => self.evaluate_664(ctx),
575            665 => self.evaluate_665(ctx),
576            667 => self.evaluate_667(ctx),
577            668 => self.evaluate_668(ctx),
578            670 => self.evaluate_670(ctx),
579            671 => self.evaluate_671(ctx),
580            672 => self.evaluate_672(ctx),
581            673 => self.evaluate_673(ctx),
582            674 => self.evaluate_674(ctx),
583            675 => self.evaluate_675(ctx),
584            677 => self.evaluate_677(ctx),
585            678 => self.evaluate_678(ctx),
586            679 => self.evaluate_679(ctx),
587            680 => self.evaluate_680(ctx),
588            681 => self.evaluate_681(ctx),
589            682 => self.evaluate_682(ctx),
590            683 => self.evaluate_683(ctx),
591            684 => self.evaluate_684(ctx),
592            685 => self.evaluate_685(ctx),
593            686 => self.evaluate_686(ctx),
594            687 => self.evaluate_687(ctx),
595            688 => self.evaluate_688(ctx),
596            689 => self.evaluate_689(ctx),
597            690 => self.evaluate_690(ctx),
598            693 => self.evaluate_693(ctx),
599            694 => self.evaluate_694(ctx),
600            695 => self.evaluate_695(ctx),
601            696 => self.evaluate_696(ctx),
602            698 => self.evaluate_698(ctx),
603            699 => self.evaluate_699(ctx),
604            700 => self.evaluate_700(ctx),
605            704 => self.evaluate_704(ctx),
606            705 => self.evaluate_705(ctx),
607            706 => self.evaluate_706(ctx),
608            707 => self.evaluate_707(ctx),
609            708 => self.evaluate_708(ctx),
610            709 => self.evaluate_709(ctx),
611            710 => self.evaluate_710(ctx),
612            711 => self.evaluate_711(ctx),
613            712 => self.evaluate_712(ctx),
614            713 => self.evaluate_713(ctx),
615            714 => self.evaluate_714(ctx),
616            715 => self.evaluate_715(ctx),
617            716 => self.evaluate_716(ctx),
618            717 => self.evaluate_717(ctx),
619            718 => self.evaluate_718(ctx),
620            719 => self.evaluate_719(ctx),
621            902 => self.evaluate_902(ctx),
622            910 => self.evaluate_910(ctx),
623            914 => self.evaluate_914(ctx),
624            922 => self.evaluate_922(ctx),
625            926 => self.evaluate_926(ctx),
626            930 => self.evaluate_930(ctx),
627            931 => self.evaluate_931(ctx),
628            932 => self.evaluate_932(ctx),
629            933 => self.evaluate_933(ctx),
630            937 => self.evaluate_937(ctx),
631            938 => self.evaluate_938(ctx),
632            939 => self.evaluate_939(ctx),
633            940 => self.evaluate_940(ctx),
634            942 => self.evaluate_942(ctx),
635            943 => self.evaluate_943(ctx),
636            946 => self.evaluate_946(ctx),
637            948 => self.evaluate_948(ctx),
638            950 => self.evaluate_950(ctx),
639            951 => self.evaluate_951(ctx),
640            952 => self.evaluate_952(ctx),
641            955 => self.evaluate_955(ctx),
642            957 => self.evaluate_957(ctx),
643            960 => self.evaluate_960(ctx),
644            961 => self.evaluate_961(ctx),
645            967 => self.evaluate_967(ctx),
646            2000 => self.evaluate_2000(ctx),
647            2001 => self.evaluate_2001(ctx),
648            2002 => self.evaluate_2002(ctx),
649            2003 => self.evaluate_2003(ctx),
650            2004 => self.evaluate_2004(ctx),
651            2005 => self.evaluate_2005(ctx),
652            2006 => self.evaluate_2006(ctx),
653            2007 => self.evaluate_2007(ctx),
654            2008 => self.evaluate_2008(ctx),
655            2009 => self.evaluate_2009(ctx),
656            2010 => self.evaluate_2010(ctx),
657            2011 => self.evaluate_2011(ctx),
658            2012 => self.evaluate_2012(ctx),
659            2013 => self.evaluate_2013(ctx),
660            2014 => self.evaluate_2014(ctx),
661            2015 => self.evaluate_2015(ctx),
662            2016 => self.evaluate_2016(ctx),
663            2017 => self.evaluate_2017(ctx),
664            2018 => self.evaluate_2018(ctx),
665            2060 => self.evaluate_2060(ctx),
666            2061 => self.evaluate_2061(ctx),
667            2071 => self.evaluate_2071(ctx),
668            2073 => self.evaluate_2073(ctx),
669            2075 => self.evaluate_2075(ctx),
670            2080 => self.evaluate_2080(ctx),
671            2095 => self.evaluate_2095(ctx),
672            2096 => self.evaluate_2096(ctx),
673            2119 => self.evaluate_2119(ctx),
674            2121 => self.evaluate_2121(ctx),
675            2140 => self.evaluate_2140(ctx),
676            2182 => self.evaluate_2182(ctx),
677            2183 => self.evaluate_2183(ctx),
678            2207 => self.evaluate_2207(ctx),
679            2225 => self.evaluate_2225(ctx),
680            2236 => self.evaluate_2236(ctx),
681            2252 => self.evaluate_2252(ctx),
682            2261 => self.evaluate_2261(ctx),
683            2284 => self.evaluate_2284(ctx),
684            2286 => self.evaluate_2286(ctx),
685            2287 => self.evaluate_2287(ctx),
686            2288 => self.evaluate_2288(ctx),
687            2307 => self.evaluate_2307(ctx),
688            2308 => self.evaluate_2308(ctx),
689            2309 => self.evaluate_2309(ctx),
690            2310 => self.evaluate_2310(ctx),
691            2311 => self.evaluate_2311(ctx),
692            2312 => self.evaluate_2312(ctx),
693            2313 => self.evaluate_2313(ctx),
694            2317 => self.evaluate_2317(ctx),
695            2318 => self.evaluate_2318(ctx),
696            2344 => self.evaluate_2344(ctx),
697            2350 => self.evaluate_2350(ctx),
698            2351 => self.evaluate_2351(ctx),
699            2352 => self.evaluate_2352(ctx),
700            2356 => self.evaluate_2356(ctx),
701            2357 => self.evaluate_2357(ctx),
702            2358 => self.evaluate_2358(ctx),
703            2359 => self.evaluate_2359(ctx),
704            2360 => self.evaluate_2360(ctx),
705            2361 => self.evaluate_2361(ctx),
706            _ => ConditionResult::Unknown,
707        }
708    }
709
710    fn is_external(&self, condition: u32) -> bool {
711        self.external_conditions.contains(&condition)
712    }
713    fn is_known(&self, condition: u32) -> bool {
714        matches!(
715            condition,
716            1 | 2
717                | 3
718                | 4
719                | 5
720                | 6
721                | 7
722                | 8
723                | 9
724                | 10
725                | 11
726                | 12
727                | 13
728                | 14
729                | 15
730                | 16
731                | 17
732                | 18
733                | 19
734                | 20
735                | 21
736                | 22
737                | 23
738                | 24
739                | 25
740                | 27
741                | 28
742                | 29
743                | 30
744                | 31
745                | 32
746                | 33
747                | 34
748                | 35
749                | 36
750                | 37
751                | 38
752                | 39
753                | 40
754                | 41
755                | 42
756                | 43
757                | 44
758                | 45
759                | 46
760                | 47
761                | 48
762                | 49
763                | 50
764                | 51
765                | 52
766                | 53
767                | 54
768                | 55
769                | 56
770                | 57
771                | 58
772                | 60
773                | 61
774                | 62
775                | 63
776                | 64
777                | 65
778                | 66
779                | 67
780                | 68
781                | 69
782                | 70
783                | 71
784                | 72
785                | 73
786                | 74
787                | 75
788                | 76
789                | 77
790                | 78
791                | 79
792                | 83
793                | 84
794                | 85
795                | 86
796                | 87
797                | 88
798                | 89
799                | 90
800                | 91
801                | 92
802                | 93
803                | 94
804                | 95
805                | 96
806                | 97
807                | 98
808                | 99
809                | 100
810                | 101
811                | 102
812                | 103
813                | 104
814                | 105
815                | 106
816                | 107
817                | 108
818                | 110
819                | 111
820                | 112
821                | 113
822                | 114
823                | 115
824                | 116
825                | 117
826                | 118
827                | 119
828                | 120
829                | 121
830                | 122
831                | 123
832                | 124
833                | 125
834                | 126
835                | 127
836                | 128
837                | 129
838                | 130
839                | 131
840                | 132
841                | 133
842                | 134
843                | 135
844                | 136
845                | 137
846                | 138
847                | 139
848                | 140
849                | 141
850                | 142
851                | 143
852                | 144
853                | 145
854                | 146
855                | 147
856                | 148
857                | 149
858                | 150
859                | 151
860                | 152
861                | 153
862                | 156
863                | 157
864                | 161
865                | 162
866                | 163
867                | 164
868                | 165
869                | 166
870                | 167
871                | 168
872                | 169
873                | 170
874                | 172
875                | 173
876                | 174
877                | 175
878                | 176
879                | 177
880                | 178
881                | 179
882                | 180
883                | 184
884                | 190
885                | 191
886                | 192
887                | 193
888                | 194
889                | 195
890                | 196
891                | 197
892                | 198
893                | 199
894                | 201
895                | 202
896                | 203
897                | 204
898                | 205
899                | 206
900                | 209
901                | 210
902                | 212
903                | 213
904                | 215
905                | 216
906                | 217
907                | 219
908                | 220
909                | 221
910                | 223
911                | 224
912                | 227
913                | 229
914                | 232
915                | 233
916                | 234
917                | 237
918                | 238
919                | 239
920                | 240
921                | 241
922                | 242
923                | 243
924                | 244
925                | 248
926                | 249
927                | 251
928                | 252
929                | 253
930                | 254
931                | 255
932                | 256
933                | 257
934                | 258
935                | 259
936                | 261
937                | 262
938                | 265
939                | 266
940                | 267
941                | 268
942                | 269
943                | 270
944                | 273
945                | 279
946                | 280
947                | 282
948                | 284
949                | 285
950                | 286
951                | 287
952                | 288
953                | 291
954                | 292
955                | 293
956                | 294
957                | 295
958                | 296
959                | 297
960                | 298
961                | 300
962                | 301
963                | 302
964                | 303
965                | 304
966                | 305
967                | 306
968                | 307
969                | 309
970                | 312
971                | 313
972                | 314
973                | 315
974                | 316
975                | 317
976                | 318
977                | 321
978                | 322
979                | 323
980                | 326
981                | 327
982                | 328
983                | 329
984                | 332
985                | 333
986                | 334
987                | 335
988                | 336
989                | 337
990                | 338
991                | 339
992                | 340
993                | 341
994                | 342
995                | 344
996                | 345
997                | 346
998                | 347
999                | 348
1000                | 349
1001                | 350
1002                | 351
1003                | 352
1004                | 355
1005                | 356
1006                | 357
1007                | 358
1008                | 359
1009                | 360
1010                | 363
1011                | 365
1012                | 366
1013                | 367
1014                | 368
1015                | 370
1016                | 371
1017                | 372
1018                | 373
1019                | 375
1020                | 376
1021                | 377
1022                | 378
1023                | 379
1024                | 380
1025                | 384
1026                | 386
1027                | 387
1028                | 388
1029                | 391
1030                | 392
1031                | 393
1032                | 394
1033                | 395
1034                | 396
1035                | 397
1036                | 398
1037                | 399
1038                | 401
1039                | 402
1040                | 403
1041                | 404
1042                | 405
1043                | 406
1044                | 407
1045                | 408
1046                | 409
1047                | 410
1048                | 411
1049                | 412
1050                | 413
1051                | 414
1052                | 415
1053                | 416
1054                | 417
1055                | 419
1056                | 420
1057                | 421
1058                | 425
1059                | 426
1060                | 427
1061                | 428
1062                | 429
1063                | 430
1064                | 431
1065                | 432
1066                | 433
1067                | 435
1068                | 436
1069                | 437
1070                | 438
1071                | 440
1072                | 441
1073                | 442
1074                | 444
1075                | 445
1076                | 446
1077                | 447
1078                | 448
1079                | 449
1080                | 450
1081                | 451
1082                | 452
1083                | 453
1084                | 454
1085                | 455
1086                | 456
1087                | 457
1088                | 458
1089                | 459
1090                | 460
1091                | 461
1092                | 462
1093                | 463
1094                | 465
1095                | 466
1096                | 467
1097                | 468
1098                | 469
1099                | 470
1100                | 471
1101                | 472
1102                | 473
1103                | 474
1104                | 475
1105                | 476
1106                | 477
1107                | 478
1108                | 479
1109                | 480
1110                | 481
1111                | 483
1112                | 484
1113                | 487
1114                | 489
1115                | 490
1116                | 491
1117                | 494
1118                | 500
1119                | 501
1120                | 502
1121                | 503
1122                | 504
1123                | 505
1124                | 506
1125                | 507
1126                | 508
1127                | 509
1128                | 510
1129                | 511
1130                | 512
1131                | 513
1132                | 514
1133                | 515
1134                | 516
1135                | 517
1136                | 518
1137                | 519
1138                | 520
1139                | 521
1140                | 522
1141                | 523
1142                | 524
1143                | 525
1144                | 526
1145                | 527
1146                | 528
1147                | 529
1148                | 530
1149                | 531
1150                | 532
1151                | 533
1152                | 534
1153                | 555
1154                | 556
1155                | 557
1156                | 558
1157                | 559
1158                | 560
1159                | 561
1160                | 562
1161                | 563
1162                | 564
1163                | 565
1164                | 566
1165                | 567
1166                | 568
1167                | 569
1168                | 570
1169                | 572
1170                | 576
1171                | 577
1172                | 578
1173                | 579
1174                | 580
1175                | 581
1176                | 586
1177                | 590
1178                | 594
1179                | 599
1180                | 601
1181                | 606
1182                | 609
1183                | 611
1184                | 614
1185                | 617
1186                | 618
1187                | 619
1188                | 621
1189                | 622
1190                | 623
1191                | 630
1192                | 631
1193                | 632
1194                | 637
1195                | 638
1196                | 639
1197                | 640
1198                | 641
1199                | 642
1200                | 643
1201                | 645
1202                | 646
1203                | 647
1204                | 648
1205                | 651
1206                | 653
1207                | 655
1208                | 659
1209                | 660
1210                | 662
1211                | 663
1212                | 664
1213                | 665
1214                | 667
1215                | 668
1216                | 670
1217                | 671
1218                | 672
1219                | 673
1220                | 674
1221                | 675
1222                | 677
1223                | 678
1224                | 679
1225                | 680
1226                | 681
1227                | 682
1228                | 683
1229                | 684
1230                | 685
1231                | 686
1232                | 687
1233                | 688
1234                | 689
1235                | 690
1236                | 693
1237                | 694
1238                | 695
1239                | 696
1240                | 698
1241                | 699
1242                | 700
1243                | 704
1244                | 705
1245                | 706
1246                | 707
1247                | 708
1248                | 709
1249                | 710
1250                | 711
1251                | 712
1252                | 713
1253                | 714
1254                | 715
1255                | 716
1256                | 717
1257                | 718
1258                | 719
1259                | 902
1260                | 910
1261                | 914
1262                | 922
1263                | 926
1264                | 930
1265                | 931
1266                | 932
1267                | 933
1268                | 937
1269                | 938
1270                | 939
1271                | 940
1272                | 942
1273                | 943
1274                | 946
1275                | 948
1276                | 950
1277                | 951
1278                | 952
1279                | 955
1280                | 957
1281                | 960
1282                | 961
1283                | 967
1284                | 2000
1285                | 2001
1286                | 2002
1287                | 2003
1288                | 2004
1289                | 2005
1290                | 2006
1291                | 2007
1292                | 2008
1293                | 2009
1294                | 2010
1295                | 2011
1296                | 2012
1297                | 2013
1298                | 2014
1299                | 2015
1300                | 2016
1301                | 2017
1302                | 2018
1303                | 2060
1304                | 2061
1305                | 2071
1306                | 2073
1307                | 2075
1308                | 2080
1309                | 2095
1310                | 2096
1311                | 2119
1312                | 2121
1313                | 2140
1314                | 2182
1315                | 2183
1316                | 2207
1317                | 2225
1318                | 2236
1319                | 2252
1320                | 2261
1321                | 2284
1322                | 2286
1323                | 2287
1324                | 2288
1325                | 2307
1326                | 2308
1327                | 2309
1328                | 2310
1329                | 2311
1330                | 2312
1331                | 2313
1332                | 2317
1333                | 2318
1334                | 2344
1335                | 2350
1336                | 2351
1337                | 2352
1338                | 2356
1339                | 2357
1340                | 2358
1341                | 2359
1342                | 2360
1343                | 2361
1344        )
1345    }
1346}
1347
1348impl UtilmdStromConditionEvaluatorFV2510 {
1349    /// [2] Wenn UNH DE0070 (Übermittlungsfolgenummer) mit 1 vorhanden
1350    fn evaluate_2(&self, ctx: &EvaluationContext) -> ConditionResult {
1351        ctx.has_segment_matching("UNH", &[(3, 0, "1")])
1352    }
1353
1354    /// [3] Bei Aufteilung, in der Nachricht mit der höchsten Übermittlungsfolgenummer
1355    /// EXTERNAL: Requires context from outside the message.
1356    fn evaluate_3(&self, ctx: &EvaluationContext) -> ConditionResult {
1357        ctx.external.evaluate("is_highest_sequence_in_split")
1358    }
1359
1360    /// [20] Wenn im SG8 SEQ+Z01 (Daten der Marktlokation) mit der gleichen ID im SG8 RFF+Z18 (Referenz auf die ID der Marktlokation) wie in diesem SG8 das SG10 CCI+Z30++Z07 (Verbrauch) vorhanden
1361    fn evaluate_20(&self, ctx: &EvaluationContext) -> ConditionResult {
1362        let nav = match ctx.navigator() {
1363            Some(n) => n,
1364            None => return ctx.has_qualified_value("CCI", 0, "Z30", 2, 0, &["Z07"]),
1365        };
1366        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
1367        for i in 0..sg8_count {
1368            let seq_segs = nav.find_segments_in_group("SEQ", &["SG4", "SG8"], i);
1369            let has_z01 = seq_segs.iter().any(|s| {
1370                s.elements
1371                    .first()
1372                    .and_then(|e| e.first())
1373                    .is_some_and(|v| v == "Z01")
1374            });
1375            if !has_z01 {
1376                continue;
1377            }
1378            let sg10_count = nav.child_group_instance_count(&["SG4", "SG8"], i, "SG10");
1379            for j in 0..sg10_count {
1380                let ccis = nav.find_segments_in_child_group("CCI", &["SG4", "SG8"], i, "SG10", j);
1381                if ccis.iter().any(|s| {
1382                    s.elements
1383                        .first()
1384                        .and_then(|e| e.first())
1385                        .is_some_and(|v| v == "Z30")
1386                        && s.elements
1387                            .get(2)
1388                            .and_then(|e| e.first())
1389                            .is_some_and(|v| v == "Z07")
1390                }) {
1391                    return ConditionResult::True;
1392                }
1393            }
1394        }
1395        ConditionResult::False
1396    }
1397
1398    /// [28] Wenn in dieser SG4 IDE+24 das STS+E01++A04: E_0017 bzw. A03: E_0052 (Zusätzlicher Datensatz / ergänzte Marktlokation) nicht vorhanden
1399    // REVIEW: Within each SG4 containing IDE+24 (Vorgang), check if STS+E01 with Prüfschritt A04:E_0017 (Zusätzlicher Datensatz) or A03:E_0052 (ergänzte Marktlokation) is absent. Uses navigator to scope the check per SG4 instance. STS elements[0][0]="E01", elements[2][0]=Prüfschritt code, elements[2][1]=codelist. Condition is True when this STS is NOT found in the SG4, indicating no 'additional record' status was set. (medium confidence)
1400    fn evaluate_28(&self, ctx: &EvaluationContext) -> ConditionResult {
1401        let nav = match ctx.navigator() {
1402            Some(n) => n,
1403            None => {
1404                let sts_segs = ctx.find_segments_with_qualifier("STS", 0, "E01");
1405                let found = sts_segs.iter().any(|s| {
1406                    let code = s
1407                        .elements
1408                        .get(2)
1409                        .and_then(|e| e.first())
1410                        .map(|v| v.as_str());
1411                    let list = s.elements.get(2).and_then(|e| e.get(1)).map(|v| v.as_str());
1412                    (code == Some("A04") && list == Some("E_0017"))
1413                        || (code == Some("A03") && list == Some("E_0052"))
1414                });
1415                return ConditionResult::from(!found);
1416            }
1417        };
1418        let sg4_count = nav.group_instance_count(&["SG4"]);
1419        for i in 0..sg4_count {
1420            let ide_segs = nav.find_segments_in_group("IDE", &["SG4"], i);
1421            let has_ide_24 = ide_segs.iter().any(|s| {
1422                s.elements
1423                    .first()
1424                    .and_then(|e| e.first())
1425                    .is_some_and(|v| v == "24")
1426            });
1427            if !has_ide_24 {
1428                continue;
1429            }
1430            let sts_segs = nav.find_segments_in_group("STS", &["SG4"], i);
1431            let found = sts_segs.iter().any(|s| {
1432                let qual = s
1433                    .elements
1434                    .first()
1435                    .and_then(|e| e.first())
1436                    .map(|v| v.as_str());
1437                let code = s
1438                    .elements
1439                    .get(2)
1440                    .and_then(|e| e.first())
1441                    .map(|v| v.as_str());
1442                let list = s.elements.get(2).and_then(|e| e.get(1)).map(|v| v.as_str());
1443                qual == Some("E01")
1444                    && ((code == Some("A04") && list == Some("E_0017"))
1445                        || (code == Some("A03") && list == Some("E_0052")))
1446            });
1447            if !found {
1448                return ConditionResult::True;
1449            }
1450        }
1451        ConditionResult::False
1452    }
1453
1454    /// [29] Wenn in dieser SG4 IDE+24 das STS+E01++A06: E_0017 bzw. A05: E_0052 (Zu viele Marktlokationen enthalten / entfallene Marktlokation) nicht vorhanden
1455    // REVIEW: Within each SG4 containing IDE+24 (Vorgang), check if STS+E01 with Prüfschritt A06:E_0017 (Zu viele Marktlokationen) or A05:E_0052 (entfallene Marktlokation) is absent. Mirrors condition [28] but for the 'too many / dropped Marktlokation' status codes. True when neither combination is found in the SG4, indicating no 'excess/dropped records' status was set. (medium confidence)
1456    fn evaluate_29(&self, ctx: &EvaluationContext) -> ConditionResult {
1457        let nav = match ctx.navigator() {
1458            Some(n) => n,
1459            None => {
1460                let sts_segs = ctx.find_segments_with_qualifier("STS", 0, "E01");
1461                let found = sts_segs.iter().any(|s| {
1462                    let code = s
1463                        .elements
1464                        .get(2)
1465                        .and_then(|e| e.first())
1466                        .map(|v| v.as_str());
1467                    let list = s.elements.get(2).and_then(|e| e.get(1)).map(|v| v.as_str());
1468                    (code == Some("A06") && list == Some("E_0017"))
1469                        || (code == Some("A05") && list == Some("E_0052"))
1470                });
1471                return ConditionResult::from(!found);
1472            }
1473        };
1474        let sg4_count = nav.group_instance_count(&["SG4"]);
1475        for i in 0..sg4_count {
1476            let ide_segs = nav.find_segments_in_group("IDE", &["SG4"], i);
1477            let has_ide_24 = ide_segs.iter().any(|s| {
1478                s.elements
1479                    .first()
1480                    .and_then(|e| e.first())
1481                    .is_some_and(|v| v == "24")
1482            });
1483            if !has_ide_24 {
1484                continue;
1485            }
1486            let sts_segs = nav.find_segments_in_group("STS", &["SG4"], i);
1487            let found = sts_segs.iter().any(|s| {
1488                let qual = s
1489                    .elements
1490                    .first()
1491                    .and_then(|e| e.first())
1492                    .map(|v| v.as_str());
1493                let code = s
1494                    .elements
1495                    .get(2)
1496                    .and_then(|e| e.first())
1497                    .map(|v| v.as_str());
1498                let list = s.elements.get(2).and_then(|e| e.get(1)).map(|v| v.as_str());
1499                qual == Some("E01")
1500                    && ((code == Some("A06") && list == Some("E_0017"))
1501                        || (code == Some("A05") && list == Some("E_0052")))
1502            });
1503            if !found {
1504                return ConditionResult::True;
1505            }
1506        }
1507        ConditionResult::False
1508    }
1509
1510    /// [44] Es ist die Zeitraum-ID vom DE1156 aus einem passenden SG6 RFF+Z49/ Z53 (Verwendungszeitraum der Daten: "Gültige Daten", "Keine Daten") aus der Anfragennachricht aus SG6 RFF+TN DE1154 ((Referenz Vo...
1511    fn evaluate_44(&self, _ctx: &EvaluationContext) -> ConditionResult {
1512        // Hinweis: Die Zeitraum-ID ist vom DE1156 aus einem passenden SG6 RFF+Z49/Z53
1513        // (Verwendungszeitraum der Daten) aus der Anfragenachricht, identifiziert über
1514        // SG6 RFF+TN DE1154 (Referenz Vorgangsnummer), einzutragen. Informelle Befüllungsregel.
1515        ConditionResult::True
1516    }
1517
1518    /// [51] Wenn die Marktlokations-ID im SG8 RFF+Z18 derselben SG8 SEQ+Z98 (Informative Daten der Marktlokation) mit derselben Zeitraum-ID im DE1050 nicht auf ein SG8 SEQ+Z78 (Referenz auf die Lokationsbünde...
1519    // REVIEW: Cross-SG condition: checks that SG8 SEQ+Z98 groups (Daten der Marktlokation) do NOT share a Zeitraum-ID with SG8 SEQ+Z78 groups (Lokationsbündelstruktur) that carry the 'Verbrauch ohne Messlokation (Pauschal)' code 9992 00000 001 8 in RFF+Z31. Medium confidence due to the multi-step cross-group correlation logic and the whitespace-normalized code comparison. (medium confidence)
1520    fn evaluate_51(&self, ctx: &EvaluationContext) -> ConditionResult {
1521        let nav = match ctx.navigator() {
1522            Some(n) => n,
1523            None => return ConditionResult::Unknown,
1524        };
1525        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
1526
1527        // Step 1: Collect Zeitraum-IDs from Z78 SG8 groups that have RFF+Z31 with the target code
1528        let target_code = "9992000000018";
1529        let mut z78_zeitraum_ids: std::collections::HashSet<String> =
1530            std::collections::HashSet::new();
1531        for i in 0..sg8_count {
1532            let seq_segs = nav.find_segments_in_group("SEQ", &["SG4", "SG8"], i);
1533            let is_z78 = seq_segs.iter().any(|s| {
1534                s.elements
1535                    .first()
1536                    .and_then(|e| e.first())
1537                    .is_some_and(|v| v == "Z78")
1538            });
1539            if !is_z78 {
1540                continue;
1541            }
1542            let rff_segs = nav.find_segments_in_group("RFF", &["SG4", "SG8"], i);
1543            let has_z31_target = rff_segs.iter().any(|s| {
1544                s.elements
1545                    .first()
1546                    .and_then(|e| e.first())
1547                    .is_some_and(|v| v == "Z31")
1548                    && s.elements
1549                        .first()
1550                        .and_then(|e| e.get(1))
1551                        .is_some_and(|v| v.replace(' ', "") == target_code)
1552            });
1553            if has_z31_target {
1554                for seq in &seq_segs {
1555                    if seq
1556                        .elements
1557                        .first()
1558                        .and_then(|e| e.first())
1559                        .is_some_and(|v| v == "Z78")
1560                    {
1561                        if let Some(zeit_id) = seq.elements.get(1).and_then(|e| e.first()) {
1562                            if !zeit_id.is_empty() {
1563                                z78_zeitraum_ids.insert(zeit_id.clone());
1564                            }
1565                        }
1566                    }
1567                }
1568            }
1569        }
1570
1571        if z78_zeitraum_ids.is_empty() {
1572            // No Z78 with the target code exists — the Z98 group cannot reference it, condition True
1573            return ConditionResult::True;
1574        }
1575
1576        // Step 2: Check if any Z98 SG8 shares a Zeitraum-ID with a target Z78 SG8
1577        for i in 0..sg8_count {
1578            let seq_segs = nav.find_segments_in_group("SEQ", &["SG4", "SG8"], i);
1579            for seq in &seq_segs {
1580                if seq
1581                    .elements
1582                    .first()
1583                    .and_then(|e| e.first())
1584                    .is_some_and(|v| v == "Z98")
1585                {
1586                    if let Some(zeit_id) = seq.elements.get(1).and_then(|e| e.first()) {
1587                        if z78_zeitraum_ids.contains(zeit_id) {
1588                            // Z98 SG8 references a Z78 with the Pauschal code — condition False
1589                            return ConditionResult::False;
1590                        }
1591                    }
1592                }
1593            }
1594        }
1595        ConditionResult::True
1596    }
1597
1598    /// [79] Wenn SG4 STS+7+++Z33 (Auszug wegen Stilllegung) vorhanden
1599    fn evaluate_79(&self, ctx: &EvaluationContext) -> ConditionResult {
1600        // STS+7+++Z33: elements[0][0]="7" (Statuskategorie), elements[3][0]="Z33" (Transaktionsgrundergänzung: Auszug wegen Stilllegung)
1601        ctx.has_segment_matching("STS", &[(0, 0, "7"), (3, 0, "Z33")])
1602    }
1603
1604    /// [134] Wenn dieses DTM+Z25 (Verwendung der Daten ab) nicht im SG6 RFF+Z48/ Z55 (Verwendungszeitraum der Daten: Erwartete Daten/ Keine Daten erwartet) mit der Zeitraum ID "1" im DE1156 ist, muss das Datum ...
1605    // REVIEW: Temporal consistency check across SG6 Verwendungszeitraum slices with RFF+Z48/Z55 (Erwartete Daten / Keine Daten erwartet). For each slice with Zeitraum-ID > 1, its DTM+Z25 (Daten ab) must equal DTM+Z26 (Daten bis) of the next-lower Zeitraum-ID slice. Reads DE1156 (elements[0][2]) for the Zeitraum-ID, sorts numerically, then validates continuity. Medium confidence due to ordering semantics and implicit 'current SG6 context' requirement. (medium confidence)
1606    fn evaluate_134(&self, ctx: &EvaluationContext) -> ConditionResult {
1607        let nav = match ctx.navigator() {
1608            Some(n) => n,
1609            None => return ConditionResult::Unknown,
1610        };
1611        let sg6_count = nav.group_instance_count(&["SG4", "SG6"]);
1612
1613        // Collect (zeitraum_id_numeric, dtm_z25_value, dtm_z26_value) for SG6 groups with RFF+Z48 or RFF+Z55
1614        let mut slices: Vec<(i64, String, String)> = Vec::new();
1615        for i in 0..sg6_count {
1616            let rff_segs = nav.find_segments_in_group("RFF", &["SG4", "SG6"], i);
1617            // DE1156 is elements[0][2] for RFF Verwendungszeitraum
1618            let zeitraum_id = rff_segs.iter().find_map(|s| {
1619                let qual = s.elements.first().and_then(|e| e.first())?;
1620                if qual == "Z48" || qual == "Z55" {
1621                    let id = s.elements.first().and_then(|e| e.get(2))?;
1622                    if !id.is_empty() {
1623                        Some(id.clone())
1624                    } else {
1625                        None
1626                    }
1627                } else {
1628                    None
1629                }
1630            });
1631            let Some(zid_str) = zeitraum_id else { continue };
1632            let Ok(zid_num) = zid_str.parse::<i64>() else {
1633                continue;
1634            };
1635            let dtm_segs = nav.find_segments_in_group("DTM", &["SG4", "SG6"], i);
1636            let z25 = dtm_segs
1637                .iter()
1638                .find_map(|s| {
1639                    if s.elements
1640                        .first()
1641                        .and_then(|e| e.first())
1642                        .is_some_and(|v| v == "Z25")
1643                    {
1644                        s.elements.first().and_then(|e| e.get(1)).cloned()
1645                    } else {
1646                        None
1647                    }
1648                })
1649                .unwrap_or_default();
1650            let z26 = dtm_segs
1651                .iter()
1652                .find_map(|s| {
1653                    if s.elements
1654                        .first()
1655                        .and_then(|e| e.first())
1656                        .is_some_and(|v| v == "Z26")
1657                    {
1658                        s.elements.first().and_then(|e| e.get(1)).cloned()
1659                    } else {
1660                        None
1661                    }
1662                })
1663                .unwrap_or_default();
1664            slices.push((zid_num, z25, z26));
1665        }
1666        if slices.len() < 2 {
1667            return ConditionResult::Unknown;
1668        }
1669        slices.sort_by_key(|s| s.0);
1670        // For each slice with Zeitraum-ID != 1, DTM+Z25 must equal DTM+Z26 of the immediately preceding slice
1671        for idx in 1..slices.len() {
1672            let (id, z25, _) = &slices[idx];
1673            let (_, _, prev_z26) = &slices[idx - 1];
1674            if *id != 1 && !z25.is_empty() && !prev_z26.is_empty() && z25 != prev_z26 {
1675                return ConditionResult::False;
1676            }
1677        }
1678        ConditionResult::True
1679    }
1680
1681    /// [135] Wenn dieses DTM+Z25 (Verwendung der Daten ab) nicht im SG6 RFF+Z47/ Z54 (Verwendungszeitraum der Daten: Im System vorhandene Daten/ Im System keine Daten vorhanden) mit der Zeitraum ID "1" im DE115...
1682    // REVIEW: Identical logic to condition [134] but applied to SG6 groups with RFF+Z47/Z54 (Im System vorhandene Daten / Im System keine Daten vorhanden) instead of Z48/Z55. Verifies temporal continuity: DTM+Z25 of each non-first slice must equal DTM+Z26 of the preceding slice when sorted by Zeitraum-ID. Same structural approach and same confidence level. (medium confidence)
1683    fn evaluate_135(&self, ctx: &EvaluationContext) -> ConditionResult {
1684        let nav = match ctx.navigator() {
1685            Some(n) => n,
1686            None => return ConditionResult::Unknown,
1687        };
1688        let sg6_count = nav.group_instance_count(&["SG4", "SG6"]);
1689
1690        // Collect (zeitraum_id_numeric, dtm_z25_value, dtm_z26_value) for SG6 groups with RFF+Z47 or RFF+Z54
1691        let mut slices: Vec<(i64, String, String)> = Vec::new();
1692        for i in 0..sg6_count {
1693            let rff_segs = nav.find_segments_in_group("RFF", &["SG4", "SG6"], i);
1694            // DE1156 is elements[0][2] for RFF Verwendungszeitraum
1695            let zeitraum_id = rff_segs.iter().find_map(|s| {
1696                let qual = s.elements.first().and_then(|e| e.first())?;
1697                if qual == "Z47" || qual == "Z54" {
1698                    let id = s.elements.first().and_then(|e| e.get(2))?;
1699                    if !id.is_empty() {
1700                        Some(id.clone())
1701                    } else {
1702                        None
1703                    }
1704                } else {
1705                    None
1706                }
1707            });
1708            let Some(zid_str) = zeitraum_id else { continue };
1709            let Ok(zid_num) = zid_str.parse::<i64>() else {
1710                continue;
1711            };
1712            let dtm_segs = nav.find_segments_in_group("DTM", &["SG4", "SG6"], i);
1713            let z25 = dtm_segs
1714                .iter()
1715                .find_map(|s| {
1716                    if s.elements
1717                        .first()
1718                        .and_then(|e| e.first())
1719                        .is_some_and(|v| v == "Z25")
1720                    {
1721                        s.elements.first().and_then(|e| e.get(1)).cloned()
1722                    } else {
1723                        None
1724                    }
1725                })
1726                .unwrap_or_default();
1727            let z26 = dtm_segs
1728                .iter()
1729                .find_map(|s| {
1730                    if s.elements
1731                        .first()
1732                        .and_then(|e| e.first())
1733                        .is_some_and(|v| v == "Z26")
1734                    {
1735                        s.elements.first().and_then(|e| e.get(1)).cloned()
1736                    } else {
1737                        None
1738                    }
1739                })
1740                .unwrap_or_default();
1741            slices.push((zid_num, z25, z26));
1742        }
1743        if slices.len() < 2 {
1744            return ConditionResult::Unknown;
1745        }
1746        slices.sort_by_key(|s| s.0);
1747        // For each slice with Zeitraum-ID != 1, DTM+Z25 must equal DTM+Z26 of the immediately preceding slice
1748        for idx in 1..slices.len() {
1749            let (id, z25, _) = &slices[idx];
1750            let (_, _, prev_z26) = &slices[idx - 1];
1751            if *id != 1 && !z25.is_empty() && !prev_z26.is_empty() && z25 != prev_z26 {
1752                return ConditionResult::False;
1753            }
1754        }
1755        ConditionResult::True
1756    }
1757
1758    /// [136] Wenn SG10 CCI+++E13 (Zählertyp) CAV+EHZ (elektronischer Haushaltszähler) in dieser SG8 vorhanden
1759    fn evaluate_136(&self, ctx: &EvaluationContext) -> ConditionResult {
1760        let nav = match ctx.navigator() {
1761            Some(n) => n,
1762            None => return ctx.has_qualified_value("CAV", 0, "EHZ", 0, 0, &["EHZ"]),
1763        };
1764        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
1765        for i in 0..sg8_count {
1766            let sg10_count = nav.child_group_instance_count(&["SG4", "SG8"], i, "SG10");
1767            for j in 0..sg10_count {
1768                let ccis = nav.find_segments_in_child_group("CCI", &["SG4", "SG8"], i, "SG10", j);
1769                let has_cci_e13 = ccis.iter().any(|s| {
1770                    s.elements
1771                        .get(2)
1772                        .and_then(|e| e.first())
1773                        .is_some_and(|v| v == "E13")
1774                });
1775                if has_cci_e13 {
1776                    let cavs =
1777                        nav.find_segments_in_child_group("CAV", &["SG4", "SG8"], i, "SG10", j);
1778                    if cavs.iter().any(|s| {
1779                        s.elements
1780                            .first()
1781                            .and_then(|e| e.first())
1782                            .is_some_and(|v| v == "EHZ")
1783                    }) {
1784                        return ConditionResult::True;
1785                    }
1786                }
1787            }
1788        }
1789        ConditionResult::False
1790    }
1791
1792    /// [139] Wenn SG10 CAV+IVA (Individuelle Abstimmung) in dieser SG8 nicht vorhanden
1793    // REVIEW: True when no SG10 child of any SG8 contains a CAV with elements[0][0]=='IVA'. Iterates all SG8 instances and their SG10 children; returns False as soon as IVA is found, True if absent throughout. 'In dieser SG8' is evaluated message-wide since no specific parent qualifier filter is given. (medium confidence)
1794    fn evaluate_139(&self, ctx: &EvaluationContext) -> ConditionResult {
1795        let nav = match ctx.navigator() {
1796            Some(n) => n,
1797            None => return ctx.lacks_qualifier("CAV", 0, "IVA"),
1798        };
1799        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
1800        for i in 0..sg8_count {
1801            let sg10_count = nav.child_group_instance_count(&["SG4", "SG8"], i, "SG10");
1802            for j in 0..sg10_count {
1803                let cavs = nav.find_segments_in_child_group("CAV", &["SG4", "SG8"], i, "SG10", j);
1804                if cavs.iter().any(|s| {
1805                    s.elements
1806                        .first()
1807                        .and_then(|e| e.first())
1808                        .is_some_and(|v| v == "IVA")
1809                }) {
1810                    return ConditionResult::False;
1811                }
1812            }
1813        }
1814        ConditionResult::True
1815    }
1816
1817    /// [157] Wenn SG4 STS+7++Z33+ZZB nicht vorhanden
1818    fn evaluate_157(&self, ctx: &EvaluationContext) -> ConditionResult {
1819        let result = ctx.has_segment_matching("STS", &[(0, 0, "7"), (2, 0, "Z33"), (3, 0, "ZZB")]);
1820        match result {
1821            ConditionResult::True => ConditionResult::False,
1822            ConditionResult::False => ConditionResult::True,
1823            ConditionResult::Unknown => ConditionResult::Unknown,
1824        }
1825    }
1826
1827    /// [168] Wenn in dieser SG8 im SG8 PIA+Z02 (Gruppenartikel- ID / Artikel-ID), die Artikel-ID 1-02-0-002/ 003/ 004 vorhanden
1828    fn evaluate_168(&self, ctx: &EvaluationContext) -> ConditionResult {
1829        ctx.any_group_has_qualified_value(
1830            "PIA",
1831            0,
1832            "Z02",
1833            1,
1834            0,
1835            &["1-02-0-002", "1-02-0-003", "1-02-0-004"],
1836            &["SG4", "SG8"],
1837        )
1838    }
1839
1840    /// [169] Wenn zwei SG8 SEQ+Z45 (Netznutzungsabrechnungsdaten der Marktlokation), mit derselben Zeitraum-ID im DE1050, mit Artikel-ID im SG8 PIA+Z02 (Gruppenartikel-ID / Artikel-ID), eine SG8 mit 1-02-0-002 ...
1841    // REVIEW: Complex cross-group condition. Collects all SG8 instances having SEQ+Z45 (Netznutzungsabrechnungsdaten), extracting the Zeitraum-ID from SEQ elements[1][0] (DE1050) and the PIA+Z02 article ID from elements[1][0]. Then checks if two such SG8 instances share the same Zeitraum-ID where one has article 1-02-0-002 and the other has 1-02-0-003 or 1-02-0-004. Medium confidence because the article ID string format ('1-02-0-002') must match exactly as stored in the EDIFACT value. (medium confidence)
1842    fn evaluate_169(&self, ctx: &EvaluationContext) -> ConditionResult {
1843        let nav = match ctx.navigator() {
1844            Some(n) => n,
1845            None => return ConditionResult::Unknown,
1846        };
1847        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
1848        let mut sg8_data: Vec<(String, String)> = Vec::new();
1849        for i in 0..sg8_count {
1850            let seqs = nav.find_segments_in_group("SEQ", &["SG4", "SG8"], i);
1851            let zeitraum_id = seqs
1852                .iter()
1853                .find(|s| {
1854                    s.elements
1855                        .first()
1856                        .and_then(|e| e.first())
1857                        .is_some_and(|v| v == "Z45")
1858                })
1859                .and_then(|s| s.elements.get(1))
1860                .and_then(|e| e.first())
1861                .filter(|v| !v.is_empty())
1862                .cloned();
1863            if let Some(zid) = zeitraum_id {
1864                let pias = nav.find_segments_in_group("PIA", &["SG4", "SG8"], i);
1865                let article_id = pias
1866                    .iter()
1867                    .find(|s| {
1868                        s.elements
1869                            .first()
1870                            .and_then(|e| e.first())
1871                            .is_some_and(|v| v == "Z02")
1872                    })
1873                    .and_then(|s| s.elements.get(1))
1874                    .and_then(|e| e.first())
1875                    .cloned()
1876                    .unwrap_or_default();
1877                sg8_data.push((zid, article_id));
1878            }
1879        }
1880        for i in 0..sg8_data.len() {
1881            for j in (i + 1)..sg8_data.len() {
1882                if sg8_data[i].0 == sg8_data[j].0 {
1883                    let (id_i, id_j) = (&sg8_data[i].1, &sg8_data[j].1);
1884                    let i_is_002 = id_i == "1-02-0-002";
1885                    let j_is_002 = id_j == "1-02-0-002";
1886                    let i_is_003_004 = id_i == "1-02-0-003" || id_i == "1-02-0-004";
1887                    let j_is_003_004 = id_j == "1-02-0-003" || id_j == "1-02-0-004";
1888                    if (i_is_002 && j_is_003_004) || (j_is_002 && i_is_003_004) {
1889                        return ConditionResult::True;
1890                    }
1891                }
1892            }
1893        }
1894        if sg8_data.is_empty() {
1895            ConditionResult::Unknown
1896        } else {
1897            ConditionResult::False
1898        }
1899    }
1900
1901    /// [206] Wenn SG4 STS+7++ZG5 (Transaktionsgrund: Aufhebung der zukünftigen Zuordnung aufgrund § 38 EEG 2014 bzw. § 21b Abs. 1 Nr. 2 EEG 2017) nicht vorhanden
1902    fn evaluate_206(&self, ctx: &EvaluationContext) -> ConditionResult {
1903        match ctx.has_segment_matching("STS", &[(0, 0, "7"), (2, 0, "ZG5")]) {
1904            ConditionResult::True => ConditionResult::False,
1905            ConditionResult::False => ConditionResult::True,
1906            ConditionResult::Unknown => ConditionResult::Unknown,
1907        }
1908    }
1909
1910    /// [242] Wenn mehr als ein SG8 (Referenz auf die Lokationsbündelstruktur) mit der selben Zeitraum-ID im DE1050 vorhanden
1911    fn evaluate_242(&self, ctx: &EvaluationContext) -> ConditionResult {
1912        let nav = match ctx.navigator() {
1913            Some(n) => n,
1914            None => return ConditionResult::Unknown,
1915        };
1916        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
1917        let mut ids: Vec<String> = Vec::new();
1918        for i in 0..sg8_count {
1919            let seq_segs = nav.find_segments_in_group("SEQ", &["SG4", "SG8"], i);
1920            for seq in &seq_segs {
1921                let qual = seq
1922                    .elements
1923                    .first()
1924                    .and_then(|e| e.first())
1925                    .map(|s| s.as_str());
1926                if matches!(qual, Some("Z78") | Some("ZC7") | Some("ZC8") | Some("ZD5")) {
1927                    if let Some(id) = seq.elements.get(1).and_then(|e| e.first()) {
1928                        if !id.is_empty() {
1929                            ids.push(id.clone());
1930                        }
1931                    }
1932                }
1933            }
1934        }
1935        if ids.is_empty() {
1936            return ConditionResult::Unknown;
1937        }
1938        let mut seen = std::collections::HashSet::new();
1939        ConditionResult::from(ids.iter().any(|id| !seen.insert(id.as_str())))
1940    }
1941
1942    /// [253] Wenn SG4 STS+E01++ZC9 (Ablehnung (keine Zuordnung möglich)) nicht vorhanden
1943    fn evaluate_253(&self, ctx: &EvaluationContext) -> ConditionResult {
1944        match ctx.has_segment_matching("STS", &[(0, 0, "E01"), (2, 0, "ZC9")]) {
1945            ConditionResult::True => ConditionResult::False,
1946            ConditionResult::False => ConditionResult::True,
1947            ConditionResult::Unknown => ConditionResult::Unknown,
1948        }
1949    }
1950
1951    /// [258] Wenn in dieser SG8 SEQ+Z01 (Daten der Marktlokation) SG10 CCI+Z30++Z07 (Verbrauch) vorhanden
1952    fn evaluate_258(&self, ctx: &EvaluationContext) -> ConditionResult {
1953        let nav = match ctx.navigator() {
1954            Some(n) => n,
1955            None => return ctx.has_qualified_value("CCI", 0, "Z30", 2, 0, &["Z07"]),
1956        };
1957        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
1958        for i in 0..sg8_count {
1959            let seq_segs = nav.find_segments_in_group("SEQ", &["SG4", "SG8"], i);
1960            let has_z01 = seq_segs.iter().any(|s| {
1961                s.elements
1962                    .first()
1963                    .and_then(|e| e.first())
1964                    .is_some_and(|v| v == "Z01")
1965            });
1966            if has_z01 {
1967                let sg10_count = nav.child_group_instance_count(&["SG4", "SG8"], i, "SG10");
1968                for j in 0..sg10_count {
1969                    let ccis =
1970                        nav.find_segments_in_child_group("CCI", &["SG4", "SG8"], i, "SG10", j);
1971                    if ccis.iter().any(|s| {
1972                        s.elements
1973                            .first()
1974                            .and_then(|e| e.first())
1975                            .is_some_and(|v| v == "Z30")
1976                            && s.elements
1977                                .get(2)
1978                                .and_then(|e| e.first())
1979                                .is_some_and(|v| v == "Z07")
1980                    }) {
1981                        return ConditionResult::True;
1982                    }
1983                }
1984            }
1985        }
1986        ConditionResult::False
1987    }
1988
1989    /// [297] Wenn mehr als eine SG8 SEQ+Z02/ ZE3 (OBIS- Daten der Marktlokation) mit einer OBIS-Kennzahl für Energiemenge im PIA+5, mit gleicher Zeitraum-ID und identischem Code im DE1229 des SG8 SEQ+Z02/ZA1/Z...
1990    // REVIEW: Find SG8 instances with SEQ qualifier Z02 or ZE3 that also have a PIA+5 segment. Collect (de1229_code, zeitraum_id) tuples. Condition is True when more than one such SG8 shares the same combination. Note: 'OBIS-Kennzahl für Energiemenge' filtering is not enforced here (would require knowledge of valid OBIS patterns), so presence of PIA+5 is used as a proxy. (medium confidence)
1991    fn evaluate_297(&self, ctx: &EvaluationContext) -> ConditionResult {
1992        let nav = match ctx.navigator() {
1993            Some(n) => n,
1994            None => return ConditionResult::Unknown,
1995        };
1996        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
1997        // Collect (seq_qualifier, zeitraum_id) tuples from SG8 instances with SEQ+Z02/ZE3 that also have PIA+5
1998        let mut keys: Vec<(String, String)> = Vec::new();
1999        for i in 0..sg8_count {
2000            let seq_segs = nav.find_segments_in_group("SEQ", &["SG4", "SG8"], i);
2001            for seq in &seq_segs {
2002                let qual = seq
2003                    .elements
2004                    .first()
2005                    .and_then(|e| e.first())
2006                    .map(|s| s.as_str());
2007                if matches!(qual, Some("Z02") | Some("ZE3")) {
2008                    let pia_segs = nav.find_segments_in_group("PIA", &["SG4", "SG8"], i);
2009                    let has_pia5 = pia_segs.iter().any(|s| {
2010                        s.elements
2011                            .first()
2012                            .and_then(|e| e.first())
2013                            .is_some_and(|v| v == "5")
2014                    });
2015                    if has_pia5 {
2016                        let zeitraum_id = seq
2017                            .elements
2018                            .get(1)
2019                            .and_then(|e| e.first())
2020                            .cloned()
2021                            .unwrap_or_default();
2022                        if !zeitraum_id.is_empty() {
2023                            keys.push((qual.unwrap_or_default().to_string(), zeitraum_id));
2024                        }
2025                    }
2026                }
2027            }
2028        }
2029        if keys.is_empty() {
2030            return ConditionResult::Unknown;
2031        }
2032        let mut seen = std::collections::HashSet::new();
2033        ConditionResult::from(keys.iter().any(|k| !seen.insert(k)))
2034    }
2035
2036    /// [312] Wenn DTM+137 (Nachrichtendatum) im DE2380 &lt; 202603312200?+00
2037    fn evaluate_312(&self, ctx: &EvaluationContext) -> ConditionResult {
2038        ctx.dtm_lt("137", "202603312200+00")
2039    }
2040
2041    /// [313] Wenn DTM+137 (Nachrichtendatum) im DE2380 ≥ 202603312200?+00
2042    fn evaluate_313(&self, ctx: &EvaluationContext) -> ConditionResult {
2043        ctx.dtm_ge("137", "202603312200+00")
2044    }
2045
2046    /// [326] Wenn die Marktlokation nicht stillgelegt wird
2047    /// EXTERNAL: Requires context from outside the message.
2048    fn evaluate_326(&self, ctx: &EvaluationContext) -> ConditionResult {
2049        ctx.external.evaluate("market_location_not_decommissioned")
2050    }
2051
2052    /// [328] Wenn die Marktlokation stillgelegt wird
2053    /// EXTERNAL: Requires context from outside the message.
2054    fn evaluate_328(&self, ctx: &EvaluationContext) -> ConditionResult {
2055        ctx.external.evaluate("market_location_decommissioned")
2056    }
2057
2058    /// [346] Wenn SG10 CCI+++E13 (Zählertyp) CAV+MME (mME) in dieser SG8 vorhanden
2059    fn evaluate_346(&self, ctx: &EvaluationContext) -> ConditionResult {
2060        let nav = match ctx.navigator() {
2061            Some(n) => n,
2062            None => {
2063                // Fallback: message-wide check for both CCI+E13 and CAV+MME
2064                let has_cci = ctx.has_qualifier("CCI", 2, "E13");
2065                let has_cav = ctx.has_qualifier("CAV", 0, "MME");
2066                return match (has_cci, has_cav) {
2067                    (ConditionResult::True, ConditionResult::True) => ConditionResult::True,
2068                    (ConditionResult::False, _) | (_, ConditionResult::False) => {
2069                        ConditionResult::False
2070                    }
2071                    _ => ConditionResult::Unknown,
2072                };
2073            }
2074        };
2075        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
2076        for i in 0..sg8_count {
2077            let sg10_count = nav.child_group_instance_count(&["SG4", "SG8"], i, "SG10");
2078            for j in 0..sg10_count {
2079                let ccis = nav.find_segments_in_child_group("CCI", &["SG4", "SG8"], i, "SG10", j);
2080                let has_cci_e13 = ccis.iter().any(|s| {
2081                    s.elements
2082                        .get(2)
2083                        .and_then(|e| e.first())
2084                        .is_some_and(|v| v == "E13")
2085                });
2086                if has_cci_e13 {
2087                    let cavs =
2088                        nav.find_segments_in_child_group("CAV", &["SG4", "SG8"], i, "SG10", j);
2089                    if cavs.iter().any(|s| {
2090                        s.elements
2091                            .first()
2092                            .and_then(|e| e.first())
2093                            .is_some_and(|v| v == "MME")
2094                    }) {
2095                        return ConditionResult::True;
2096                    }
2097                }
2098            }
2099        }
2100        ConditionResult::False
2101    }
2102
2103    /// [387] Wenn in derselben SG8 (OBIS-Daten der Marktlokation) eine OBIS-Kennzahl für Energiemenge im PIA+5 vorhanden und es sich lt. Codeliste OBIS um eine OBIS mit zugeordneter Zählzeit handelt (Tarif: e...
2104    // REVIEW: Iterates SG8 instances that are OBIS-Daten der Marktlokation (SEQ+Z02/ZA1/ZA2/ZE3), then for each finds PIA+5 and parses the OBIS string to extract the E value (Tarif/Zählzeit field at index 2 after splitting C.D.E by '.'). Returns True if E is in range 1–62, indicating a Zählzeit-differentiated (tariff-associated) OBIS. Medium confidence because the 'Energiemenge' semantic (C value should indicate energy quantity) is not fully validated — only the E=1..62 Zählzeit criterion stated in the condition text is checked. (medium confidence)
2105    fn evaluate_387(&self, ctx: &EvaluationContext) -> ConditionResult {
2106        let nav = match ctx.navigator() {
2107            Some(n) => n,
2108            None => return ConditionResult::Unknown,
2109        };
2110        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
2111        for i in 0..sg8_count {
2112            let seq_segs = nav.find_segments_in_group("SEQ", &["SG4", "SG8"], i);
2113            let is_obis_malo = seq_segs.iter().any(|s| {
2114                s.elements
2115                    .first()
2116                    .and_then(|e| e.first())
2117                    .is_some_and(|v| matches!(v.as_str(), "Z02" | "ZA1" | "ZA2" | "ZE3"))
2118            });
2119            if !is_obis_malo {
2120                continue;
2121            }
2122            let pia_segs = nav.find_segments_in_group("PIA", &["SG4", "SG8"], i);
2123            for pia in &pia_segs {
2124                if !pia
2125                    .elements
2126                    .first()
2127                    .and_then(|e| e.first())
2128                    .is_some_and(|v| v == "5")
2129                {
2130                    continue;
2131                }
2132                let obis = match pia.elements.get(1).and_then(|e| e.first()) {
2133                    Some(o) => o.as_str(),
2134                    None => continue,
2135                };
2136                // OBIS format: A-B:C.D.E*F — extract E (Tarif/Zählzeit) as split('.')[2] after ':'
2137                let after_colon = obis.find(':').map(|p| &obis[p + 1..]).unwrap_or(obis);
2138                let parts: Vec<&str> = after_colon.split('.').collect();
2139                if parts.len() >= 3 {
2140                    let e_str = parts[2].split('*').next().unwrap_or("");
2141                    if let Ok(e_val) = e_str.parse::<u32>() {
2142                        if e_val >= 1 && e_val <= 62 {
2143                            return ConditionResult::True;
2144                        }
2145                    }
2146                }
2147            }
2148        }
2149        ConditionResult::False
2150    }
2151
2152    /// [399] Wenn im SG8 SEQ+Z78 mit identischer Zeitraum-ID im DE1050 wie im DE3224 dieses Segments, das RFF+Z31 (Lokationsbündelstruktur) im DE1153 der Code Z31 (Lokationsbündelstruktur) vorhanden, und im D...
2153    fn evaluate_399(&self, _ctx: &EvaluationContext) -> ConditionResult {
2154        // TODO: Condition [399] requires manual implementation
2155        // Reason: Requires cross-group Zeitraum-ID matching: the SEQ+Z78 DE1050 value must equal DE3224 of 'dieses Segments'. The phrase 'dieses Segments' implies the condition is evaluated in the context of a specific segment instance (likely a SG5/SG6 LOC or RFF) whose Zeitraum-ID is to be compared — this runtime per-segment context is not available in the message-wide EvaluationContext. The RFF+Z31 value check (not equal to '9992 00000 001 8') is individually implementable, but without the Zeitraum-ID anchor the result would be incorrect for most PIDs.
2156        ConditionResult::Unknown
2157    }
2158
2159    /// [413] Wenn eine SG8 SEQ+Z02/ZA1/ZA2 (OBIS-Daten der Marktlokation) mit einer OBIS-Kennzahl für Wirkarbeit und kumuliert im PIA+5, mit gleicher Zeitraum-ID und identischem Code im DE1229 des SG8 SEQ+Z02/...
2160    fn evaluate_413(&self, _ctx: &EvaluationContext) -> ConditionResult {
2161        // TODO: Condition [413] requires manual implementation
2162        // Reason: Requires knowing the 'current SG8' instance being validated: the condition checks whether another SG8 with SEQ+Z02/ZA1/ZA2, same Zeitraum-ID (SEQ DE1050), and identical DE1229 code as the current SG8 exists and has a Wirkarbeit+kumuliert OBIS (C=1/2, D=8) in PIA+5. The 'same Zeitraum-ID and identical DE1229 code as the current SG8' comparison requires per-instance context not available in EvaluationContext. A message-wide existence check would produce false positives by ignoring the Zeitraum-ID/code correlation constraint.
2163        ConditionResult::Unknown
2164    }
2165
2166    /// [414] Wenn keine SG8 SEQ+Z02/ZA1/ZA2 (OBIS-Daten der Marktlokation) mit einer OBIS-Kennzahl für Wirkarbeit und kumuliert im PIA+5, mit gleicher Zeitraum-ID und identischem Code im DE1229 des SG8 SEQ+Z02...
2167    fn evaluate_414(&self, _ctx: &EvaluationContext) -> ConditionResult {
2168        // TODO: Condition [414] requires manual implementation
2169        // Reason: Negation of condition [413]. Same structural complexity: requires 'current SG8' context to verify that no other SG8 with matching Zeitraum-ID (SEQ DE1050) and same DE1229 code has a Wirkarbeit+kumuliert OBIS in PIA+5. Cannot be correctly implemented without the per-instance current SG8 reference.
2170        ConditionResult::Unknown
2171    }
2172
2173    /// [415] Wenn eine weitere SG8 SEQ+Z02/ZA1/ZA2 (OBIS-Daten der Marktlokation) mit einer OBIS-Kennzahl für Wirkarbeit und 1/4 Stunde im PIA+5, mit gleicher Zeitraum-ID und identischem Code im DE1229 des SG8...
2174    fn evaluate_415(&self, _ctx: &EvaluationContext) -> ConditionResult {
2175        // TODO: Condition [415] requires manual implementation
2176        // Reason: Analogous to condition [413] but targets Wirkarbeit+1/4 Stunde (OBIS D=5, quarter-hour interval measurement) instead of kumuliert (D=8). Requires the same 'current SG8' context for Zeitraum-ID and DE1229 code matching against another SG8 instance. Cannot be implemented correctly without per-instance evaluation context.
2177        ConditionResult::Unknown
2178    }
2179
2180    /// [416] Wenn keine SG8 SEQ+Z02/ZA1/ZA2 (OBIS-Daten der Marktlokation) mit einer OBIS-Kennzahl für Wirkarbeit und 1/4 Stunde im PIA+5, mit gleicher Zeitraum-ID und identischem Code im DE1229 des SG8 SEQ+Z0...
2181    fn evaluate_416(&self, _ctx: &EvaluationContext) -> ConditionResult {
2182        // TODO: Condition [416] requires manual implementation
2183        // Reason: This condition requires checking for absence of an SG8 SEQ+Z02/ZA1/ZA2 group that contains a PIA+5 with an OBIS code specifically representing 'Wirkarbeit und 1/4 Stunde'. Determining which OBIS code patterns qualify as 'Wirkarbeit' (active energy) and '1/4 Stunde' (15-minute granularity) requires domain-specific knowledge about OBIS code structure not derivable from the MIG schema alone. The cross-group Zeitraum-ID matching adds further complexity. Cannot implement reliably without an authoritative list of qualifying OBIS code patterns.
2184        ConditionResult::Unknown
2185    }
2186
2187    /// [461] Wenn in derselben SG10 das CCI (Art und Nutzung der Technischen Ressource) im CAV+ZG8 (Technischen Ressource fällt unter § 14a EnWG) vorhanden
2188    fn evaluate_461(&self, ctx: &EvaluationContext) -> ConditionResult {
2189        // Wenn in derselben SG10 das CCI (Art und Nutzung der Technischen Ressource)
2190        // im CAV+ZG8 (Technische Ressource fällt unter § 14a EnWG) vorhanden
2191        // CCI is the SG10 entry segment; condition reduces to: any SG10 has CAV with ZG8
2192        ctx.any_group_has_qualifier("CAV", 0, "ZG8", &["SG4", "SG8", "SG10"])
2193    }
2194
2195    /// [517] Hinweis: Anzuwenden, wenn einer Marktlokation eine Paket-ID zugeordnet wurde und diese wieder gelöscht werden soll, da sie nicht unter den genannten NB-Wechsel fällt
2196    fn evaluate_517(&self, _ctx: &EvaluationContext) -> ConditionResult {
2197        // Hinweis: Anzuwenden, wenn einer Marktlokation eine Paket-ID zugeordnet wurde
2198        // und diese wieder gelöscht werden soll, da sie nicht unter den genannten
2199        // NB-Wechsel fällt — informational note, always applies
2200        ConditionResult::True
2201    }
2202
2203    /// [534] Hinweis: Aufgrund der Wiederholbarkeit des SG4 STS Status der Antwort ist die Angabe von max. acht Verwendungszeiträumen möglich
2204    fn evaluate_534(&self, _ctx: &EvaluationContext) -> ConditionResult {
2205        // Hinweis: Aufgrund der Wiederholbarkeit des SG4 STS Status der Antwort ist
2206        // die Angabe von max. acht Verwendungszeiträumen möglich
2207        // — informational note about cardinality, always applies
2208        ConditionResult::True
2209    }
2210
2211    /// [557] Hinweis: Wurden vom Verantwortlichen mehr als acht Verwendungszeiträume gesendet, so werden acht beliebige Verwendungszeiträume beantwortet
2212    fn evaluate_557(&self, _ctx: &EvaluationContext) -> ConditionResult {
2213        // Hinweis: Wurden vom Verantwortlichen mehr als acht Verwendungszeiträume
2214        // gesendet, so werden acht beliebige Verwendungszeiträume beantwortet
2215        // — informational processing note, always applies
2216        ConditionResult::True
2217    }
2218
2219    /// [560] Hinweis: Dies entspricht dem Geschäftsvorfall A
2220    fn evaluate_560(&self, _ctx: &EvaluationContext) -> ConditionResult {
2221        // Hinweis: Dies entspricht dem Geschäftsvorfall A — informational note, always applies
2222        ConditionResult::True
2223    }
2224
2225    /// [561] Hinweis: Dies entspricht dem Geschäftsvorfall B
2226    fn evaluate_561(&self, _ctx: &EvaluationContext) -> ConditionResult {
2227        // Hinweis: Dies entspricht dem Geschäftsvorfall B — informational note, always applies
2228        ConditionResult::True
2229    }
2230
2231    /// [562] Hinweis: Bei Marktlokationen, welche auf Basis von Werten bilanziert werden, wird der erste Tag des Jahres angegeben.
2232    fn evaluate_562(&self, _ctx: &EvaluationContext) -> ConditionResult {
2233        // Hinweis: Bei Marktlokationen, welche auf Basis von Werten bilanziert werden, wird der erste Tag des Jahres angegeben — informational note, always applies
2234        ConditionResult::True
2235    }
2236
2237    /// [564] Hinweis: Das IDE+Z01 (Liste) definiert den Geschäftsvorfall. Alle aufgelisteten IDE+24 sind Bestandteil des Geschäftsvorfalls
2238    fn evaluate_564(&self, _ctx: &EvaluationContext) -> ConditionResult {
2239        // Hinweis: Das IDE+Z01 (Liste) definiert den Geschäftsvorfall. Alle aufgelisteten IDE+24 sind Bestandteil des Geschäftsvorfalls — informational note, always applies
2240        ConditionResult::True
2241    }
2242
2243    /// [565] Hinweis: Das IDE+24 beschreibt einen Bestandteil einer Liste, wenn ein IDE+Z01 vorhanden ist. Es beschreibt nicht den Geschäftsvorfall
2244    fn evaluate_565(&self, _ctx: &EvaluationContext) -> ConditionResult {
2245        // Hinweis: Das IDE+24 beschreibt einen Bestandteil einer Liste, wenn ein IDE+Z01 vorhanden ist. Es beschreibt nicht den Geschäftsvorfall — informational note, always applies
2246        ConditionResult::True
2247    }
2248
2249    /// [578] Hinweis: Dies gilt für den Zeitraum welcher mit Z53 (keine Daten) definiert ist
2250    fn evaluate_578(&self, _ctx: &EvaluationContext) -> ConditionResult {
2251        // Hinweis: Dies gilt für den Zeitraum welcher mit Z53 (keine Daten) definiert ist — informational note, always applies
2252        ConditionResult::True
2253    }
2254
2255    /// [580] Hinweis:  Ersatzbelieferung gibt es nur bei  - Marktlokationen in der Niederspannung, die kein Haushaltskunde gem. EnWG sind und die nicht mehr der gesetzlichen Ersatzversorgung (drei Monate) unter...
2256    fn evaluate_580(&self, _ctx: &EvaluationContext) -> ConditionResult {
2257        // Hinweis: Ersatzbelieferung context note (Niederspannung non-household / Mittelspannung/Hochspannung) — informational note, always applies
2258        ConditionResult::True
2259    }
2260
2261    /// [681] Hinweis: Übergangsversorgung gibt es nur bei Marktlokationen, die unter § 38a EnWG fallen. Grundlage ist eine bilaterale Vereinbarung
2262    fn evaluate_681(&self, _ctx: &EvaluationContext) -> ConditionResult {
2263        // Hinweis: Übergangsversorgung applies only to Marktlokationen under § 38a EnWG based on bilateral agreement — informational note, always applies
2264        ConditionResult::True
2265    }
2266
2267    /// [686] Hinweis: Zu verwenden bei der Abmeldung der Übergangsversorgung.
2268    fn evaluate_686(&self, _ctx: &EvaluationContext) -> ConditionResult {
2269        // Hinweis: Zu verwenden bei der Abmeldung der Übergangsversorgung — informational note, always applies
2270        ConditionResult::True
2271    }
2272
2273    /// [2000] Je SG5 LOC+Z16 (Marktlokation) ist genau einmal die Segmentgruppe anzugeben
2274    // REVIEW: Checks that per SG4 transaction group, exactly one SG5 child group contains a LOC segment with qualifier Z16 (Marktlokation). Uses parent-child navigator to iterate SG4 instances and count LOC+Z16 occurrences in their SG5 children. Falls back to message-wide count when no navigator is available. (medium confidence)
2275    fn evaluate_2000(&self, ctx: &EvaluationContext) -> ConditionResult {
2276        // Je SG5 LOC+Z16 (Marktlokation) ist genau einmal die Segmentgruppe anzugeben
2277        // Per SG4 transaction group, exactly one SG5 instance should contain LOC+Z16
2278        let nav = match ctx.navigator() {
2279            Some(n) => n,
2280            None => {
2281                let count = ctx.count_qualified_in_group("LOC", 0, "Z16", &["SG4", "SG5"]);
2282                return ConditionResult::from(count == 1);
2283            }
2284        };
2285        let sg4_count = nav.group_instance_count(&["SG4"]);
2286        if sg4_count == 0 {
2287            return ConditionResult::Unknown;
2288        }
2289        for i in 0..sg4_count {
2290            let sg5_count = nav.child_group_instance_count(&["SG4"], i, "SG5");
2291            let mut loc_z16_count = 0usize;
2292            for j in 0..sg5_count {
2293                let locs = nav.find_segments_in_child_group("LOC", &["SG4"], i, "SG5", j);
2294                for loc in &locs {
2295                    if loc
2296                        .elements
2297                        .first()
2298                        .and_then(|e| e.first())
2299                        .is_some_and(|v| v == "Z16")
2300                    {
2301                        loc_z16_count += 1;
2302                    }
2303                }
2304            }
2305            if loc_z16_count != 1 {
2306                return ConditionResult::False;
2307            }
2308        }
2309        ConditionResult::True
2310    }
2311
2312    /// [2006] Segmentgruppe ist genau einmal für jede SG8 SEQ+Z01 (Daten der Marktlokation) anzugeben, bei der die Bedingung [199] an der Segmentgruppe erfüllt ist. Dabei ist die selbe Zeitraum-ID im nachfolge...
2313    // REVIEW: Condition 2006 requires the segment group to appear exactly once for each SG8 SEQ+Z01 where condition [199] is fulfilled, with matching Zeitraum-ID (SEQ DE1050). Implementation: collect Zeitraum-IDs from qualifying SG8s, then verify each appears exactly once across SG8 SEQ groups. Medium confidence because the exact 'current group' path is not directly accessible — we approximate via Zeitraum-ID uniqueness across all SG8 SEQ instances. (medium confidence)
2314    fn evaluate_2006(&self, ctx: &EvaluationContext) -> ConditionResult {
2315        let nav = match ctx.navigator() {
2316            Some(n) => n,
2317            None => return ConditionResult::Unknown,
2318        };
2319        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
2320        let mut qualifying_zeitraum_ids: Vec<String> = Vec::new();
2321        for i in 0..sg8_count {
2322            let seq_segs = nav.find_segments_in_group("SEQ", &["SG4", "SG8"], i);
2323            let z01_seq = seq_segs.iter().find(|s| {
2324                s.elements
2325                    .first()
2326                    .and_then(|e| e.first())
2327                    .is_some_and(|v| v == "Z01")
2328            });
2329            if let Some(seq) = z01_seq {
2330                if self.evaluate(199, ctx).is_true() {
2331                    if let Some(zeitraum_id) = seq.elements.get(1).and_then(|e| e.first()) {
2332                        if !zeitraum_id.is_empty() {
2333                            qualifying_zeitraum_ids.push(zeitraum_id.clone());
2334                        }
2335                    }
2336                }
2337            }
2338        }
2339        if qualifying_zeitraum_ids.is_empty() {
2340            return ConditionResult::Unknown;
2341        }
2342        // Verify each qualifying Zeitraum-ID appears exactly once across all SG8 SEQ groups
2343        let mut id_counts: std::collections::HashMap<String, usize> =
2344            std::collections::HashMap::new();
2345        for i in 0..sg8_count {
2346            let seq_segs = nav.find_segments_in_group("SEQ", &["SG4", "SG8"], i);
2347            for seq in &seq_segs {
2348                if let Some(zeitraum_id) = seq.elements.get(1).and_then(|e| e.first()) {
2349                    if !zeitraum_id.is_empty() && qualifying_zeitraum_ids.contains(zeitraum_id) {
2350                        *id_counts.entry(zeitraum_id.clone()).or_insert(0) += 1;
2351                    }
2352                }
2353            }
2354        }
2355        ConditionResult::from(
2356            qualifying_zeitraum_ids
2357                .iter()
2358                .all(|id| id_counts.get(id).copied().unwrap_or(0) == 1),
2359        )
2360    }
2361
2362    /// [2007] Segmentgruppe ist genau einmal für jede SG8 SEQ+Z01 (Daten der Marktlokation) anzugeben, bei der die Bedingungen [199] an der Segmentgruppe erfüllt ist. Dabei ist die selbe Zeitraum-ID im nachfol...
2363    // REVIEW: Condition 2007 is semantically identical to 2006 — the only difference is grammatical (plural 'Bedingungen' vs singular 'Bedingung'). Same logic: exactly once per qualifying SG8 SEQ+Z01 where condition [199] is met, with matching Zeitraum-ID. (medium confidence)
2364    fn evaluate_2007(&self, ctx: &EvaluationContext) -> ConditionResult {
2365        let nav = match ctx.navigator() {
2366            Some(n) => n,
2367            None => return ConditionResult::Unknown,
2368        };
2369        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
2370        let mut qualifying_zeitraum_ids: Vec<String> = Vec::new();
2371        for i in 0..sg8_count {
2372            let seq_segs = nav.find_segments_in_group("SEQ", &["SG4", "SG8"], i);
2373            let z01_seq = seq_segs.iter().find(|s| {
2374                s.elements
2375                    .first()
2376                    .and_then(|e| e.first())
2377                    .is_some_and(|v| v == "Z01")
2378            });
2379            if let Some(seq) = z01_seq {
2380                if self.evaluate(199, ctx).is_true() {
2381                    if let Some(zeitraum_id) = seq.elements.get(1).and_then(|e| e.first()) {
2382                        if !zeitraum_id.is_empty() {
2383                            qualifying_zeitraum_ids.push(zeitraum_id.clone());
2384                        }
2385                    }
2386                }
2387            }
2388        }
2389        if qualifying_zeitraum_ids.is_empty() {
2390            return ConditionResult::Unknown;
2391        }
2392        let mut id_counts: std::collections::HashMap<String, usize> =
2393            std::collections::HashMap::new();
2394        for i in 0..sg8_count {
2395            let seq_segs = nav.find_segments_in_group("SEQ", &["SG4", "SG8"], i);
2396            for seq in &seq_segs {
2397                if let Some(zeitraum_id) = seq.elements.get(1).and_then(|e| e.first()) {
2398                    if !zeitraum_id.is_empty() && qualifying_zeitraum_ids.contains(zeitraum_id) {
2399                        *id_counts.entry(zeitraum_id.clone()).or_insert(0) += 1;
2400                    }
2401                }
2402            }
2403        }
2404        ConditionResult::from(
2405            qualifying_zeitraum_ids
2406                .iter()
2407                .all(|id| id_counts.get(id).copied().unwrap_or(0) == 1),
2408        )
2409    }
2410
2411    /// [2008] Segmentgruppe ist genau einmal für jede SG8 SEQ+Z01 (Daten der Marktlokation) anzugeben, bei der die Bedingung [89] an der Segmentgruppe erfüllt ist. Dabei ist die selbe Zeitraum-ID im nachfolgen...
2412    // REVIEW: Condition 2008 is the same structure as 2006/2007 but references condition [89] instead of [199]. Exactly once per SG8 SEQ+Z01 where condition [89] is fulfilled, with matching Zeitraum-ID in SEQ DE1050. (medium confidence)
2413    fn evaluate_2008(&self, ctx: &EvaluationContext) -> ConditionResult {
2414        let nav = match ctx.navigator() {
2415            Some(n) => n,
2416            None => return ConditionResult::Unknown,
2417        };
2418        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
2419        let mut qualifying_zeitraum_ids: Vec<String> = Vec::new();
2420        for i in 0..sg8_count {
2421            let seq_segs = nav.find_segments_in_group("SEQ", &["SG4", "SG8"], i);
2422            let z01_seq = seq_segs.iter().find(|s| {
2423                s.elements
2424                    .first()
2425                    .and_then(|e| e.first())
2426                    .is_some_and(|v| v == "Z01")
2427            });
2428            if let Some(seq) = z01_seq {
2429                if self.evaluate(89, ctx).is_true() {
2430                    if let Some(zeitraum_id) = seq.elements.get(1).and_then(|e| e.first()) {
2431                        if !zeitraum_id.is_empty() {
2432                            qualifying_zeitraum_ids.push(zeitraum_id.clone());
2433                        }
2434                    }
2435                }
2436            }
2437        }
2438        if qualifying_zeitraum_ids.is_empty() {
2439            return ConditionResult::Unknown;
2440        }
2441        let mut id_counts: std::collections::HashMap<String, usize> =
2442            std::collections::HashMap::new();
2443        for i in 0..sg8_count {
2444            let seq_segs = nav.find_segments_in_group("SEQ", &["SG4", "SG8"], i);
2445            for seq in &seq_segs {
2446                if let Some(zeitraum_id) = seq.elements.get(1).and_then(|e| e.first()) {
2447                    if !zeitraum_id.is_empty() && qualifying_zeitraum_ids.contains(zeitraum_id) {
2448                        *id_counts.entry(zeitraum_id.clone()).or_insert(0) += 1;
2449                    }
2450                }
2451            }
2452        }
2453        ConditionResult::from(
2454            qualifying_zeitraum_ids
2455                .iter()
2456                .all(|id| id_counts.get(id).copied().unwrap_or(0) == 1),
2457        )
2458    }
2459
2460    /// [2009] Segmentgruppe ist genau einmal für jede SG8 SEQ+Z01 (Daten der Marktlokation) anzugeben, bei der die Bedingungen [89] an der Segmentgruppe erfüllt ist. Dabei ist die selbe Zeitraum-ID im nachfolg...
2461    // REVIEW: Condition 2009 is the plural-grammar variant of 2008 ('Bedingungen' vs 'Bedingung'), semantically identical. Same implementation as 2008 referencing condition [89]. (medium confidence)
2462    fn evaluate_2009(&self, ctx: &EvaluationContext) -> ConditionResult {
2463        let nav = match ctx.navigator() {
2464            Some(n) => n,
2465            None => return ConditionResult::Unknown,
2466        };
2467        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
2468        let mut qualifying_zeitraum_ids: Vec<String> = Vec::new();
2469        for i in 0..sg8_count {
2470            let seq_segs = nav.find_segments_in_group("SEQ", &["SG4", "SG8"], i);
2471            let z01_seq = seq_segs.iter().find(|s| {
2472                s.elements
2473                    .first()
2474                    .and_then(|e| e.first())
2475                    .is_some_and(|v| v == "Z01")
2476            });
2477            if let Some(seq) = z01_seq {
2478                if self.evaluate(89, ctx).is_true() {
2479                    if let Some(zeitraum_id) = seq.elements.get(1).and_then(|e| e.first()) {
2480                        if !zeitraum_id.is_empty() {
2481                            qualifying_zeitraum_ids.push(zeitraum_id.clone());
2482                        }
2483                    }
2484                }
2485            }
2486        }
2487        if qualifying_zeitraum_ids.is_empty() {
2488            return ConditionResult::Unknown;
2489        }
2490        let mut id_counts: std::collections::HashMap<String, usize> =
2491            std::collections::HashMap::new();
2492        for i in 0..sg8_count {
2493            let seq_segs = nav.find_segments_in_group("SEQ", &["SG4", "SG8"], i);
2494            for seq in &seq_segs {
2495                if let Some(zeitraum_id) = seq.elements.get(1).and_then(|e| e.first()) {
2496                    if !zeitraum_id.is_empty() && qualifying_zeitraum_ids.contains(zeitraum_id) {
2497                        *id_counts.entry(zeitraum_id.clone()).or_insert(0) += 1;
2498                    }
2499                }
2500            }
2501        }
2502        ConditionResult::from(
2503            qualifying_zeitraum_ids
2504                .iter()
2505                .all(|id| id_counts.get(id).copied().unwrap_or(0) == 1),
2506        )
2507    }
2508
2509    /// [2011] Segmentgruppe ist genau einmal für jede Zeitraum-ID aus dem DE1156 der SG6 RFF+Z49 / Z53 (Verwendungszeitraum der Daten: "Gültige Daten" / "Keine Daten") aus der Anfragennachricht aus SG6 RFF+TN ...
2510    // REVIEW: Condition 2011 requires the group to appear exactly once for each Zeitraum-ID from DE1156 of SG6 RFF+Z49/Z53 (Verwendungszeitraum 'Gültige Daten'/'Keine Daten') as referenced by the request message (via SG6 RFF+TN). Implementation collects DE1156 values (elements[0][2]) from SG6 RFF with Z49 or Z53 qualifiers. Returns True when qualifying Zeitraum-IDs are found (indicating the group should exist). Medium confidence because the 'aus der Anfragenachricht' cross-message reference cannot be fully verified within a single message context. (medium confidence)
2511    fn evaluate_2011(&self, ctx: &EvaluationContext) -> ConditionResult {
2512        let nav = match ctx.navigator() {
2513            Some(n) => n,
2514            None => {
2515                // Fallback: check message-wide RFF+Z49/Z53 for DE1156 Zeitraum-IDs
2516                let rff_segs = ctx.find_segments("RFF");
2517                let has_qualifying = rff_segs.iter().any(|s| {
2518                    let qual = s
2519                        .elements
2520                        .first()
2521                        .and_then(|e| e.first())
2522                        .map(|v| v.as_str());
2523                    matches!(qual, Some("Z49") | Some("Z53"))
2524                        && s.elements
2525                            .first()
2526                            .and_then(|e| e.get(2))
2527                            .is_some_and(|v| !v.is_empty())
2528                });
2529                return if has_qualifying {
2530                    ConditionResult::True
2531                } else {
2532                    ConditionResult::Unknown
2533                };
2534            }
2535        };
2536        let sg6_count = nav.group_instance_count(&["SG4", "SG6"]);
2537        let mut zeitraum_ids: std::collections::HashSet<String> = std::collections::HashSet::new();
2538        for i in 0..sg6_count {
2539            let rff_segs = nav.find_segments_in_group("RFF", &["SG4", "SG6"], i);
2540            for rff in &rff_segs {
2541                let qual = rff
2542                    .elements
2543                    .first()
2544                    .and_then(|e| e.first())
2545                    .map(|s| s.as_str());
2546                if matches!(qual, Some("Z49") | Some("Z53")) {
2547                    // DE1156 is elements[0][2]
2548                    if let Some(zeitraum_id) = rff.elements.first().and_then(|e| e.get(2)) {
2549                        if !zeitraum_id.is_empty() {
2550                            zeitraum_ids.insert(zeitraum_id.clone());
2551                        }
2552                    }
2553                }
2554            }
2555        }
2556        if zeitraum_ids.is_empty() {
2557            return ConditionResult::Unknown;
2558        }
2559        // The segment group should appear exactly once per distinct Zeitraum-ID from SG6 RFF+Z49/Z53
2560        // We verify that qualifying Zeitraum-IDs are present — cardinality enforcement is structural
2561        ConditionResult::True
2562    }
2563
2564    /// [2013] Mindesten einmal anzugeben, wenn kein SG6 RFF+Z54 (Verwendungszeitraum der Daten: Im System keine Daten vorhanden) vorhanden
2565    fn evaluate_2013(&self, ctx: &EvaluationContext) -> ConditionResult {
2566        // Mindestens einmal anzugeben, wenn kein SG6 RFF+Z54 vorhanden
2567        // True when RFF+Z54 (Verwendungszeitraum: Im System keine Daten vorhanden) is absent
2568        ctx.lacks_qualifier("RFF", 0, "Z54")
2569    }
2570
2571    /// [2014] Mindesten einmal anzugeben, wenn kein SG6 RFF+Z47 (Verwendungszeitraum der Daten: Im System vorhandene Daten) vorhanden
2572    fn evaluate_2014(&self, ctx: &EvaluationContext) -> ConditionResult {
2573        // Mindestens einmal anzugeben, wenn kein SG6 RFF+Z47 vorhanden
2574        // True when RFF+Z47 (Verwendungszeitraum: Im System vorhandene Daten) is absent
2575        ctx.lacks_qualifier("RFF", 0, "Z47")
2576    }
2577
2578    /// [2016] Segmentgruppe ist mind. einmal für jede Zeitraum-ID aus dem DE1156 der SG6 RFF+Z49 / Z53 (Verwendungszeitraum der Daten: "Gültige Daten" / "Keine Daten") aus der Anfragennachricht aus SG6 RFF+TN ...
2579    // REVIEW: Cross-group Zeitraum-ID correlation: collect DE1156 values from SG6 RFF+Z49/Z53, verify each is covered by at least one SG8 SEQ's DE1050 reference. The mention of 'Anfragenachricht' (request message) and RFF+TN adds ambiguity about whether the source is this message or an external one; implemented as intra-message check on the most direct interpretation. (medium confidence)
2580    fn evaluate_2016(&self, ctx: &EvaluationContext) -> ConditionResult {
2581        // Segmentgruppe ist mind. einmal für jede Zeitraum-ID aus DE1156 der SG6 RFF+Z49/Z53 anzugeben
2582        // Collect Zeitraum-IDs from SG6 RFF+Z49 and Z53 (DE1156 = elements[0][2])
2583        // Then verify each required Zeitraum-ID is referenced by at least one SG8 SEQ (DE1050 = elements[1][0])
2584        let nav = match ctx.navigator() {
2585            Some(n) => n,
2586            None => return ConditionResult::Unknown,
2587        };
2588        let sg6_count = nav.group_instance_count(&["SG4", "SG6"]);
2589        let mut required_ids: std::collections::HashSet<String> = std::collections::HashSet::new();
2590        for i in 0..sg6_count {
2591            let rffs = nav.find_segments_in_group("RFF", &["SG4", "SG6"], i);
2592            for rff in &rffs {
2593                let qual = rff
2594                    .elements
2595                    .first()
2596                    .and_then(|e| e.first())
2597                    .map(|s| s.as_str());
2598                if matches!(qual, Some("Z49") | Some("Z53")) {
2599                    if let Some(zeitraum_id) = rff.elements.first().and_then(|e| e.get(2)) {
2600                        if !zeitraum_id.is_empty() {
2601                            required_ids.insert(zeitraum_id.clone());
2602                        }
2603                    }
2604                }
2605            }
2606        }
2607        if required_ids.is_empty() {
2608            return ConditionResult::Unknown;
2609        }
2610        // Check that for each required Zeitraum-ID at least one SG8 SEQ references it via DE1050
2611        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
2612        let mut found_ids: std::collections::HashSet<String> = std::collections::HashSet::new();
2613        for i in 0..sg8_count {
2614            let seqs = nav.find_segments_in_group("SEQ", &["SG4", "SG8"], i);
2615            for seq in &seqs {
2616                if let Some(ref_id) = seq.elements.get(1).and_then(|e| e.first()) {
2617                    if !ref_id.is_empty() {
2618                        found_ids.insert(ref_id.clone());
2619                    }
2620                }
2621            }
2622        }
2623        ConditionResult::from(required_ids.iter().all(|id| found_ids.contains(id)))
2624    }
2625
2626    /// [2121] Segmentgruppe ist genau einmal je SG8 SEQ+Z01/ Z80/Z81 (Daten der Marktlokation/ Erwartete Daten der Marktlokation/ Im System vorhandene Daten der Marktlokation) anzugeben
2627    // REVIEW: Cardinality condition: this segment group must appear exactly once per qualifying SG8 (those with SEQ+Z01/Z80/Z81 — Daten/Erwartete Daten/Im System vorhandene Daten der Marktlokation). As a boolean predicate the condition evaluates True when such qualifying SG8 instances exist, signalling that the subject segment group is required. Full cardinality validation (exactly-once enforcement) requires the broader validator framework. (medium confidence)
2628    fn evaluate_2121(&self, ctx: &EvaluationContext) -> ConditionResult {
2629        // Segmentgruppe ist genau einmal je SG8 SEQ+Z01/Z80/Z81 anzugeben
2630        // True when at least one SG8 with SEQ+Z01, Z80, or Z81 exists (cardinality trigger)
2631        ctx.any_group_has_any_qualifier("SEQ", 0, &["Z01", "Z80", "Z81"], &["SG4", "SG8"])
2632    }
2633
2634    /// [1] Wenn Aufteilung vorhanden
2635    fn evaluate_1(&self, ctx: &EvaluationContext) -> ConditionResult {
2636        ctx.external.evaluate("message_splitting")
2637    }
2638
2639    /// [4] Wenn MP-ID in SG2 NAD+MR (Nachrichtenempfänger) in der Rolle LF
2640    fn evaluate_4(&self, ctx: &EvaluationContext) -> ConditionResult {
2641        ctx.external.evaluate("recipient_is_lf")
2642    }
2643
2644    /// [5] Wenn MP-ID in SG2 NAD+MS (Nachrichtenabsender) in der Rolle LF
2645    fn evaluate_5(&self, ctx: &EvaluationContext) -> ConditionResult {
2646        ctx.external.evaluate("sender_is_lf")
2647    }
2648
2649    /// [6] Wenn MP-ID in SG2 NAD+MR (Nachrichtenempfänger) in der Rolle ÜNB
2650    fn evaluate_6(&self, ctx: &EvaluationContext) -> ConditionResult {
2651        ctx.external.evaluate("recipient_role_check")
2652    }
2653
2654    /// [7] Wenn SG4 STS+7++ZG9/ZH1/ZH2 (Transaktionsgrund: Aufhebung einer zukünftigen Zuordnung wegen Auszug des Kunden / -wegen Stilllegung / -wegen aufgehobenem Vertragsverhältnis) vorhanden
2655    fn evaluate_7(&self, ctx: &EvaluationContext) -> ConditionResult {
2656        let sts_segs = ctx.find_segments_with_qualifier("STS", 0, "7");
2657        let found = sts_segs.iter().any(|s| {
2658            s.elements.get(2)
2659                .and_then(|e| e.first())
2660                .is_some_and(|v| ["ZG9", "ZH1", "ZH2"].contains(&v.as_str()))
2661        });
2662        ConditionResult::from(found)
2663
2664    }
2665
2666    /// [8] Wenn für Datenclearing erforderlich
2667    fn evaluate_8(&self, ctx: &EvaluationContext) -> ConditionResult {
2668        ctx.external.evaluate("data_clearing_required")
2669    }
2670
2671    /// [9] Wenn MP-ID in SG2 NAD+MS (Nachrichtenabsender) in der Rolle MSB
2672    fn evaluate_9(&self, ctx: &EvaluationContext) -> ConditionResult {
2673        ctx.external.evaluate("sender_role_check")
2674    }
2675
2676    /// [10] Wenn SG4 STS+7++xxx+xxx+E01/E03 (Transaktionsgrund befristete Anmeldung) vorhanden
2677    fn evaluate_10(&self, ctx: &EvaluationContext) -> ConditionResult {
2678        // STS+7++E01+ZW4+E03: element[4] is c556_3.d9013 = transaktionsgrundErgaenzungBefristeteAnmeldung
2679        ctx.has_qualified_value("STS", 0, "7", 4, 0, &["E01", "E03"])
2680    }
2681
2682    /// [11] Wenn SG4 STS+7++ZG9/ZH1/ZH2 (Transaktionsgrund: Aufhebung einer zukünftigen Zuordnung wegen Auszug des Kunden / -wegen Stilllegung / -wegen aufgehobenem Vertragsverhältnis) nicht vorhanden
2683    fn evaluate_11(&self, ctx: &EvaluationContext) -> ConditionResult {
2684        let sts_segs = ctx.find_segments_with_qualifier("STS", 0, "7");
2685        let found = sts_segs.iter().any(|s| {
2686            s.elements.get(2)
2687                .and_then(|e| e.first())
2688                .is_some_and(|v| ["ZG9", "ZH1", "ZH2"].contains(&v.as_str()))
2689        });
2690        ConditionResult::from(!found)
2691
2692    }
2693
2694    /// [12] Wenn SG4 DTM+471 (Ende zum nächstmöglichem Termin) nicht vorhanden
2695    fn evaluate_12(&self, ctx: &EvaluationContext) -> ConditionResult {
2696        ctx.lacks_qualifier("DTM", 0, "471")
2697
2698    }
2699
2700    /// [13] Wenn SG4 STS+E01++Z01 (Status der Antwort: Zustimmung mit Terminänderung) nicht vorhanden
2701    fn evaluate_13(&self, ctx: &EvaluationContext) -> ConditionResult {
2702        match ctx.has_qualified_value("STS", 0, "E01", 2, 0, &["Z01"]) {
2703            ConditionResult::True => ConditionResult::False,
2704            ConditionResult::False => ConditionResult::True,
2705            other => other,
2706        }
2707
2708    }
2709
2710    /// [14] Wenn Datum bekannt
2711    fn evaluate_14(&self, ctx: &EvaluationContext) -> ConditionResult {
2712        ctx.external.evaluate("date_known")
2713    }
2714
2715    /// [15] Wenn in derselben SG8 das SEQ+Z98 (Informative Daten der Marktlokation) vorhanden
2716    fn evaluate_15(&self, ctx: &EvaluationContext) -> ConditionResult {
2717        ctx.has_qualifier("SEQ", 0, "Z98")
2718
2719    }
2720
2721    /// [16] Wenn SG4 STS+E01++Z12 (Status der Antwort: Ablehnung Vertragsbindung) vorhanden
2722    fn evaluate_16(&self, ctx: &EvaluationContext) -> ConditionResult {
2723        ctx.has_qualified_value("STS", 0, "E01", 2, 0, &["Z12"])
2724
2725    }
2726
2727    /// [17] Wenn in derselben SG8 das SEQ+Z01/Z80/Z81 (Daten der Marktlokation) vorhanden
2728    fn evaluate_17(&self, ctx: &EvaluationContext) -> ConditionResult {
2729        let seq_segs = ctx.find_segments("SEQ");
2730        let found = seq_segs.iter().any(|s| {
2731            s.elements.first()
2732                .and_then(|e| e.first())
2733                .is_some_and(|v| ["Z01", "Z80", "Z81"].contains(&v.as_str()))
2734        });
2735        ConditionResult::from(found)
2736
2737    }
2738
2739    /// [18] Wenn SG4 DTM+93 (Ende zum) nicht vorhanden
2740    fn evaluate_18(&self, ctx: &EvaluationContext) -> ConditionResult {
2741        ctx.lacks_qualifier("DTM", 0, "93")
2742
2743    }
2744
2745    /// [19] Wenn SG8 SEQ+Z01/ Z98 (Daten der Marktlokation) SG10 CCI+++ZC0 (Prognose auf Basis von Werten) vorhanden
2746    fn evaluate_19(&self, ctx: &EvaluationContext) -> ConditionResult {
2747        ctx.has_qualifier("CCI", 2, "ZC0")
2748
2749    }
2750
2751    /// [21] Wenn SG10 CCI+++ZA6 (Prognose auf Basis von Profilen) in dieser SG8 vorhanden
2752    fn evaluate_21(&self, ctx: &EvaluationContext) -> ConditionResult {
2753        ctx.has_qualifier("CCI", 2, "ZA6")
2754
2755    }
2756
2757    /// [22] Es ist die Zeitraum-ID vom DE1156 aus einem passenden SG6 RFF+Z47/ Z48/ Z49 (Verwendungszeitraum der Daten) einzutragen
2758    // REVIEW: Checks if any SG6 RFF+Z47/Z48/Z49 Zeitraum-ID (DE1156 at elements[0][2]) matches any SG8 SEQ Zeitraum-ID reference (DE1050 at elements[1][0]). Uses groups_share_qualified_value for cross-group correlation across all three qualifying codes. (medium confidence)
2759    fn evaluate_22(&self, ctx: &EvaluationContext) -> ConditionResult {
2760        for qual in &["Z47", "Z48", "Z49"] {
2761            if matches!(
2762                ctx.groups_share_qualified_value(
2763                    "RFF",
2764                    0,
2765                    qual,
2766                    0,
2767                    2,
2768                    &["SG4", "SG6"],
2769                    "SEQ",
2770                    1,
2771                    0,
2772                    &["SG4", "SG8"],
2773                ),
2774                ConditionResult::True
2775            ) {
2776                return ConditionResult::True;
2777            }
2778        }
2779        ConditionResult::False
2780    }
2781
2782    /// [23] Wenn in dieser SG4 das STS+E01++A05/A99 (Status der Antwort) vorhanden
2783    fn evaluate_23(&self, ctx: &EvaluationContext) -> ConditionResult {
2784        let sts_segs = ctx.find_segments_with_qualifier("STS", 0, "E01");
2785        let found = sts_segs.iter().any(|s| {
2786            s.elements.get(2)
2787                .and_then(|e| e.first())
2788                .is_some_and(|v| ["A05", "A99"].contains(&v.as_str()))
2789        });
2790        ConditionResult::from(found)
2791
2792    }
2793
2794    /// [24] Wenn in dieser SG4 das STS+E01++A25/A99 (Status der Antwort) vorhanden
2795    fn evaluate_24(&self, ctx: &EvaluationContext) -> ConditionResult {
2796        let sts_segs = ctx.find_segments_with_qualifier("STS", 0, "E01");
2797        let found = sts_segs.iter().any(|s| {
2798            s.elements.get(2)
2799                .and_then(|e| e.first())
2800                .is_some_and(|v| ["A25", "A99"].contains(&v.as_str()))
2801        });
2802        ConditionResult::from(found)
2803
2804    }
2805
2806    /// [25] Wenn die Veräußerungsform der erzeugenden Marktlokation der Marktprämie zugeordnet ist
2807    fn evaluate_25(&self, _ctx: &EvaluationContext) -> ConditionResult {
2808        // TODO: implement
2809        ConditionResult::Unknown
2810    }
2811
2812    /// [27] Wenn das DE2380 von SG4 DTM+Z01 (Kündigungsfrist des Vertrags) an vierter Stelle ein T oder R enthält
2813    fn evaluate_27(&self, ctx: &EvaluationContext) -> ConditionResult {
2814        let dtm_segs = ctx.find_segments_with_qualifier("DTM", 0, "Z01");
2815        let found = dtm_segs.iter().any(|s| {
2816            s.elements.first()
2817                .and_then(|e| e.get(1))
2818                .and_then(|v| v.chars().nth(3))
2819                .is_some_and(|c| ['T', 'R'].contains(&c))
2820        });
2821        ConditionResult::from(found)
2822
2823    }
2824
2825    /// [30] Wenn Antwort auf Aktivierung übermittelt wird
2826    fn evaluate_30(&self, ctx: &EvaluationContext) -> ConditionResult {
2827        // Antwort auf Aktivierung — depends on PID context
2828        ctx.external.evaluate("activation_response")
2829    }
2830
2831    /// [31] Wenn eine Korrektur erfolgt
2832    fn evaluate_31(&self, ctx: &EvaluationContext) -> ConditionResult {
2833        ctx.external.evaluate("correction_in_progress")
2834    }
2835
2836    /// [32] Wenn mehr als ein SG8 (Referenz auf die Lokationsbündelstruktur) vorhanden
2837    fn evaluate_32(&self, ctx: &EvaluationContext) -> ConditionResult {
2838        let seq_count = ctx.find_segments("SEQ").len();
2839        ConditionResult::from(seq_count > 1)
2840
2841    }
2842
2843    /// [33] Wenn in einer SG8 SEQ+Z79 im PIA+5 (Erforderliches Produkt) DE7140 ein Produkt-Code genannt ist, der in der Codeliste der Konfigurationen im Kapitel 6 \"Produkte zur Bestellung / Änderung von Date...
2844    fn evaluate_33(&self, ctx: &EvaluationContext) -> ConditionResult {
2845        ctx.external.evaluate("code_list_membership_check")
2846    }
2847
2848    /// [34] Wenn Antwort auf Deaktivierung übermittelt wird
2849    fn evaluate_34(&self, ctx: &EvaluationContext) -> ConditionResult {
2850        // Antwort auf Deaktivierung — depends on PID context
2851        ctx.external.evaluate("deactivation_response")
2852    }
2853
2854    /// [35] Wenn das DE2380 von SG4 DTM+Z01 (Kündigungsfrist des Vertrags) an vierter Stelle T (Termin) enthält
2855    fn evaluate_35(&self, ctx: &EvaluationContext) -> ConditionResult {
2856        let dtm_segs = ctx.find_segments_with_qualifier("DTM", 0, "Z01");
2857        let found = dtm_segs.iter().any(|s| {
2858            s.elements.first()
2859                .and_then(|e| e.get(1))
2860                .and_then(|v| v.chars().nth(3))
2861                .is_some_and(|c| ['T'].contains(&c))
2862        });
2863        ConditionResult::from(found)
2864
2865    }
2866
2867    /// [36] Wenn in derselben SG8 SEQ+Z79 im PIA+5 (Erforderliches Produkt) DE7140 ein Produkt-Code genannt ist, der in der Codeliste der Konfigurationen im Kapitel 6.1 \"Anmeldung einer Zuordnung des LFN (UTI...
2868    fn evaluate_36(&self, ctx: &EvaluationContext) -> ConditionResult {
2869        ctx.external.evaluate("code_list_membership_check")
2870    }
2871
2872    /// [37] Wenn Anmeldung/ Änderung befristet
2873    fn evaluate_37(&self, ctx: &EvaluationContext) -> ConditionResult {
2874        // Same check as [10]: STS element[4] is befristete Anmeldung code
2875        ctx.has_qualified_value("STS", 0, "7", 4, 0, &["E01", "E03"])
2876    }
2877
2878    /// [38] Es sind nur die Code der Produkteigenschaft zu dem in derselben SG8 SEQ+Z79 im PIA+5 (Erforderliches Produkt) DE7140 erlaubt, die in der Codeliste der Konfigurationen im Kapitel  6.1 \"Anmeldung ei...
2879    fn evaluate_38(&self, ctx: &EvaluationContext) -> ConditionResult {
2880        ctx.external.evaluate("code_list_membership_check")
2881    }
2882
2883    /// [39] Wenn in derselben SG8 SEQ+Z79 m PIA+5 (Erforderliches Produkt) DE7140 ein Produkt-Code genannt ist, der in der Codeliste der Konfigurationen im Kapitel 6.1 \"Anmeldung einer Zuordnung des LFN (UTIL...
2884    fn evaluate_39(&self, ctx: &EvaluationContext) -> ConditionResult {
2885        ctx.external.evaluate("code_list_membership_check")
2886    }
2887
2888    /// [40] Es ist nur der Wertebereich erlaubt, der zu dem in derselben SG8 SEQ+Z79 im PIA+5 (Erforderliches Produkt) DE7140 genannten Produkt, das in der Codeliste der Konfigurationen im Kapitel 6.1 \"Anmeld...
2889    fn evaluate_40(&self, ctx: &EvaluationContext) -> ConditionResult {
2890        ctx.external.evaluate("code_list_membership_check")
2891    }
2892
2893    /// [41] Es ist eine Produktpaket-ID aus dem DE1050 von einem SG8 SEQ+Z79 (Erforderliches Produkt) zu nennen
2894    fn evaluate_41(&self, ctx: &EvaluationContext) -> ConditionResult {
2895        // Check: Produktpaket-ID exists in SEQ+Z79 (Erforderliches Produkt)
2896        let seq_z79 = ctx.find_segments_with_qualifier("SEQ", 0, "Z79");
2897        if seq_z79.is_empty() {
2898            return ConditionResult::Unknown;
2899        }
2900        // Check if SEQ has a value in C286.D1050 (elements[1][0])
2901        let found = seq_z79.iter().any(|s| {
2902            s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| !v.is_empty())
2903        });
2904        ConditionResult::from(found)
2905    }
2906
2907    /// [42] Wenn mehr als ein SG8 SEQ+ZH0 (Priorisierung erforderliches Produktpaket) vorhanden
2908    fn evaluate_42(&self, ctx: &EvaluationContext) -> ConditionResult {
2909        // Count SEQ segments with qualifier ZH0
2910        let seq_segs = ctx.find_segments("SEQ");
2911        let zh0_count = seq_segs.iter().filter(|s| {
2912            s.elements.first()
2913                .and_then(|e| e.first())
2914                .is_some_and(|v| v == "ZH0")
2915        }).count();
2916        ConditionResult::from(zh0_count > 1)
2917    }
2918
2919    /// [43] Wenn in dieser SG4 das STS+E01++A12 / A13 (Status der Antwort) vorhanden
2920    fn evaluate_43(&self, ctx: &EvaluationContext) -> ConditionResult {
2921        let sts_segs = ctx.find_segments_with_qualifier("STS", 0, "E01");
2922        let found = sts_segs.iter().any(|s| {
2923            s.elements.get(2)
2924                .and_then(|e| e.first())
2925                .is_some_and(|v| ["A12", "A13"].contains(&v.as_str()))
2926        });
2927        ConditionResult::from(found)
2928
2929    }
2930
2931    /// [45] Wenn in derselben SG8 das SG10 CCI+Z01++Z82 (Verwendungsumfang: ID der prozessual behandelten Messlokation) vorhanden
2932    fn evaluate_45(&self, ctx: &EvaluationContext) -> ConditionResult {
2933        let cci_segs = ctx.find_segments_with_qualifier("CCI", 0, "Z01");
2934        let found = cci_segs.iter().any(|s| {
2935            s.elements.get(2)
2936                .and_then(|e| e.first())
2937                .is_some_and(|v| ["Z82"].contains(&v.as_str()))
2938        });
2939        ConditionResult::from(found)
2940
2941    }
2942
2943    /// [46] Wenn ID für Objekt vergeben wurde
2944    fn evaluate_46(&self, _ctx: &EvaluationContext) -> ConditionResult {
2945        // TODO: implement
2946        ConditionResult::Unknown
2947    }
2948
2949    /// [47] Wenn SG10 CCI+15++Z21 (Summenzeitreihentyp) CAV+DBA/ VZR/ EGS/ LGS/ SLS/ SES/ TLS/ TES/ BIL/ BIP/ BIT/ GAL/ GAP/ GAT/ GEL/ GEP/ GET/ SOL/ SOP/ SOT/ WFL/ WFP/ WFT/ WNL/ WNP/ WNT vorhanden
2950    // REVIEW: Iterates all SG8 instances and their SG10 children looking for CCI with elements[0][0]=="15" and elements[2][0]=="Z21" (Zeitreihentyp), then checks if the same SG10 has a CAV with elements[0][0] in the Summenzeitreihentyp code list. Uses navigator for parent-child group traversal. (medium confidence)
2951    fn evaluate_47(&self, ctx: &EvaluationContext) -> ConditionResult {
2952        const CODES: &[&str] = &[
2953            "DBA", "VZR", "EGS", "LGS", "SLS", "SES", "TLS", "TES", "BIL", "BIP", "BIT", "GAL",
2954            "GAP", "GAT", "GEL", "GEP", "GET", "SOL", "SOP", "SOT", "WFL", "WFP", "WFT", "WNL",
2955            "WNP", "WNT",
2956        ];
2957        let nav = match ctx.navigator {
2958            Some(n) => n,
2959            None => return ConditionResult::Unknown,
2960        };
2961        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
2962        for i in 0..sg8_count {
2963            let sg10_count = nav.child_group_instance_count(&["SG4", "SG8"], i, "SG10");
2964            for j in 0..sg10_count {
2965                let ccis = nav.find_segments_in_child_group("CCI", &["SG4", "SG8"], i, "SG10", j);
2966                let has_cci_15_z21 = ccis.iter().any(|s| {
2967                    s.elements
2968                        .first()
2969                        .and_then(|e: &Vec<String>| e.first())
2970                        .is_some_and(|v: &String| v == "15")
2971                        && s.elements
2972                            .get(2)
2973                            .and_then(|e: &Vec<String>| e.first())
2974                            .is_some_and(|v: &String| v == "Z21")
2975                });
2976                if has_cci_15_z21 {
2977                    let cavs =
2978                        nav.find_segments_in_child_group("CAV", &["SG4", "SG8"], i, "SG10", j);
2979                    if cavs.iter().any(|s| {
2980                        s.elements
2981                            .first()
2982                            .and_then(|e: &Vec<String>| e.first())
2983                            .is_some_and(|v: &String| CODES.contains(&v.as_str()))
2984                    }) {
2985                        return ConditionResult::True;
2986                    }
2987                }
2988            }
2989        }
2990        ConditionResult::False
2991    }
2992
2993    /// [48] Wenn in dieser SG4 das STS+E01++A99 (Status der Antwort: Sonstiges) vorhanden
2994    fn evaluate_48(&self, ctx: &EvaluationContext) -> ConditionResult {
2995        ctx.has_qualified_value("STS", 0, "E01", 2, 0, &["A99"])
2996
2997    }
2998
2999    /// [49] Wenn in dieser SG4 das STS+E01++E14 (Status der Antwort: Ablehnung Sonstiges) vorhanden
3000    fn evaluate_49(&self, ctx: &EvaluationContext) -> ConditionResult {
3001        ctx.has_qualified_value("STS", 0, "E01", 2, 0, &["E14"])
3002
3003    }
3004
3005    /// [50] Wenn kein SG8 SEQ+ZE7 (Informative Daten der Tranche) mit derselben Zeitraum-ID im DE1050 auf die gleiche Marktlokation-ID mit dem RFF+Z18 (Marktlokation) referenziert wie dieses SG8 SEQ+Z98 (Infor...
3006    // REVIEW: Collects (Zeitraum-ID from SEQ.elements[1][0], Marktlok-ID from sibling RFF+Z18.elements[0][1]) for all SG8 instances with SEQ+Z98 and SEQ+ZE7. Returns True if any Z98 group has no ZE7 group matching on both values. Uses collect_group_values with instance-index correlation via HashMap, assuming each SG8 has at most one SEQ (guaranteed as entry segment) and the zip ordering is stable. (medium confidence)
3007    fn evaluate_50(&self, ctx: &EvaluationContext) -> ConditionResult {
3008        use std::collections::HashMap;
3009
3010        let seq_quals = ctx.collect_group_values("SEQ", 0, 0, &["SG4", "SG8"]);
3011        let seq_zeitraum = ctx.collect_group_values("SEQ", 1, 0, &["SG4", "SG8"]);
3012        let rff_quals = ctx.collect_group_values("RFF", 0, 0, &["SG4", "SG8"]);
3013        let rff_ids = ctx.collect_group_values("RFF", 0, 1, &["SG4", "SG8"]);
3014
3015        // Build per-instance RFF lookup: instance_idx -> list of (qualifier, id)
3016        let mut rff_by_instance: HashMap<usize, Vec<(String, String)>> = HashMap::new();
3017        for ((idx, q), (_, id)) in rff_quals.iter().zip(rff_ids.iter()) {
3018            rff_by_instance
3019                .entry(*idx)
3020                .or_default()
3021                .push((q.clone(), id.clone()));
3022        }
3023
3024        // Build (zeitraum_id, marktlok_id) for Z98 and ZE7 SG8 instances
3025        let mut seq_zeitraum_map: HashMap<usize, String> = HashMap::new();
3026        for (idx, z) in &seq_zeitraum {
3027            seq_zeitraum_map.insert(*idx, z.clone());
3028        }
3029
3030        let z98_data: Vec<(String, String)> = seq_quals
3031            .iter()
3032            .filter(|(_, q)| q == "Z98")
3033            .map(|(idx, _)| {
3034                let zeitraum = seq_zeitraum_map.get(idx).cloned().unwrap_or_default();
3035                let marktlok = rff_by_instance
3036                    .get(idx)
3037                    .and_then(|rffs| rffs.iter().find(|(q, _)| q == "Z18"))
3038                    .map(|(_, id)| id.clone())
3039                    .unwrap_or_default();
3040                (zeitraum, marktlok)
3041            })
3042            .collect();
3043
3044        if z98_data.is_empty() {
3045            return ConditionResult::Unknown;
3046        }
3047
3048        let ze7_data: Vec<(String, String)> = seq_quals
3049            .iter()
3050            .filter(|(_, q)| q == "ZE7")
3051            .map(|(idx, _)| {
3052                let zeitraum = seq_zeitraum_map.get(idx).cloned().unwrap_or_default();
3053                let marktlok = rff_by_instance
3054                    .get(idx)
3055                    .and_then(|rffs| rffs.iter().find(|(q, _)| q == "Z18"))
3056                    .map(|(_, id)| id.clone())
3057                    .unwrap_or_default();
3058                (zeitraum, marktlok)
3059            })
3060            .collect();
3061
3062        ConditionResult::from(z98_data.iter().any(|(z98_zeit, z98_lok)| {
3063            !ze7_data
3064                .iter()
3065                .any(|(ze7_zeit, ze7_lok)| ze7_zeit == z98_zeit && ze7_lok == z98_lok)
3066        }))
3067    }
3068
3069    /// [52] Wenn SG10 CCI+Z07++Z55/ Z56 (Profiltyp: sonstige verbrauchende Marktlokation / sonstige erzeugende Marktlokation) vorhanden
3070    fn evaluate_52(&self, ctx: &EvaluationContext) -> ConditionResult {
3071        let cci_segs = ctx.find_segments_with_qualifier("CCI", 0, "Z07");
3072        let found = cci_segs.iter().any(|s| {
3073            s.elements.get(2)
3074                .and_then(|e| e.first())
3075                .is_some_and(|v| ["Z55", "Z56"].contains(&v.as_str()))
3076        });
3077        ConditionResult::from(found)
3078
3079    }
3080
3081    /// [53] Wenn weitere Präzisierungen als über CCI+Z07 (Profiltyp) möglich sind
3082    fn evaluate_53(&self, _ctx: &EvaluationContext) -> ConditionResult {
3083        // TODO: implement
3084        ConditionResult::Unknown
3085    }
3086
3087    /// [54] Wenn SG8 SEQ+Z01 (Daten der Marktlokation) SG10 CCI+Z30++Z06 (Lieferrichtung: Erzeugung) vorhanden
3088    fn evaluate_54(&self, ctx: &EvaluationContext) -> ConditionResult {
3089        let cci_segs = ctx.find_segments_with_qualifier("CCI", 0, "Z30");
3090        let found = cci_segs.iter().any(|s| {
3091            s.elements.get(2)
3092                .and_then(|e| e.first())
3093                .is_some_and(|v| ["Z06"].contains(&v.as_str()))
3094        });
3095        ConditionResult::from(found)
3096
3097    }
3098
3099    /// [55] Wenn in dieser SG4 das STS+E01++A06/ A99 (Status der Antwort) vorhanden
3100    fn evaluate_55(&self, ctx: &EvaluationContext) -> ConditionResult {
3101        let sts_segs = ctx.find_segments_with_qualifier("STS", 0, "E01");
3102        let found = sts_segs.iter().any(|s| {
3103            s.elements.get(2)
3104                .and_then(|e| e.first())
3105                .is_some_and(|v| ["A06", "A99"].contains(&v.as_str()))
3106        });
3107        ConditionResult::from(found)
3108
3109    }
3110
3111    /// [56] Wenn im STS+E01 im DE9013 (Status der Antwort) ein Antwortcode aus dem Cluster Zustimmung vorhanden ist
3112    fn evaluate_56(&self, ctx: &EvaluationContext) -> ConditionResult {
3113        // Check: STS+E01 DE9013 has a Zustimmung (approval) cluster code
3114        // Zustimmung codes start with A0x (A01-A07 are approval codes)
3115        let sts_segs = ctx.find_segments_with_qualifier("STS", 0, "E01");
3116        let found = sts_segs.iter().any(|s| {
3117            s.elements.get(2).and_then(|e| e.first()).is_some_and(|v| {
3118                ["A01", "A02", "A03", "A04", "A05", "A06", "A07", "A08", "A09", "A10",
3119                 "A11", "A12", "A13", "A14", "A15", "A16", "A17", "A25"].contains(&v.as_str())
3120            })
3121        });
3122        ConditionResult::from(found)
3123    }
3124
3125    /// [57] Wenn in derselben SG8 das CCI+Z45++ZD9 (Abrechnung findet statt) vorhanden ist
3126    fn evaluate_57(&self, ctx: &EvaluationContext) -> ConditionResult {
3127        let cci_segs = ctx.find_segments_with_qualifier("CCI", 0, "Z45");
3128        let found = cci_segs.iter().any(|s| {
3129            s.elements.get(2)
3130                .and_then(|e| e.first())
3131                .is_some_and(|v| ["ZD9"].contains(&v.as_str()))
3132        });
3133        ConditionResult::from(found)
3134
3135    }
3136
3137    /// [58] Wenn in diesem CCI das DE3055 mit dem Code 293 vorhanden
3138    fn evaluate_58(&self, ctx: &EvaluationContext) -> ConditionResult {
3139        let cci_segs = ctx.find_segments("CCI");
3140        let found = cci_segs.iter().any(|s| {
3141            // DE3055 is typically a component within CCI composites
3142            s.elements.iter().any(|e| e.iter().any(|v| v == "293"))
3143        });
3144        ConditionResult::from(found)
3145
3146    }
3147
3148    /// [60] Wenn BGM+Z90 (Beendigung der Zuordnung zur Lokation) vorhanden
3149    fn evaluate_60(&self, ctx: &EvaluationContext) -> ConditionResult {
3150        ctx.has_qualifier("BGM", 0, "Z90")
3151
3152    }
3153
3154    /// [61] Wenn BGM+Z89 (Zuordnung zur Lokation) vorhanden
3155    fn evaluate_61(&self, ctx: &EvaluationContext) -> ConditionResult {
3156        ctx.has_qualifier("BGM", 0, "Z89")
3157
3158    }
3159
3160    /// [62] Wenn mehr als eine SG8 SEQ+Z45 (Netznutzungsabrechnungsdaten der Marktlokation), mit derselben Zeitraum-ID im DE1050, mit einer Gruppen-/Artikel-ID im SG8 PIA+Z02 (Gruppenartikel-ID / Artikel-ID), ...
3161    // REVIEW: Iterates SG8 instances within each SG4, finds those with SEQ+Z45 and a non-empty Zeitraum-ID in elements[1][0]. Checks if PIA+Z02 in the same SG8 has a product code (elements[1][0]) beginning with 1-08-1 through 1-08-5. Groups by Zeitraum-ID and checks if any ID appears more than once. Requires navigator for per-instance group segment access; returns Unknown without one. (medium confidence)
3162    fn evaluate_62(&self, ctx: &EvaluationContext) -> ConditionResult {
3163        let nav = match ctx.navigator {
3164            Some(n) => n,
3165            None => return ConditionResult::Unknown,
3166        };
3167        use std::collections::HashMap;
3168        let mut count_by_zid: HashMap<String, u32> = HashMap::new();
3169        let sg4_count = nav.group_instance_count(&["SG4"]);
3170        for sg4_idx in 0..sg4_count {
3171            let sg8_count = nav.child_group_instance_count(&["SG4"], sg4_idx, "SG8");
3172            for sg8_idx in 0..sg8_count {
3173                let seqs =
3174                    nav.find_segments_in_child_group("SEQ", &["SG4"], sg4_idx, "SG8", sg8_idx);
3175                let zeitraum_opt = seqs
3176                    .iter()
3177                    .filter(|s| {
3178                        s.elements
3179                            .first()
3180                            .and_then(|e: &Vec<String>| e.first())
3181                            .is_some_and(|v: &String| v == "Z45")
3182                    })
3183                    .filter_map(|s| {
3184                        s.elements
3185                            .get(1)
3186                            .and_then(|e: &Vec<String>| e.first())
3187                            .filter(|v| !v.is_empty())
3188                            .cloned()
3189                    })
3190                    .next();
3191                let Some(zid) = zeitraum_opt else { continue };
3192                let pias =
3193                    nav.find_segments_in_child_group("PIA", &["SG4"], sg4_idx, "SG8", sg8_idx);
3194                let has_qualifying_pia = pias.iter().any(|s| {
3195                    s.elements
3196                        .first()
3197                        .and_then(|e: &Vec<String>| e.first())
3198                        .is_some_and(|v: &String| v == "Z02")
3199                        && s.elements
3200                            .get(1)
3201                            .and_then(|e: &Vec<String>| e.first())
3202                            .map(|code| {
3203                                code.starts_with("1-08-1")
3204                                    || code.starts_with("1-08-2")
3205                                    || code.starts_with("1-08-3")
3206                                    || code.starts_with("1-08-4")
3207                                    || code.starts_with("1-08-5")
3208                            })
3209                            .unwrap_or(false)
3210                });
3211                if has_qualifying_pia {
3212                    *count_by_zid.entry(zid).or_insert(0) += 1;
3213                }
3214            }
3215        }
3216        ConditionResult::from(count_by_zid.values().any(|&c| c > 1))
3217    }
3218
3219    /// [63] Wenn in dieser SG4 das STS+E01++A15/ A99 (Status der Antwort) vorhanden
3220    fn evaluate_63(&self, ctx: &EvaluationContext) -> ConditionResult {
3221        let sts_segs = ctx.find_segments_with_qualifier("STS", 0, "E01");
3222        let found = sts_segs.iter().any(|s| {
3223            s.elements.get(2)
3224                .and_then(|e| e.first())
3225                .is_some_and(|v| ["A15", "A99"].contains(&v.as_str()))
3226        });
3227        ConditionResult::from(found)
3228
3229    }
3230
3231    /// [64] Wenn mehr als eine SG8 SEQ+ZE1 (Informative Netznutzungsabrechnungsdaten der Marktlokation) mit einer Gruppen-/ Artikel-ID im SG8 PIA+Z02 (Gruppenartikel-ID / Artikel-ID), welche mit 1-08-1/2/3/4/5...
3232    fn evaluate_64(&self, ctx: &EvaluationContext) -> ConditionResult {
3233        let seq_segs = ctx.find_segments("SEQ");
3234        let count = seq_segs.iter().filter(|s| {
3235            s.elements.first()
3236                .and_then(|e| e.first())
3237                .is_some_and(|v| v == "ZE1")
3238        }).count();
3239        ConditionResult::from(count > 1)
3240
3241    }
3242
3243    /// [65] Wenn in derselben SG8 SEQ+ZE1 (Informative Netznutzungsabrechnungsdaten der Marktlokation) eine Gruppen-/Artikel-ID im PIA+Z02 (Gruppenartikel-ID / Artikel-ID), welche mit 1-08-1/2/3/4/5 beginnt, v...
3244    fn evaluate_65(&self, ctx: &EvaluationContext) -> ConditionResult {
3245        // Check: PIA+Z02 with code starting with 1-08-1/2/3/4/5 exists in same SG8 SEQ+ZE1
3246        let pia_segs = ctx.find_segments("PIA");
3247        let found = pia_segs.iter().any(|s| {
3248            s.elements.first().and_then(|e| e.first()).is_some_and(|v| v == "Z02")
3249                && s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
3250                    ["1-08-1", "1-08-2", "1-08-3", "1-08-4", "1-08-5"].iter().any(|p| v.starts_with(p))
3251                })
3252        });
3253        ConditionResult::from(found)
3254    }
3255
3256    /// [66] Wenn SG8 SEQ+ZH0 (Priorisierung erforderliches Produktpaket) mehr als einmal vorhanden
3257    fn evaluate_66(&self, ctx: &EvaluationContext) -> ConditionResult {
3258        let seq_segs = ctx.find_segments("SEQ");
3259        let count = seq_segs.iter().filter(|s| {
3260            s.elements.first()
3261                .and_then(|e| e.first())
3262                .is_some_and(|v| v == "ZH0")
3263        }).count();
3264        ConditionResult::from(count > 1)
3265
3266    }
3267
3268    /// [67] Wenn in keinem SG8 SEQ+Z79 (Bestandteil eine Produktpaketes CCI+Z66/CAV+ZH9 (Produkteigenschaft/ Code der Produkteigenschaft) der Code 9991000002933 (Ruhende Marktlokation ausprägen) vorhanden ist.
3269    fn evaluate_67(&self, ctx: &EvaluationContext) -> ConditionResult {
3270        // Check: no SG8 SEQ+Z79 has CAV+ZH9 with code 9991000002933
3271        let cav_segs = ctx.find_segments("CAV");
3272        let found = cav_segs.iter().any(|s| {
3273            s.elements.first().and_then(|e| e.first()).is_some_and(|v| v == "ZH9")
3274                && s.elements.first().and_then(|e| e.get(1)).is_some_and(|v| v == "9991000002933")
3275        });
3276        ConditionResult::from(!found)
3277    }
3278
3279    /// [68] Wenn SG8 SEQ+ZH0 (Priorisierung erforderliches Produktpaket) mehr als zweimal vorhanden
3280    fn evaluate_68(&self, ctx: &EvaluationContext) -> ConditionResult {
3281        let seq_segs = ctx.find_segments("SEQ");
3282        let count = seq_segs.iter().filter(|s| {
3283            s.elements.first()
3284                .and_then(|e| e.first())
3285                .is_some_and(|v| v == "ZH0")
3286        }).count();
3287        ConditionResult::from(count > 2)
3288
3289    }
3290
3291    /// [69] Wenn SG8 SEQ+ZH0 (Priorisierung erforderliches Produktpaket) mehr als dreimal vorhanden
3292    fn evaluate_69(&self, ctx: &EvaluationContext) -> ConditionResult {
3293        let seq_segs = ctx.find_segments("SEQ");
3294        let count = seq_segs.iter().filter(|s| {
3295            s.elements.first()
3296                .and_then(|e| e.first())
3297                .is_some_and(|v| v == "ZH0")
3298        }).count();
3299        ConditionResult::from(count > 3)
3300
3301    }
3302
3303    /// [70] Wenn SG8 SEQ+ZH0 (Priorisierung erforderliches Produktpaket) fünfmal vorhanden
3304    fn evaluate_70(&self, ctx: &EvaluationContext) -> ConditionResult {
3305        let seq_segs = ctx.find_segments("SEQ");
3306        let count = seq_segs.iter().filter(|s| {
3307            s.elements.first()
3308                .and_then(|e| e.first())
3309                .is_some_and(|v| v == "ZH0")
3310        }).count();
3311        ConditionResult::from(count == 5)
3312
3313    }
3314
3315    /// [71] Wenn Antwort auf Zuordnung übermittelt wird
3316    fn evaluate_71(&self, ctx: &EvaluationContext) -> ConditionResult {
3317        // Antwort auf Zuordnung — depends on PID context
3318        ctx.external.evaluate("assignment_response")
3319    }
3320
3321    /// [72] Wenn Antwort auf Beendigung der Zuordnung übermittelt wird
3322    fn evaluate_72(&self, ctx: &EvaluationContext) -> ConditionResult {
3323        // Antwort auf Beendigung der Zuordnung — depends on PID context
3324        ctx.external.evaluate("assignment_end_response")
3325    }
3326
3327    /// [73] Wenn in derselben SG8 im PIA DE7140 ein Code aus der Codeliste der Gruppenartikel- und Artikel-ID vorhanden ist, der in der Spalte UTILMD/Preisangabe mit X gekennzeichnet ist
3328    fn evaluate_73(&self, ctx: &EvaluationContext) -> ConditionResult {
3329        // Check: PIA DE7140 has a code from Gruppenartikel codeliste with Preisangabe=X
3330        // Approximate: check if PIA+Z02 exists (external codelist check not available)
3331        let pia_segs = ctx.find_segments("PIA");
3332        let found = pia_segs.iter().any(|s| {
3333            s.elements.first().and_then(|e| e.first()).is_some_and(|v| v == "Z02")
3334                && s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| !v.is_empty())
3335        });
3336        if found { ConditionResult::True } else { ConditionResult::Unknown }
3337    }
3338
3339    /// [74] Wenn in der selben SG8 SEQ+Z59 (Produkt-Daten der Marktlokation) das PIA+5 (Produkt-Daten der Marktlokation) vorhanden
3340    fn evaluate_74(&self, ctx: &EvaluationContext) -> ConditionResult {
3341        ctx.has_qualifier("PIA", 0, "5")
3342
3343    }
3344
3345    /// [75] Wenn in diesem PIA+5 in DE7140 der Code 9991000000721 (vgl.: Kapitel 4.2. Konfigurationsprodukte Leistungskurvendefinition der Codeliste der Konfigurationen) vorhanden
3346    fn evaluate_75(&self, ctx: &EvaluationContext) -> ConditionResult {
3347        ctx.external.evaluate("code_list_membership_check")
3348    }
3349
3350    /// [76] Wenn Summenzeitreihe auf Ebene des Bilanzierungsgebiet
3351    // REVIEW: SEQ+Z49 is 'Abgerechnete Daten der Bilanzierungsgebietssummenzeitreihe' — the only SEQ code in the reference that explicitly names a Bilanzierungsgebiet-level Summenzeitreihe. Checking its presence is the most direct mapping of the condition text. A broader interpretation involving CAV codes (Bezeichnung der Summenzeitreihe) is possible but those codes are not fully enumerated in the provided reference. (medium confidence)
3352    fn evaluate_76(&self, ctx: &EvaluationContext) -> ConditionResult {
3353        ctx.has_qualifier("SEQ", 0, "Z49")
3354    }
3355
3356    /// [77] Wenn SG8 SEQ+Z03 (Zähleinrichtungsdaten) CAV+Z30 (Identifikation/Nummer des Gerätes) nicht vorhanden
3357    fn evaluate_77(&self, ctx: &EvaluationContext) -> ConditionResult {
3358        ctx.lacks_qualifier("CAV", 0, "Z30")
3359
3360    }
3361
3362    /// [78] Wenn SG4 STS+7++E02 (Transaktionsgrund: Einzug in Neuanlage) nicht vorhanden
3363    fn evaluate_78(&self, ctx: &EvaluationContext) -> ConditionResult {
3364        match ctx.has_qualified_value("STS", 0, "7", 2, 0, &["E02"]) {
3365            ConditionResult::True => ConditionResult::False,
3366            ConditionResult::False => ConditionResult::True,
3367            other => other,
3368        }
3369
3370    }
3371
3372    /// [83] Wenn in dieser SG4 das STS+E01++A08/ A16/ A99 (Status der Antwort) vorhanden
3373    fn evaluate_83(&self, ctx: &EvaluationContext) -> ConditionResult {
3374        let sts_segs = ctx.find_segments_with_qualifier("STS", 0, "E01");
3375        let found = sts_segs.iter().any(|s| {
3376            s.elements.get(2)
3377                .and_then(|e| e.first())
3378                .is_some_and(|v| ["A08", "A16", "A99"].contains(&v.as_str()))
3379        });
3380        ConditionResult::from(found)
3381
3382    }
3383
3384    /// [84] Wenn SG4 STS+E01++A57 (Status der Antwort) vorhanden
3385    fn evaluate_84(&self, ctx: &EvaluationContext) -> ConditionResult {
3386        ctx.has_qualified_value("STS", 0, "E01", 2, 0, &["A57"])
3387
3388    }
3389
3390    /// [85] Wenn das DE2380 von SG4 DTM+Z01 (Kündigungsfrist des Vertrags) an vierter Stelle M, Q, H oder J enthält
3391    fn evaluate_85(&self, ctx: &EvaluationContext) -> ConditionResult {
3392        let dtm_segs = ctx.find_segments_with_qualifier("DTM", 0, "Z01");
3393        let found = dtm_segs.iter().any(|s| {
3394            s.elements.first()
3395                .and_then(|e| e.get(1))
3396                .and_then(|v| v.chars().nth(3))
3397                .is_some_and(|c| ['M', 'Q', 'H', 'J'].contains(&c))
3398        });
3399        ConditionResult::from(found)
3400
3401    }
3402
3403    /// [86] Wenn das RFF+Z50 (Termine der Marktlokation) aus dieser SG6 (Termine der Marktlokation) auf das gleiche SG5 LOC+Z16 (Marktlokation), wie ein RFF+Z18 (Marktlokation) aus einer SG8 SEQ+Z01 (Daten der...
3404    fn evaluate_86(&self, ctx: &EvaluationContext) -> ConditionResult {
3405        // Check: RFF+Z50 references same LOC as RFF+Z18 in another SG8
3406        ctx.groups_share_qualified_value(
3407            "RFF", 0, "Z50", 0, 2, &["SG4", "SG6"],
3408            "RFF", 0, 2, &["SG4", "SG8"],
3409        )
3410    }
3411
3412    /// [87] Es ist ein Monatserster 0 Uhr (gem. deutscher Zeit) anzugeben
3413    fn evaluate_87(&self, ctx: &EvaluationContext) -> ConditionResult {
3414        // Check: date value is 1st of month at 00:00 (format 303: CCYYMMDDHHmm)
3415        let dtm_segs = ctx.find_segments("DTM");
3416        let found = dtm_segs.iter().any(|s| {
3417            s.elements.first().and_then(|e| e.get(1)).is_some_and(|v| {
3418                v.len() >= 12
3419                    && &v[6..8] == "01"  // day = 01
3420                    && &v[8..12] == "0000"  // time = 00:00
3421            })
3422        });
3423        if !found {
3424            // Check if any DTM has a non-matching date (definite False) vs no DTM at all (Unknown)
3425            if dtm_segs.is_empty() {
3426                return ConditionResult::Unknown;
3427            }
3428        }
3429        ConditionResult::from(found)
3430    }
3431
3432    /// [88] Wert muss identisch mit DE2380 aus dem SG4 DTM+157 (Änderung zum) sein
3433    fn evaluate_88(&self, ctx: &EvaluationContext) -> ConditionResult {
3434        // Check: value matches DTM+157 (Änderung zum)
3435        let dtm157 = ctx.find_segments_with_qualifier("DTM", 0, "157");
3436        if dtm157.is_empty() {
3437            return ConditionResult::Unknown;
3438        }
3439        // Condition is True when DTM+157 exists (the value check is structural)
3440        ConditionResult::True
3441    }
3442
3443    /// [89] Wenn im SG8 SEQ+Z01 (Daten der Marktlokation) mit identischer Zeitraum-ID im DE1050 wie in diesem SG8, das SG10 CCI+++ZA6 (Prognosegrundlage der Marktlokation: Prognose auf Basis von Profilen) CAV+...
3444    // REVIEW: Checks whether any SG8 instance contains an SG10 child with CCI at elements[2][0]=ZA6 and CAV at elements[0][0]=E14. The condition text requires the Zeitraum-ID in DE1050 to match 'this SG8', but a per-group evaluation context is not available in the current API — the Zeitraum-ID cross-reference is therefore not enforced and is instead approximated as a message-wide check. The CCI qualifier position (elements[2][0]) is confirmed by the MIG reference for CCI segments in SG10. (medium confidence)
3445    fn evaluate_89(&self, ctx: &EvaluationContext) -> ConditionResult {
3446        let nav = match ctx.navigator {
3447            Some(n) => n,
3448            None => {
3449                return ctx.filtered_parent_child_has_qualifier(
3450                    &["SG4", "SG8"],
3451                    "SEQ",
3452                    0,
3453                    "Z01",
3454                    "SG10",
3455                    "CCI",
3456                    2,
3457                    "ZA6",
3458                )
3459            }
3460        };
3461        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
3462        for i in 0..sg8_count {
3463            let sg10_count = nav.child_group_instance_count(&["SG4", "SG8"], i, "SG10");
3464            for j in 0..sg10_count {
3465                let ccis = nav.find_segments_in_child_group("CCI", &["SG4", "SG8"], i, "SG10", j);
3466                let has_cci_za6 = ccis.iter().any(|s| {
3467                    s.elements
3468                        .get(2)
3469                        .and_then(|e: &Vec<String>| e.first())
3470                        .is_some_and(|v: &String| v == "ZA6")
3471                });
3472                if !has_cci_za6 {
3473                    continue;
3474                }
3475                let cavs = nav.find_segments_in_child_group("CAV", &["SG4", "SG8"], i, "SG10", j);
3476                if cavs.iter().any(|s| {
3477                    s.elements
3478                        .first()
3479                        .and_then(|e: &Vec<String>| e.first())
3480                        .is_some_and(|v: &String| v == "E14")
3481                }) {
3482                    return ConditionResult::True;
3483                }
3484            }
3485        }
3486        ConditionResult::False
3487    }
3488
3489    /// [90] Wenn SG10 CCI+++ZA6 (Prognosegrundlage der Marktlokation: Prognose auf Basis von Profilen) einmal mit CAV+E14 (TLP/TEP) und einmal mit CAV+E02 (SLP/SEP) in dieser SG8 vorhanden
3490    // REVIEW: Within each SG8, iterates all SG10 child instances. For each SG10 that has a CCI with elements[2][0]=ZA6, checks the CAV elements[0][0] value. Tracks whether E14 and E02 have both been seen within the same SG8. Returns True when both are found in a single SG8, satisfying the requirement that both Prognosegrundlage options appear 'in dieser SG8'. (medium confidence)
3491    fn evaluate_90(&self, ctx: &EvaluationContext) -> ConditionResult {
3492        let nav = match ctx.navigator {
3493            Some(n) => n,
3494            None => return ConditionResult::Unknown,
3495        };
3496        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
3497        for i in 0..sg8_count {
3498            let sg10_count = nav.child_group_instance_count(&["SG4", "SG8"], i, "SG10");
3499            let mut has_za6_e14 = false;
3500            let mut has_za6_e02 = false;
3501            for j in 0..sg10_count {
3502                let ccis = nav.find_segments_in_child_group("CCI", &["SG4", "SG8"], i, "SG10", j);
3503                let has_cci_za6 = ccis.iter().any(|s| {
3504                    s.elements
3505                        .get(2)
3506                        .and_then(|e: &Vec<String>| e.first())
3507                        .is_some_and(|v: &String| v == "ZA6")
3508                });
3509                if !has_cci_za6 {
3510                    continue;
3511                }
3512                let cavs = nav.find_segments_in_child_group("CAV", &["SG4", "SG8"], i, "SG10", j);
3513                for cav in &cavs {
3514                    if let Some(code) = cav.elements.first().and_then(|e: &Vec<String>| e.first()) {
3515                        if code == "E14" {
3516                            has_za6_e14 = true;
3517                        }
3518                        if code == "E02" {
3519                            has_za6_e02 = true;
3520                        }
3521                    }
3522                }
3523            }
3524            if has_za6_e14 && has_za6_e02 {
3525                return ConditionResult::True;
3526            }
3527        }
3528        ConditionResult::False
3529    }
3530
3531    /// [91] Wenn nicht SG10 CCI+++ZA6 (Prognosegrundlage der Marktlokation: Prognose auf Basis von Profilen) einmal mit CAV+E14 (TLP/ TEP) und einmal mit CAV+E02 (SLP/SEP) in dieser SG8 vorhanden
3532    // REVIEW: Logical negation of condition 90. Condition 91 is explicitly defined as 'Wenn NICHT (CCI+++ZA6 einmal mit CAV+E14 und einmal mit CAV+E02 in dieser SG8 vorhanden)', which is the exact complement of condition 90. Preserving Unknown propagation handles the no-navigator fallback case correctly. (medium confidence)
3533    fn evaluate_91(&self, ctx: &EvaluationContext) -> ConditionResult {
3534        match self.evaluate_90(ctx) {
3535            ConditionResult::True => ConditionResult::False,
3536            ConditionResult::False => ConditionResult::True,
3537            ConditionResult::Unknown => ConditionResult::Unknown,
3538        }
3539    }
3540
3541    /// [92] Wenn Wert innerhalb SG bzw. Segment geändert wird
3542    fn evaluate_92(&self, _ctx: &EvaluationContext) -> ConditionResult {
3543        // TODO: implement
3544        ConditionResult::Unknown
3545    }
3546
3547    /// [93] Erlaubte Codes aus der PRICAT BGM+Z32 (Preisblatt Messstellenbetrieb) des verantwortlichen MSB
3548    fn evaluate_93(&self, _ctx: &EvaluationContext) -> ConditionResult {
3549        // TODO: implement
3550        ConditionResult::Unknown
3551    }
3552
3553    /// [94] Wenn ein Segment innerhalb der SG vorhanden
3554    fn evaluate_94(&self, _ctx: &EvaluationContext) -> ConditionResult {
3555        // Structural: segment exists within SG (always true for valid messages)
3556        ConditionResult::True
3557    }
3558
3559    /// [95] Wenn in derselben SG10 das CCI+Z17 (Stromverbrauchsart) CAV+ZE5 (E-Mobilität) vorhanden
3560    fn evaluate_95(&self, ctx: &EvaluationContext) -> ConditionResult {
3561        let cci_segs = ctx.find_segments_with_qualifier("CCI", 0, "Z17");
3562        if cci_segs.is_empty() {
3563            return ConditionResult::from(false);
3564        }
3565        let cav_segs = ctx.find_segments("CAV");
3566        let found = cav_segs.iter().any(|s| {
3567            s.elements.first()
3568                .and_then(|e| e.first())
3569                .is_some_and(|v| ["ZE5"].contains(&v.as_str()))
3570        });
3571        ConditionResult::from(found)
3572
3573    }
3574
3575    /// [96] Wenn SG4 STS+7++xxx+ZAP (Transaktionsgrundergänzung ruhende Marktlokation) vorhanden
3576    fn evaluate_96(&self, ctx: &EvaluationContext) -> ConditionResult {
3577        let sts_segs = ctx.find_segments_with_qualifier("STS", 0, "7");
3578        let found = sts_segs.iter().any(|s| {
3579            s.elements.get(3)
3580                .and_then(|e| e.first())
3581                .is_some_and(|v| ["ZAP"].contains(&v.as_str()))
3582        });
3583        ConditionResult::from(found)
3584
3585    }
3586
3587    /// [97] Wenn in einem SG10 CAV+ZH9 DE7110 der Code der Produkteigenschaft (Wertebereich) 9991000002420 (Marktprämie) vorhanden ist
3588    fn evaluate_97(&self, ctx: &EvaluationContext) -> ConditionResult {
3589        // Check: CAV+ZH9 has code 9991000002420 (Marktprämie)
3590        let cav_segs = ctx.find_segments("CAV");
3591        let found = cav_segs.iter().any(|s| {
3592            s.elements.first().and_then(|e| e.first()).is_some_and(|v| v == "ZH9")
3593                && s.elements.first().and_then(|e| e.get(1)).is_some_and(|v| v == "9991000002420")
3594        });
3595        ConditionResult::from(found)
3596    }
3597
3598    /// [98] Wenn MP-ID in SG2 NAD+MS (Nachrichtenabsender) in der Rolle NB
3599    fn evaluate_98(&self, ctx: &EvaluationContext) -> ConditionResult {
3600        ctx.external.evaluate("sender_role_check")
3601    }
3602
3603    /// [99] Wenn MP-ID in SG2 NAD+MS (Nachrichtenabsender) in der Rolle ÜNB
3604    fn evaluate_99(&self, ctx: &EvaluationContext) -> ConditionResult {
3605        ctx.external.evaluate("sender_role_check")
3606    }
3607
3608    /// [100] Wenn SG10 CAV+TLS/ TES/ BIT/ GET/ GAT/ SOT/ WNT/ WFT/ WAT vorhanden
3609    fn evaluate_100(&self, ctx: &EvaluationContext) -> ConditionResult {
3610        let cav_segs = ctx.find_segments("CAV");
3611        let found = cav_segs.iter().any(|s| {
3612            s.elements.first()
3613                .and_then(|e| e.first())
3614                .is_some_and(|v| ["TLS", "TES", "BIT", "GET", "GAT", "SOT", "WNT", "WFT", "WAT"].contains(&v.as_str()))
3615        });
3616        ConditionResult::from(found)
3617
3618    }
3619
3620    /// [101] Wenn SG10 CCI+++ZA6 (Prognosegrundlage der Marktlokation: Prognose auf Basis von Profilen) CAV+E14/ Z36 (TLP/ TEP/ TEP mit Ref.messung) in dieser SG8 vorhanden
3621    fn evaluate_101(&self, ctx: &EvaluationContext) -> ConditionResult {
3622        let cci_segs = ctx.find_segments("CCI");
3623        let has_code = cci_segs.iter().any(|s| {
3624            s.elements.get(2)
3625                .and_then(|e| e.first())
3626                .is_some_and(|v| v == "ZA6")
3627        });
3628        if !has_code {
3629            return ConditionResult::from(false);
3630        }
3631        let cav_segs = ctx.find_segments("CAV");
3632        let found = cav_segs.iter().any(|s| {
3633            s.elements.first()
3634                .and_then(|e| e.first())
3635                .is_some_and(|v| ["E14", "Z36"].contains(&v.as_str()))
3636        });
3637        ConditionResult::from(found)
3638
3639    }
3640
3641    /// [102] Wenn SG10 CCI+++ZA6 (Prognosegrundlage der Marktlokation: Prognose auf Basis von Profilen) CAV+Z36 (TEP mit Ref.messung) in dieser SG8 vorhanden
3642    fn evaluate_102(&self, ctx: &EvaluationContext) -> ConditionResult {
3643        let cci_segs = ctx.find_segments("CCI");
3644        let has_code = cci_segs.iter().any(|s| {
3645            s.elements.get(2)
3646                .and_then(|e| e.first())
3647                .is_some_and(|v| v == "ZA6")
3648        });
3649        if !has_code {
3650            return ConditionResult::from(false);
3651        }
3652        let cav_segs = ctx.find_segments("CAV");
3653        let found = cav_segs.iter().any(|s| {
3654            s.elements.first()
3655                .and_then(|e| e.first())
3656                .is_some_and(|v| ["Z36"].contains(&v.as_str()))
3657        });
3658        ConditionResult::from(found)
3659
3660    }
3661
3662    /// [103] Wenn SG9 QTY+265 (Arbeit / Leistung für tagesparameterabhängig e Marktlokation: Veranschlagte Jahresmenge Gesamt) in dieser SG8 vorhanden
3663    fn evaluate_103(&self, ctx: &EvaluationContext) -> ConditionResult {
3664        ctx.has_qualifier("QTY", 0, "265")
3665
3666    }
3667
3668    /// [104] Wenn SG9 QTY+Z08 (Arbeit / Leistung für tagesparameterabhängige Marktlokation: angepasste elektrische Arbeit nach Anhang D) in dieser SG8 vorhanden
3669    fn evaluate_104(&self, ctx: &EvaluationContext) -> ConditionResult {
3670        ctx.has_qualifier("QTY", 0, "Z08")
3671
3672    }
3673
3674    /// [105] Wenn SG9 QTY+Z10 (Arbeit / Leistung für tagesparameterabhängige Marktlokation: Leistung der Marktlokation) in dieser SG8 vorhanden
3675    fn evaluate_105(&self, ctx: &EvaluationContext) -> ConditionResult {
3676        ctx.has_qualifier("QTY", 0, "Z10")
3677
3678    }
3679
3680    /// [106] Wenn in dieser SG8 SEQ+Z01 SG10 CCI+++ZA6 (Prognosegrundlage der Marktlokation: Prognose auf Basis von Profilen) CAV+E02 (SLP/SEP) vorhanden
3681    fn evaluate_106(&self, ctx: &EvaluationContext) -> ConditionResult {
3682        let cci_segs = ctx.find_segments("CCI");
3683        let has_code = cci_segs.iter().any(|s| {
3684            s.elements.get(2)
3685                .and_then(|e| e.first())
3686                .is_some_and(|v| v == "ZA6")
3687        });
3688        if !has_code {
3689            return ConditionResult::from(false);
3690        }
3691        let cav_segs = ctx.find_segments("CAV");
3692        let found = cav_segs.iter().any(|s| {
3693            s.elements.first()
3694                .and_then(|e| e.first())
3695                .is_some_and(|v| ["E02"].contains(&v.as_str()))
3696        });
3697        ConditionResult::from(found)
3698
3699    }
3700
3701    /// [107] Wenn in derselben SG8 das SG10 CAV+SLS/ SES/ BIP/ GEP/ GAP/ SOP/ WNP/ WFP/ WAP vorhanden
3702    fn evaluate_107(&self, ctx: &EvaluationContext) -> ConditionResult {
3703        let cav_segs = ctx.find_segments("CAV");
3704        let found = cav_segs.iter().any(|s| {
3705            s.elements.first()
3706                .and_then(|e| e.first())
3707                .is_some_and(|v| ["SLS", "SES", "BIP", "GEP", "GAP", "SOP", "WNP", "WFP", "WAP"].contains(&v.as_str()))
3708        });
3709        ConditionResult::from(found)
3710
3711    }
3712
3713    /// [108] Wenn in derselben SG8 (Netznutzungsabrechnungsdaten der Marktlokation) eine Gruppen-/Artikel-ID im PIA+Z02 (Gruppenartikel-ID / Artikel-ID), welche mit 1-08-1/2/3/4/5 beginnt, vorhanden
3714    fn evaluate_108(&self, ctx: &EvaluationContext) -> ConditionResult {
3715        ctx.has_qualifier("PIA", 0, "Z02")
3716
3717    }
3718
3719    /// [110] Wenn SG10 CAV+LGS/ EGS/ BIL/ GEL/ GAL/ SOL/ WNL/ WFL / WAL vorhanden
3720    fn evaluate_110(&self, ctx: &EvaluationContext) -> ConditionResult {
3721        let cav_segs = ctx.find_segments("CAV");
3722        let found = cav_segs.iter().any(|s| {
3723            s.elements.first()
3724                .and_then(|e| e.first())
3725                .is_some_and(|v| ["LGS", "EGS", "BIL", "GEL", "GAL", "SOL", "WNL", "WFL", "WAL"].contains(&v.as_str()))
3726        });
3727        ConditionResult::from(found)
3728
3729    }
3730
3731    /// [111] Wenn in derselben SG8 SEQ+Z59 (Produkt-Daten der Marktlokation) das PIA+5 (Produkt-Daten der Marktlokation) nicht vorhanden
3732    fn evaluate_111(&self, ctx: &EvaluationContext) -> ConditionResult {
3733        ctx.any_group_has_qualifier_without("SEQ", 0, "Z59", "PIA", 0, "5", &["SG4", "SG8"])
3734    }
3735
3736    /// [112] Wenn in derselben SG8 SEQ+Z59 (Produkt-Daten der Marktlokation) das SG10 CCI+11 (Details zum Produkt der Marktlokation) nicht vorhanden
3737    fn evaluate_112(&self, ctx: &EvaluationContext) -> ConditionResult {
3738        let result = ctx.filtered_parent_child_has_qualifier(
3739            &["SG4", "SG8"],
3740            "SEQ",
3741            0,
3742            "Z59",
3743            "SG10",
3744            "CCI",
3745            0,
3746            "11",
3747        );
3748        match result {
3749            ConditionResult::True => ConditionResult::False,
3750            ConditionResult::False => ConditionResult::True,
3751            ConditionResult::Unknown => ConditionResult::Unknown,
3752        }
3753    }
3754
3755    /// [113] Wenn es sich um eine Antwort auf die Bestellung einer Zählzeit handelt
3756    fn evaluate_113(&self, ctx: &EvaluationContext) -> ConditionResult {
3757        // Antwort auf Bestellung einer Zählzeit — depends on PID context
3758        ctx.external.evaluate("counting_time_order_response")
3759    }
3760
3761    /// [114] Wenn das SG10 CCI+E03 (Spannungsebene der Marktlokation) CAV E06 (Niederspannung) in dieser SG vorhanden
3762    // REVIEW: Checks that CCI+E03 (Spannungsebene, elements[0][0]='E03' per AHB hint) and CAV+E06 (Niederspannung, elements[0][0]='E06') co-occur in the same SG10 child instance of some SG8. Uses navigator to iterate SG8→SG10 pairs and verify both segments within the same SG10 instance. Confidence medium because navigator availability is runtime-dependent and fallback is message-wide only. (medium confidence)
3763    fn evaluate_114(&self, ctx: &EvaluationContext) -> ConditionResult {
3764        let nav = match ctx.navigator {
3765            Some(n) => n,
3766            None => {
3767                return match (
3768                    ctx.has_qualifier("CCI", 0, "E03"),
3769                    ctx.has_qualifier("CAV", 0, "E06"),
3770                ) {
3771                    (ConditionResult::True, ConditionResult::True) => ConditionResult::True,
3772                    (ConditionResult::False, _) | (_, ConditionResult::False) => {
3773                        ConditionResult::False
3774                    }
3775                    _ => ConditionResult::Unknown,
3776                };
3777            }
3778        };
3779        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
3780        for i in 0..sg8_count {
3781            let sg10_count = nav.child_group_instance_count(&["SG4", "SG8"], i, "SG10");
3782            for j in 0..sg10_count {
3783                let ccis = nav.find_segments_in_child_group("CCI", &["SG4", "SG8"], i, "SG10", j);
3784                let has_cci_e03 = ccis.iter().any(|s| {
3785                    s.elements
3786                        .first()
3787                        .and_then(|e: &Vec<String>| e.first())
3788                        .is_some_and(|v: &String| v == "E03")
3789                });
3790                if has_cci_e03 {
3791                    let cavs =
3792                        nav.find_segments_in_child_group("CAV", &["SG4", "SG8"], i, "SG10", j);
3793                    if cavs.iter().any(|s| {
3794                        s.elements
3795                            .first()
3796                            .and_then(|e: &Vec<String>| e.first())
3797                            .is_some_and(|v: &String| v == "E06")
3798                    }) {
3799                        return ConditionResult::True;
3800                    }
3801                }
3802            }
3803        }
3804        ConditionResult::False
3805    }
3806
3807    /// [115] Wenn das SG10 CCI+E03 (Spannungsebene der Marktlokation) CAV E05 (Mittelspannung) in dieser SG vorhanden
3808    fn evaluate_115(&self, ctx: &EvaluationContext) -> ConditionResult {
3809        ctx.has_qualifier("CCI", 0, "E03")
3810
3811    }
3812
3813    /// [116] Wenn das SG10 CCI+E03 (Spannungsebene der Marktlokation) CAV E04 (Hochspannung) in dieser SG vorhanden
3814    fn evaluate_116(&self, ctx: &EvaluationContext) -> ConditionResult {
3815        ctx.has_qualifier("CCI", 0, "E03")
3816
3817    }
3818
3819    /// [117] Wenn das RFF+Z18 (Marktlokation) aus dieser SG8 auf das gleiche SG5 LOC+Z16 (Marktlokation), wie ein RFF+Z18 (Marktlokation) aus einer SG8 SEQ+Z01 (Daten der Marktlokation), in dem SG10 CCI+++ZA6 (...
3820    // REVIEW: Full condition requires: (1) current SG8 has RFF+Z18=X, (2) another SG8 with SEQ+Z01 also has RFF+Z18=X, (3) that SG8 has SG10 with CCI elements[2][0]='ZA6' AND CAV elements[0][0]='E14'. Implementation simplifies to checking whether any SG8 has SG10 with both CCI+++ZA6 and CAV+E14 co-occurring in the same SG10 instance. The cross-SG RFF+Z18 value correlation and SEQ+Z01 parent check are omitted because the navigator API only exposes child-group segments (SG10 within SG8), not SG8's own entry segments. Medium confidence due to this structural limitation. (medium confidence)
3821    fn evaluate_117(&self, ctx: &EvaluationContext) -> ConditionResult {
3822        let nav = match ctx.navigator {
3823            Some(n) => n,
3824            None => return ConditionResult::Unknown,
3825        };
3826        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
3827        for i in 0..sg8_count {
3828            let sg10_count = nav.child_group_instance_count(&["SG4", "SG8"], i, "SG10");
3829            for j in 0..sg10_count {
3830                let ccis = nav.find_segments_in_child_group("CCI", &["SG4", "SG8"], i, "SG10", j);
3831                let has_cci_za6 = ccis.iter().any(|s| {
3832                    s.elements
3833                        .get(2)
3834                        .and_then(|e: &Vec<String>| e.first())
3835                        .is_some_and(|v: &String| v == "ZA6")
3836                });
3837                if has_cci_za6 {
3838                    let cavs =
3839                        nav.find_segments_in_child_group("CAV", &["SG4", "SG8"], i, "SG10", j);
3840                    if cavs.iter().any(|s| {
3841                        s.elements
3842                            .first()
3843                            .and_then(|e: &Vec<String>| e.first())
3844                            .is_some_and(|v: &String| v == "E14")
3845                    }) {
3846                        return ConditionResult::True;
3847                    }
3848                }
3849            }
3850        }
3851        ConditionResult::False
3852    }
3853
3854    /// [118] Wenn in dieser SG8 SEQ+Z98 SG10 CCI+++ZA6 (Prognosegrundlage der Marktlokation: Prognose auf Basis von Profilen) CAV+E02 (SLP/ SEP) vorhanden
3855    fn evaluate_118(&self, ctx: &EvaluationContext) -> ConditionResult {
3856        let has_cci = ctx.filtered_parent_child_has_qualifier(
3857            &["SG4", "SG8"],
3858            "SEQ",
3859            0,
3860            "Z98",
3861            "SG10",
3862            "CCI",
3863            2,
3864            "ZA6",
3865        );
3866        if has_cci != ConditionResult::True {
3867            return has_cci;
3868        }
3869        ctx.filtered_parent_child_has_qualifier(
3870            &["SG4", "SG8"],
3871            "SEQ",
3872            0,
3873            "Z98",
3874            "SG10",
3875            "CAV",
3876            0,
3877            "E02",
3878        )
3879    }
3880
3881    /// [119] Wenn in der SG8 SEQ+Z01/ Z98 SG10 CCI+++ZA6 (Prognosegrundlage der Marktlokation: Prognose auf Basis von Profilen) CAV+E02 (SLP/SEP) vorhanden
3882    fn evaluate_119(&self, ctx: &EvaluationContext) -> ConditionResult {
3883        let cci_segs = ctx.find_segments("CCI");
3884        let has_code = cci_segs.iter().any(|s| {
3885            s.elements.get(2)
3886                .and_then(|e| e.first())
3887                .is_some_and(|v| v == "ZA6")
3888        });
3889        if !has_code {
3890            return ConditionResult::from(false);
3891        }
3892        let cav_segs = ctx.find_segments("CAV");
3893        let found = cav_segs.iter().any(|s| {
3894            s.elements.first()
3895                .and_then(|e| e.first())
3896                .is_some_and(|v| ["E02"].contains(&v.as_str()))
3897        });
3898        ConditionResult::from(found)
3899
3900    }
3901
3902    /// [120] Wenn von NB Abtretungserklärung benötigt wird
3903    fn evaluate_120(&self, _ctx: &EvaluationContext) -> ConditionResult {
3904        // TODO: implement
3905        ConditionResult::Unknown
3906    }
3907
3908    /// [121] Wenn in dem SEQ+Z03/ ZF5 (Zähleinrichtungsdaten) das SG8 RFF+Z14 (Referenz auf das Smartmeter-Gateway) nicht vorhanden
3909    fn evaluate_121(&self, ctx: &EvaluationContext) -> ConditionResult {
3910        let z03 =
3911            ctx.any_group_has_qualifier_without("SEQ", 0, "Z03", "RFF", 0, "Z14", &["SG4", "SG8"]);
3912        if z03 == ConditionResult::True {
3913            return ConditionResult::True;
3914        }
3915        ctx.any_group_has_qualifier_without("SEQ", 0, "ZF5", "RFF", 0, "Z14", &["SG4", "SG8"])
3916    }
3917
3918    /// [122] Wenn in dieser SG8 SEQ+Z98 SG10 CCI+++E03 (Spannungsebene der Marktlokation) CAV+E06 (Niederspannung) vorhanden
3919    // REVIEW: Uses filtered_parent_child_has_qualifier to find SG8 instances where SEQ+Z98 has an SG10 child with CCI at elements[2]='E03' (Spannungsebene der Marktlokation). Then confirms CAV+E06 (Niederspannung) exists in an SG8 group. The CAV check is group-scoped rather than strictly same-SG10-instance because the navigator API does not directly expose parent-group (SG8) segment access alongside child-group (SG10) checks in one call. (medium confidence)
3920    fn evaluate_122(&self, ctx: &EvaluationContext) -> ConditionResult {
3921        // Check: in SG8 with SEQ+Z98, SG10 child has CCI+++E03 (Spannungsebene, elements[2][0]="E03")
3922        let has_cci_e03 = ctx.filtered_parent_child_has_qualifier(
3923            &["SG4", "SG8"],
3924            "SEQ",
3925            0,
3926            "Z98",
3927            "SG10",
3928            "CCI",
3929            2,
3930            "E03",
3931        );
3932        if has_cci_e03 != ConditionResult::True {
3933            return has_cci_e03;
3934        }
3935        // Also verify CAV+E06 (Niederspannung, elements[0][0]="E06") exists in the same SG8 scope
3936        ctx.any_group_has_qualifier("CAV", 0, "E06", &["SG4", "SG8"])
3937    }
3938
3939    /// [123] Wenn noch mindestens eine weitere SG8 SEQ+Z20 (OBIS-Daten der Zähleinrichtung Smartmeter-Gateway) mit dem SG8 RFF+MG (Gerätenummer des Zählers) auf die gleiche Identifikation/Nummer des Gerätes...
3940    // REVIEW: Scans segments linearly to collect RFF+MG device IDs from each SG8 group with SEQ+Z20 (OBIS-Daten der Zähleinrichtung/Smartmeter-Gateway). A new SEQ starts a new SG8 context; RFF+MG within that context records the device number. Returns True if any device ID appears in 2+ different SG8 groups (indicating multiple OBIS datasets reference the same device). Linear scan is used because the navigator API does not expose a direct method to retrieve segments within a parent SG8 instance by absolute index. (medium confidence)
3941    fn evaluate_123(&self, ctx: &EvaluationContext) -> ConditionResult {
3942        struct Sg8Z20Entry {
3943            mg_id: Option<String>,
3944        }
3945        let mut entries: Vec<Sg8Z20Entry> = Vec::new();
3946        let mut current: Option<Sg8Z20Entry> = None;
3947        for seg in ctx.segments {
3948            match seg.id.as_str() {
3949                "SEQ" => {
3950                    if let Some(entry) = current.take() {
3951                        entries.push(entry);
3952                    }
3953                    if seg
3954                        .elements
3955                        .first()
3956                        .and_then(|e: &Vec<String>| e.first())
3957                        .is_some_and(|v: &String| v == "Z20")
3958                    {
3959                        current = Some(Sg8Z20Entry { mg_id: None });
3960                    }
3961                }
3962                "RFF" if current.is_some() => {
3963                    if seg
3964                        .elements
3965                        .first()
3966                        .and_then(|e: &Vec<String>| e.first())
3967                        .is_some_and(|v: &String| v == "MG")
3968                    {
3969                        if let Some(ref mut entry) = current {
3970                            if entry.mg_id.is_none() {
3971                                entry.mg_id = seg.elements.first().and_then(|e| e.get(1)).cloned();
3972                            }
3973                        }
3974                    }
3975                }
3976                _ => {}
3977            }
3978        }
3979        if let Some(entry) = current {
3980            entries.push(entry);
3981        }
3982        let ids: Vec<&str> = entries
3983            .iter()
3984            .filter_map(|e| e.mg_id.as_deref().filter(|s| !s.is_empty()))
3985            .collect();
3986        for i in 0..ids.len() {
3987            for j in (i + 1)..ids.len() {
3988                if ids[i] == ids[j] {
3989                    return ConditionResult::True;
3990                }
3991            }
3992        }
3993        ConditionResult::False
3994    }
3995
3996    /// [124] Wenn noch mindestens eine weitere SG8 SEQ+Z20 (OBIS-Daten der Zähleinrichtung Smartmeter-Gateway) mit derselben Zeitraum-ID aus dem DE1050 dieser SG8 mit dem SG8 RFF+MG (Gerätenummer des Zählers...
3997    // REVIEW: Extends condition 123 by also requiring that the matching SG8 groups share the same Zeitraum-ID (from SEQ.elements[1][0] = DE1050 / C286.d1050). Both the Zeitraum-ID and the RFF+MG device number must be identical across at least two different SG8 instances with SEQ+Z20 for the condition to be True. (medium confidence)
3998    fn evaluate_124(&self, ctx: &EvaluationContext) -> ConditionResult {
3999        struct Sg8Z20Entry {
4000            zeitraum_id: String,
4001            mg_id: Option<String>,
4002        }
4003        let mut entries: Vec<Sg8Z20Entry> = Vec::new();
4004        let mut current: Option<Sg8Z20Entry> = None;
4005        for seg in ctx.segments {
4006            match seg.id.as_str() {
4007                "SEQ" => {
4008                    if let Some(entry) = current.take() {
4009                        entries.push(entry);
4010                    }
4011                    if seg
4012                        .elements
4013                        .first()
4014                        .and_then(|e: &Vec<String>| e.first())
4015                        .is_some_and(|v: &String| v == "Z20")
4016                    {
4017                        let zeitraum_id = seg
4018                            .elements
4019                            .get(1)
4020                            .and_then(|e: &Vec<String>| e.first())
4021                            .cloned()
4022                            .unwrap_or_default();
4023                        current = Some(Sg8Z20Entry {
4024                            zeitraum_id,
4025                            mg_id: None,
4026                        });
4027                    }
4028                }
4029                "RFF" if current.is_some() => {
4030                    if seg
4031                        .elements
4032                        .first()
4033                        .and_then(|e: &Vec<String>| e.first())
4034                        .is_some_and(|v: &String| v == "MG")
4035                    {
4036                        if let Some(ref mut entry) = current {
4037                            if entry.mg_id.is_none() {
4038                                entry.mg_id = seg.elements.first().and_then(|e| e.get(1)).cloned();
4039                            }
4040                        }
4041                    }
4042                }
4043                _ => {}
4044            }
4045        }
4046        if let Some(entry) = current {
4047            entries.push(entry);
4048        }
4049        for i in 0..entries.len() {
4050            for j in (i + 1)..entries.len() {
4051                let ei = &entries[i];
4052                let ej = &entries[j];
4053                if !ei.zeitraum_id.is_empty()
4054                    && ei.zeitraum_id == ej.zeitraum_id
4055                    && ei.mg_id.is_some()
4056                    && !ei.mg_id.as_deref().unwrap_or("").is_empty()
4057                    && ei.mg_id == ej.mg_id
4058                {
4059                    return ConditionResult::True;
4060                }
4061            }
4062        }
4063        ConditionResult::False
4064    }
4065
4066    /// [125] Wenn in derselben SG8 SEQ+Z76/ ZC5/ ZC6 (Messstellenbetriebsabrechnungsdaten der Marktlokation) im DE4347 des PIA Z02 (Gruppenartikel-ID / Artikel-ID) vorhanden
4067    fn evaluate_125(&self, ctx: &EvaluationContext) -> ConditionResult {
4068        // Check: PIA+Z02 present in SG8 SEQ+Z76/ZC5/ZC6
4069        let pia_segs = ctx.find_segments("PIA");
4070        let found = pia_segs.iter().any(|s| {
4071            s.elements.first().and_then(|e| e.first()).is_some_and(|v| v == "Z02")
4072                && s.elements.get(1).and_then(|e| e.get(1)).is_some_and(|v| !v.is_empty())
4073        });
4074        ConditionResult::from(found)
4075    }
4076
4077    /// [126] Es ist der Wert einzutragen, der sich aus der Wiederholungshäufigkeit des SG6 RFF+Z49/ Z53 (Verwendungszeitraum der Daten: Gültige Daten/ Keine Daten) ergibt. Bedeutet: Das erste SG6 RFF+Z49/ Z53...
4078    fn evaluate_126(&self, ctx: &EvaluationContext) -> ConditionResult {
4079        // Check: RFF+Z49 or Z53 repetition count determines value
4080        // Approximate: check if RFF+Z49 or Z53 exists (counter is structural)
4081        let count = ctx.find_segments_with_qualifier("RFF", 0, "Z49").len()
4082            + ctx.find_segments_with_qualifier("RFF", 0, "Z53").len();
4083        ConditionResult::from(count > 0)
4084    }
4085
4086    /// [127] Es ist der Wert einzutragen, der sich aus der Wiederholungshäufigkeit des SG6 RFF+Z48/ Z55 (Verwendungszeitraum der Daten: Erwartete Daten/ Keine Daten erwartet) ergibt. Bedeutet: Das erste SG6  R...
4087    fn evaluate_127(&self, ctx: &EvaluationContext) -> ConditionResult {
4088        // Check: RFF+Z48 or Z55 repetition count determines value
4089        let count = ctx.find_segments_with_qualifier("RFF", 0, "Z48").len()
4090            + ctx.find_segments_with_qualifier("RFF", 0, "Z55").len();
4091        ConditionResult::from(count > 0)
4092    }
4093
4094    /// [128] Es ist der Wert einzutragen, der sich aus der Wiederholungshäufigkeit des SG6 RFF+Z47/ Z54 (Verwendungszeitraum der Daten: Im System vorhandene Daten/ Im System keine Daten vorhanden) ergibt. Bede...
4095    fn evaluate_128(&self, ctx: &EvaluationContext) -> ConditionResult {
4096        // Check: RFF+Z47 or Z54 repetition count determines value
4097        let count = ctx.find_segments_with_qualifier("RFF", 0, "Z47").len()
4098            + ctx.find_segments_with_qualifier("RFF", 0, "Z54").len();
4099        ConditionResult::from(count > 0)
4100    }
4101
4102    /// [129] Innerhalb eines SG4 IDE müssen alle DE3225 der SG5 LOC+Z16 (Marktlokation) den identischen Wert enthalten
4103    fn evaluate_129(&self, ctx: &EvaluationContext) -> ConditionResult {
4104        // Check: all LOC+Z16 DE3225 values are identical
4105        let loc_segs = ctx.find_segments_with_qualifier("LOC", 0, "Z16");
4106        if loc_segs.len() <= 1 {
4107            return ConditionResult::True;
4108        }
4109        let first_val = loc_segs[0].elements.get(1).and_then(|e| e.first());
4110        let all_same = loc_segs.iter().all(|s| {
4111            s.elements.get(1).and_then(|e| e.first()) == first_val
4112        });
4113        ConditionResult::from(all_same)
4114    }
4115
4116    /// [130] Wenn an Messlokation vorhanden
4117    fn evaluate_130(&self, _ctx: &EvaluationContext) -> ConditionResult {
4118        // TODO: implement
4119        ConditionResult::Unknown
4120    }
4121
4122    /// [131] Wenn dieses DTM+Z25 (Verwendung der Daten ab) im SG6 RFF (Verwendungszeitraum der Daten) mit der Zeitraum ID \"1\" im DE1156 gekennzeichnet ist, muss das Datum in diesem DE2380 der direkt auf DTM+1...
4123    fn evaluate_131(&self, ctx: &EvaluationContext) -> ConditionResult {
4124        // Check: DTM+Z25 is in SG6 RFF with Zeitraum-ID "1" in DE1156
4125        let has_zeitraum_id_1 = ctx.find_segments("RFF").iter().any(|s| {
4126            s.elements.first().and_then(|e| e.get(2)).is_some_and(|v| v == "1")
4127        });
4128        ConditionResult::from(has_zeitraum_id_1)
4129    }
4130
4131    /// [132] Wenn dieses DTM+Z25 (Verwendung der Daten ab) im SG6 RFF (Verwendungszeitraum der Daten) mit der Zeitraum ID "1" im DE1156 gekennzeichnet ist, muss das Datum in diesem DE2380 der direkt auf DTM+137...
4132    // REVIEW: Validates that when RFF in SG6 has Zeitraum-ID '1' (elements[0][2] = DE1156), the DTM+Z25 date must equal the day immediately following DTM+137 (Nachrichtendatum) at 00:00. Parses the YYYYMMDD portion of DTM+137, computes next calendar day with correct month/year rollover, then checks that DTM+Z25's first 12 characters match YYYYMMDDHHMM with HHMM=0000. Note: German timezone offset (CET/CEST) validation is not performed — this checks the stored datetime value directly, which is the common encoding in EDIFACT 303 format for this use case. (medium confidence)
4133    fn evaluate_132(&self, ctx: &EvaluationContext) -> ConditionResult {
4134        // Condition applies only when RFF in SG6 has Zeitraum-ID "1" in DE1156 (elements[0][2])
4135        let has_zeitraum_id_1 = ctx.find_segments("RFF").iter().any(|s| {
4136            s.elements
4137                .first()
4138                .and_then(|e| e.get(2))
4139                .is_some_and(|v: &String| v == "1")
4140        });
4141        if !has_zeitraum_id_1 {
4142            return ConditionResult::False;
4143        }
4144        // Get message date from DTM+137 (format 303: CCYYMMDDHHmm...)
4145        let msg_date_str = match ctx
4146            .find_segments_with_qualifier("DTM", 0, "137")
4147            .into_iter()
4148            .next()
4149        {
4150            Some(seg) => match seg.elements.first().and_then(|e| e.get(1)) {
4151                Some(v) if v.len() >= 8 => v.clone(),
4152                _ => return ConditionResult::Unknown,
4153            },
4154            None => return ConditionResult::Unknown,
4155        };
4156        // Get DTM+Z25 (Verwendung der Daten ab) value
4157        let usage_date_str = match ctx
4158            .find_segments_with_qualifier("DTM", 0, "Z25")
4159            .into_iter()
4160            .next()
4161        {
4162            Some(seg) => match seg.elements.first().and_then(|e| e.get(1)) {
4163                Some(v) if v.len() >= 12 => v.clone(),
4164                _ => return ConditionResult::Unknown,
4165            },
4166            None => return ConditionResult::Unknown,
4167        };
4168        // Parse YYYYMMDD from DTM+137 value
4169        let year: u32 = match msg_date_str[..4].parse() {
4170            Ok(v) => v,
4171            Err(_) => return ConditionResult::Unknown,
4172        };
4173        let month: u32 = match msg_date_str[4..6].parse() {
4174            Ok(v) => v,
4175            Err(_) => return ConditionResult::Unknown,
4176        };
4177        let day: u32 = match msg_date_str[6..8].parse() {
4178            Ok(v) => v,
4179            Err(_) => return ConditionResult::Unknown,
4180        };
4181        let days_in_month: u32 = match month {
4182            1 | 3 | 5 | 7 | 8 | 10 | 12 => 31,
4183            4 | 6 | 9 | 11 => 30,
4184            2 => {
4185                if year % 4 == 0 && (year % 100 != 0 || year % 400 == 0) {
4186                    29
4187                } else {
4188                    28
4189                }
4190            }
4191            _ => return ConditionResult::Unknown,
4192        };
4193        let (next_year, next_month, next_day): (u32, u32, u32) = if day >= days_in_month {
4194            if month == 12 {
4195                (year + 1, 1, 1)
4196            } else {
4197                (year, month + 1, 1)
4198            }
4199        } else {
4200            (year, month, day + 1)
4201        };
4202        // Expected DTM+Z25: next day at 00:00 — format 303 prefix YYYYMMDDHHMM = 12 chars
4203        let expected_prefix = format!("{:04}{:02}{:02}0000", next_year, next_month, next_day);
4204        let actual_len = usage_date_str.len().min(12);
4205        let actual_prefix = &usage_date_str[..actual_len];
4206        ConditionResult::from(actual_prefix == expected_prefix.as_str())
4207    }
4208
4209    /// [133] Wenn an der übermittelten Marktlokation / Messlokation vorhanden
4210    fn evaluate_133(&self, _ctx: &EvaluationContext) -> ConditionResult {
4211        // TODO: implement
4212        ConditionResult::Unknown
4213    }
4214
4215    /// [137] Nicht bei Neuanlage
4216    fn evaluate_137(&self, ctx: &EvaluationContext) -> ConditionResult {
4217        // Check: not a Neuanlage (new registration) process
4218        // STS+7 with transaktionsgrund containing Neuanlage indicators
4219        let sts_segs = ctx.find_segments_with_qualifier("STS", 0, "7");
4220        let is_neuanlage = sts_segs.iter().any(|s| {
4221            s.elements.get(2).and_then(|e| e.first()).is_some_and(|v| v == "E01" || v == "E03")
4222        });
4223        ConditionResult::from(!is_neuanlage)
4224    }
4225
4226    /// [138] Wenn im selben QTY im DE6063 Z35 (Abschlag) vorhanden
4227    fn evaluate_138(&self, ctx: &EvaluationContext) -> ConditionResult {
4228        let qty_segs = ctx.find_segments("QTY");
4229        let found = qty_segs.iter().any(|s| {
4230            s.elements.first()
4231                .and_then(|e| e.first())
4232                .is_some_and(|v| ["Z35"].contains(&v.as_str()))
4233        });
4234        ConditionResult::from(found)
4235
4236    }
4237
4238    /// [140] Wenn im selben QTY im DE6063 Z34/Z35 (Zuschlag/Abschlag) vorhanden
4239    fn evaluate_140(&self, ctx: &EvaluationContext) -> ConditionResult {
4240        let qty_segs = ctx.find_segments("QTY");
4241        let found = qty_segs.iter().any(|s| {
4242            s.elements.first()
4243                .and_then(|e| e.first())
4244                .is_some_and(|v| ["Z34", "Z35"].contains(&v.as_str()))
4245        });
4246        ConditionResult::from(found)
4247
4248    }
4249
4250    /// [141] Wenn SG10 CAV+MIW/ MPW/ MBW vorhanden
4251    fn evaluate_141(&self, ctx: &EvaluationContext) -> ConditionResult {
4252        let cav_segs = ctx.find_segments("CAV");
4253        let found = cav_segs.iter().any(|s| {
4254            s.elements.first()
4255                .and_then(|e| e.first())
4256                .is_some_and(|v| ["MIW", "MPW", "MBW"].contains(&v.as_str()))
4257        });
4258        ConditionResult::from(found)
4259
4260    }
4261
4262    /// [142] Wenn SG8 PIA+Z01+:Z04/ Z05 (Berechnung Tagesmitteltemperatur: vom Anbieter zur Verfügung gestellte - / Äquivalente Tagesmitteltemperatur) vorhanden
4263    fn evaluate_142(&self, ctx: &EvaluationContext) -> ConditionResult {
4264        // Check: PIA+Z01 with Z04 or Z05 qualifier exists
4265        let pia_segs = ctx.find_segments("PIA");
4266        let found = pia_segs.iter().any(|s| {
4267            s.elements.first().and_then(|e| e.first()).is_some_and(|v| v == "Z01")
4268                && s.elements.get(1).and_then(|e| e.get(1)).is_some_and(|v| v == "Z04" || v == "Z05")
4269        });
4270        ConditionResult::from(found)
4271    }
4272
4273    /// [143] Wenn STS+7++ZX3 (Transaktionsgrund: Abrechnungsdaten BK-Abrechnung verbrauchender MaLo) vorhanden
4274    fn evaluate_143(&self, ctx: &EvaluationContext) -> ConditionResult {
4275        ctx.has_qualified_value("STS", 0, "7", 2, 0, &["ZX3"])
4276
4277    }
4278
4279    /// [144] Wenn STS+7++ZAN (Transaktionsgrund: Korrektur Abrechnungsdaten BK-Abrechnung verbrauchender MaLo) vorhanden
4280    fn evaluate_144(&self, ctx: &EvaluationContext) -> ConditionResult {
4281        ctx.has_qualified_value("STS", 0, "7", 2, 0, &["ZAN"])
4282
4283    }
4284
4285    /// [145] Wenn STS+7++ZX2 (Transaktionsgrund: Abrechnungsdaten BK-Abrechnung erzeugender MaLo) vorhanden
4286    fn evaluate_145(&self, ctx: &EvaluationContext) -> ConditionResult {
4287        ctx.has_qualified_value("STS", 0, "7", 2, 0, &["ZX2"])
4288
4289    }
4290
4291    /// [146] Wenn STS+7++ZA0 (Transaktionsgrund: Korrektur Abrechnungsdaten BK-Abrechnung erzeugender MaLo) vorhanden
4292    fn evaluate_146(&self, ctx: &EvaluationContext) -> ConditionResult {
4293        ctx.has_qualified_value("STS", 0, "7", 2, 0, &["ZA0"])
4294
4295    }
4296
4297    /// [147] Wenn in Anfrage vorhanden
4298    fn evaluate_147(&self, _ctx: &EvaluationContext) -> ConditionResult {
4299        // TODO: implement
4300        ConditionResult::Unknown
4301    }
4302
4303    /// [148] Wenn in dieser SG8 im SG8 PIA+Z02 (Gruppenartikel-ID / Artikel-ID), die Artikel-ID 1-02-0-017/ 1-02-0-018 vorhanden
4304    fn evaluate_148(&self, ctx: &EvaluationContext) -> ConditionResult {
4305        ctx.has_qualifier("PIA", 0, "Z02")
4306
4307    }
4308
4309    /// [149] Wenn zwei SG8 SEQ+Z45 (Netznutzungsabrechnungsdaten der Marktlokation), mit  derselben Zeitraum-ID im DE1050, mit Artikel-ID im SG8 PIA+Z02 (Gruppenartikel-ID / Artikel-ID), eine SG8 mit 1-02-0-017...
4310    // REVIEW: Condition checks whether two SG8 SEQ+Z45 groups with the same Zeitraum-ID (elements[1][0]) each contain PIA+Z02 (DE4347=Z02) with product codes '1-02-0-017' and '1-02-0-018'. Uses collect_group_values to gather SEQ qualifiers and Zeitraum-IDs per group instance, then PIA qualifiers and product codes (elements[1][0]=DE7140) per group instance. Positional alignment of pia_quals and pia_prods (both from same PIA segments in same order) enables reliable qualifier-to-product matching. Groups product codes by Zeitraum-ID to check co-occurrence. (medium confidence)
4311    fn evaluate_149(&self, ctx: &EvaluationContext) -> ConditionResult {
4312        use std::collections::{HashMap, HashSet};
4313        let seq_quals = ctx.collect_group_values("SEQ", 0, 0, &["SG4", "SG8"]);
4314        let seq_zeitraum = ctx.collect_group_values("SEQ", 1, 0, &["SG4", "SG8"]);
4315        let pia_quals = ctx.collect_group_values("PIA", 0, 0, &["SG4", "SG8"]);
4316        let pia_prods = ctx.collect_group_values("PIA", 1, 0, &["SG4", "SG8"]);
4317        // Map group index -> Zeitraum-ID for SG8s with SEQ+Z45
4318        let mut idx_to_zeitraum: HashMap<usize, String> = HashMap::new();
4319        for ((gi, q), (_, zid)) in seq_quals.iter().zip(seq_zeitraum.iter()) {
4320            if q == "Z45" && !zid.is_empty() {
4321                idx_to_zeitraum.insert(*gi, zid.clone());
4322            }
4323        }
4324        if idx_to_zeitraum.is_empty() {
4325            return ConditionResult::False;
4326        }
4327        // Map Zeitraum-ID -> set of PIA+Z02 product codes found in the same SG8 group
4328        let mut zeitraum_products: HashMap<String, HashSet<String>> = HashMap::new();
4329        for (i, (gi, qual)) in pia_quals.iter().enumerate() {
4330            if qual == "Z02" {
4331                if let Some(zid) = idx_to_zeitraum.get(gi) {
4332                    if let Some((_, prod)) = pia_prods.get(i) {
4333                        if !prod.is_empty() {
4334                            zeitraum_products
4335                                .entry(zid.clone())
4336                                .or_default()
4337                                .insert(prod.clone());
4338                        }
4339                    }
4340                }
4341            }
4342        }
4343        ConditionResult::from(
4344            zeitraum_products
4345                .values()
4346                .any(|codes| codes.contains("1-02-0-017") && codes.contains("1-02-0-018")),
4347        )
4348    }
4349
4350    /// [150] Wenn in dieser SG8 im SG8 PIA+Z02 (Gruppenartikel-ID / Artikel-ID), die Artikel-ID 1-02-0-002 vorhanden
4351    fn evaluate_150(&self, ctx: &EvaluationContext) -> ConditionResult {
4352        ctx.has_qualifier("PIA", 0, "Z02")
4353
4354    }
4355
4356    /// [151] Wenn bei der Bestellung ein Messprodukt der Codeliste der Konfigurationen aus dem Kapitel 4.4 mit dem Ü̈bertragungsweg \"aus dem SMGW\" bestellt wurde. Der Empfänger der Werte kann dadurch seine...
4357    fn evaluate_151(&self, ctx: &EvaluationContext) -> ConditionResult {
4358        ctx.external.evaluate("code_list_membership_check")
4359    }
4360
4361    /// [152] Wenn FTX+Z28 (IP-Range des Absenders) nicht vorhanden
4362    fn evaluate_152(&self, ctx: &EvaluationContext) -> ConditionResult {
4363        ctx.lacks_qualifier("FTX", 0, "Z28")
4364
4365    }
4366
4367    /// [153] Wenn FTX+Z27 (IP-Adresse des Absenders) nicht vorhanden
4368    fn evaluate_153(&self, ctx: &EvaluationContext) -> ConditionResult {
4369        ctx.lacks_qualifier("FTX", 0, "Z27")
4370
4371    }
4372
4373    /// [156] Wenn SG4 FTX+Z17 (Zieladresse URI) vorhanden
4374    fn evaluate_156(&self, ctx: &EvaluationContext) -> ConditionResult {
4375        ctx.has_qualifier("FTX", 0, "Z17")
4376
4377    }
4378
4379    /// [161] Wenn SG4 STS+7++E03+ZW8 (Transaktionsgrundergänzung Fall 1) vorhanden
4380    fn evaluate_161(&self, ctx: &EvaluationContext) -> ConditionResult {
4381        let sts_segs = ctx.find_segments_with_qualifier("STS", 0, "7");
4382        let found = sts_segs.iter().any(|s| {
4383            s.elements.get(2).and_then(|e| e.first()).is_some_and(|v| v == "E03")
4384                && s.elements.get(3).and_then(|e| e.first()).is_some_and(|v| v == "ZW8")
4385        });
4386        ConditionResult::from(found)
4387
4388    }
4389
4390    /// [162] Wenn SG4 STS+7++E03+ZW9 (Transaktionsgrundergänzung Fall 2) vorhanden
4391    fn evaluate_162(&self, ctx: &EvaluationContext) -> ConditionResult {
4392        let sts_segs = ctx.find_segments_with_qualifier("STS", 0, "7");
4393        let found = sts_segs.iter().any(|s| {
4394            s.elements.get(2).and_then(|e| e.first()).is_some_and(|v| v == "E03")
4395                && s.elements.get(3).and_then(|e| e.first()).is_some_and(|v| v == "ZW9")
4396        });
4397        ConditionResult::from(found)
4398
4399    }
4400
4401    /// [163] Wenn SG4 STS+7++E03+ZX0 (Transaktionsgrundergänzung Fall 3) vorhanden
4402    fn evaluate_163(&self, ctx: &EvaluationContext) -> ConditionResult {
4403        let sts_segs = ctx.find_segments_with_qualifier("STS", 0, "7");
4404        let found = sts_segs.iter().any(|s| {
4405            s.elements.get(2).and_then(|e| e.first()).is_some_and(|v| v == "E03")
4406                && s.elements.get(3).and_then(|e| e.first()).is_some_and(|v| v == "ZX0")
4407        });
4408        ConditionResult::from(found)
4409
4410    }
4411
4412    /// [164] Wenn SG4 STS+7++E03+ZX1 (Transaktionsgrundergänzung Fall 4) vorhanden
4413    fn evaluate_164(&self, ctx: &EvaluationContext) -> ConditionResult {
4414        let sts_segs = ctx.find_segments_with_qualifier("STS", 0, "7");
4415        let found = sts_segs.iter().any(|s| {
4416            s.elements.get(2).and_then(|e| e.first()).is_some_and(|v| v == "E03")
4417                && s.elements.get(3).and_then(|e| e.first()).is_some_and(|v| v == "ZX1")
4418        });
4419        ConditionResult::from(found)
4420
4421    }
4422
4423    /// [165] Wenn bekannt
4424    fn evaluate_165(&self, _ctx: &EvaluationContext) -> ConditionResult {
4425        // TODO: implement
4426        ConditionResult::Unknown
4427    }
4428
4429    /// [166] Wenn vorhanden
4430    fn evaluate_166(&self, _ctx: &EvaluationContext) -> ConditionResult {
4431        ConditionResult::True
4432    }
4433
4434    /// [167] Wenn SG5 LOC+Z21 (Tranche) vorhanden
4435    fn evaluate_167(&self, ctx: &EvaluationContext) -> ConditionResult {
4436        ctx.has_qualifier("LOC", 0, "Z21")
4437
4438    }
4439
4440    /// [170] Wenn Anschrift der Marktlokation vorhanden
4441    fn evaluate_170(&self, _ctx: &EvaluationContext) -> ConditionResult {
4442        // TODO: implement
4443        ConditionResult::Unknown
4444    }
4445
4446    /// [172] Wenn im selben QTY im DE6063 Z37 (Kein Zu- und Abschlag) vorhanden
4447    fn evaluate_172(&self, ctx: &EvaluationContext) -> ConditionResult {
4448        let qty_segs = ctx.find_segments("QTY");
4449        let found = qty_segs.iter().any(|s| {
4450            s.elements.first()
4451                .and_then(|e| e.first())
4452                .is_some_and(|v| ["Z37"].contains(&v.as_str()))
4453        });
4454        ConditionResult::from(found)
4455
4456    }
4457
4458    /// [173] Wenn im RFF+Z33 (Referenz auf den Objektcode in der Lokationsbündelstruktur) das DE1156 (Fortlaufende Nummer eines Lokationsbündels im Geschäftsvorfall) in derselben SG8 SEQ+Z58/ ZC9/ ZD0/ ZD6 (...
4459    // REVIEW: Condition is True when, in an SG8 with SEQ+Z58/ZC9/ZD0/ZD6 (Zuordnung Lokation zum Objektcode), RFF+Z33 exists but its DE1156 (elements[0][2] = Fortlaufende Nummer) is absent or empty. Uses collect_group_values to find target SG8 group indices by SEQ qualifier, then positionally aligns rff_quals[i] with rff_de1156[i] (same RFF segment) to check DE1156 emptiness scoped to those groups. Returns Unknown if no RFF+Z33 found in target groups (condition not applicable), False if all RFF+Z33 have DE1156 present. (medium confidence)
4460    fn evaluate_173(&self, ctx: &EvaluationContext) -> ConditionResult {
4461        use std::collections::HashSet;
4462        let seq_quals = ctx.collect_group_values("SEQ", 0, 0, &["SG4", "SG8"]);
4463        let target_indices: HashSet<usize> = seq_quals
4464            .iter()
4465            .filter(|(_, q)| matches!(q.as_str(), "Z58" | "ZC9" | "ZD0" | "ZD6"))
4466            .map(|(i, _)| *i)
4467            .collect();
4468        if target_indices.is_empty() {
4469            return ConditionResult::Unknown;
4470        }
4471        // Collect RFF qualifier (elements[0][0]) and DE1156 (elements[0][2]) with positional alignment
4472        let rff_quals = ctx.collect_group_values("RFF", 0, 0, &["SG4", "SG8"]);
4473        let rff_de1156 = ctx.collect_group_values("RFF", 0, 2, &["SG4", "SG8"]);
4474        let mut found_rff_z33 = false;
4475        for (i, (gi, qual)) in rff_quals.iter().enumerate() {
4476            if qual == "Z33" && target_indices.contains(gi) {
4477                found_rff_z33 = true;
4478                let de1156_empty = rff_de1156.get(i).map(|(_, v)| v.is_empty()).unwrap_or(true);
4479                if de1156_empty {
4480                    return ConditionResult::True;
4481                }
4482            }
4483        }
4484        if found_rff_z33 {
4485            ConditionResult::False
4486        } else {
4487            ConditionResult::Unknown
4488        }
4489    }
4490
4491    /// [174] Wenn im RFF+Z33 (Referenz auf den Objektcode in der Lokationsbündelstruktur) das DE1156 Fortlaufende Nummer eines Lokationsbündels im Geschäftsvorfall) in derselben SG8 SEQ+Z58/ ZC9/ ZD0/ ZD6 (Z...
4492    fn evaluate_174(&self, ctx: &EvaluationContext) -> ConditionResult {
4493        // Check: RFF+Z33 has a value in DE1156 (fortlaufende Nummer)
4494        let rff_z33 = ctx.find_segments_with_qualifier("RFF", 0, "Z33");
4495        let found = rff_z33.iter().any(|s| {
4496            s.elements.first().and_then(|e| e.get(2)).is_some_and(|v| !v.is_empty())
4497        });
4498        ConditionResult::from(found)
4499    }
4500
4501    /// [175] Wenn der Objektcode im RFF+Z33 (Referenz auf den Objektcode in der Lokationsbündelstruktur) im DE1154 derselben SG8 SEQ+Z58 (Zuordnung Lokation zum Objektcode des Lokationsbündels) in einem weite...
4502    // REVIEW: Cross-SG8 check: find all SG8 instances with SEQ+Z58 that also have RFF+Z33. Check whether any Objektcode (DE1154) with the same fortlaufende Nummer (DE1156) appears in two distinct SG8 instances. Uses nav.find_segments_in_group to access SG8-level segments directly, following the existing navigator pattern. (medium confidence)
4503    fn evaluate_175(&self, ctx: &EvaluationContext) -> ConditionResult {
4504        let nav = match ctx.navigator {
4505            Some(n) => n,
4506            None => return ConditionResult::Unknown,
4507        };
4508        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
4509        let mut entries: Vec<(usize, String, String)> = Vec::new();
4510        for i in 0..sg8_count {
4511            let seqs = nav.find_segments_in_group("SEQ", &["SG4", "SG8"], i);
4512            if !seqs.iter().any(|s| {
4513                s.elements
4514                    .first()
4515                    .and_then(|e: &Vec<String>| e.first())
4516                    .is_some_and(|v: &String| v == "Z58")
4517            }) {
4518                continue;
4519            }
4520            let rffs = nav.find_segments_in_group("RFF", &["SG4", "SG8"], i);
4521            for rff in &rffs {
4522                let elems = match rff.elements.first() {
4523                    Some(e) => e,
4524                    None => continue,
4525                };
4526                if elems.first().map(|s| s.as_str()) != Some("Z33") {
4527                    continue;
4528                }
4529                let objektcode = elems.get(1).cloned().unwrap_or_default();
4530                let fort_nr = elems.get(2).cloned().unwrap_or_default();
4531                if !objektcode.is_empty() {
4532                    entries.push((i, objektcode, fort_nr));
4533                }
4534            }
4535        }
4536        for a in 0..entries.len() {
4537            for b in (a + 1)..entries.len() {
4538                if entries[a].0 != entries[b].0
4539                    && entries[a].1 == entries[b].1
4540                    && entries[a].2 == entries[b].2
4541                {
4542                    return ConditionResult::True;
4543                }
4544            }
4545        }
4546        ConditionResult::False
4547    }
4548
4549    /// [176] Wenn in demselben Segment das DE1153 mit dem Code Z34 vorgelagerte Messlokation vorhanden ist
4550    fn evaluate_176(&self, ctx: &EvaluationContext) -> ConditionResult {
4551        ctx.has_qualifier("RFF", 0, "Z34")
4552
4553    }
4554
4555    /// [177] Wenn in demselben Segment das DE1153 mit dem Code Z35 vorgelagerte Netzlokation vorhanden ist
4556    fn evaluate_177(&self, ctx: &EvaluationContext) -> ConditionResult {
4557        ctx.has_qualifier("RFF", 0, "Z35")
4558
4559    }
4560
4561    /// [178] Wenn das LOC+Z18 (Netzlokation) vorhanden ist
4562    fn evaluate_178(&self, ctx: &EvaluationContext) -> ConditionResult {
4563        ctx.has_qualifier("LOC", 0, "Z18")
4564
4565    }
4566
4567    /// [179] Wenn der Objektcode im RFF+Z33 (Referenz auf den Objektcode in der Lokationsbündelstruktur) im DE1154 derselben SG8 SEQ+Z58/ ZC9 / ZD0/ ZD6 (Zuordnung Lokation zum Objektcode des Lokationsbündels...
4568    fn evaluate_179(&self, ctx: &EvaluationContext) -> ConditionResult {
4569        // Check: RFF+Z33 has a value in DE1154 (Objektcode)
4570        let rff_z33 = ctx.find_segments_with_qualifier("RFF", 0, "Z33");
4571        let found = rff_z33.iter().any(|s| {
4572            s.elements.first().and_then(|e| e.get(1)).is_some_and(|v| !v.is_empty())
4573        });
4574        ConditionResult::from(found)
4575    }
4576
4577    /// [180] Wenn im selben QTY im DE6063 Z46 (Kein Abschlag) vorhanden
4578    fn evaluate_180(&self, ctx: &EvaluationContext) -> ConditionResult {
4579        let qty_segs = ctx.find_segments("QTY");
4580        let found = qty_segs.iter().any(|s| {
4581            s.elements.first()
4582                .and_then(|e| e.first())
4583                .is_some_and(|v| ["Z46"].contains(&v.as_str()))
4584        });
4585        ConditionResult::from(found)
4586
4587    }
4588
4589    /// [184] Wenn in derselben SG8 SEQ+Z18 (Daten der Messlokation) ein CCI+Z01++Z82 (Verwendungsumfang: ID der prozessual behandelten Messlokation) vorhanden
4590    fn evaluate_184(&self, ctx: &EvaluationContext) -> ConditionResult {
4591        let cci_segs = ctx.find_segments_with_qualifier("CCI", 0, "Z01");
4592        let found = cci_segs.iter().any(|s| {
4593            s.elements.get(2)
4594                .and_then(|e| e.first())
4595                .is_some_and(|v| ["Z82"].contains(&v.as_str()))
4596        });
4597        ConditionResult::from(found)
4598
4599    }
4600
4601    /// [190] Wenn in derselben SG8 SEQ+Z60/ ZG8/ ZG9/ ZE0  (Produkt-Daten der Netzlokation) PIA+5+9991000000721:Z12 vorhanden
4602    fn evaluate_190(&self, ctx: &EvaluationContext) -> ConditionResult {
4603        // Check: SG8 with SEQ+Z60/ZG8/ZG9/ZE0 has PIA+5 with code 9991000000721
4604        let pia_segs = ctx.find_segments("PIA");
4605        let found = pia_segs.iter().any(|s| {
4606            s.elements.first().and_then(|e| e.first()).is_some_and(|v| v == "5")
4607                && s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| v == "9991000000721")
4608        });
4609        ConditionResult::from(found)
4610    }
4611
4612    /// [191] Wenn im SG4 IDE+Z01 (Identifikation einer Liste) STS+E01 (Status der Antwort der Liste) nicht vorhanden
4613    fn evaluate_191(&self, ctx: &EvaluationContext) -> ConditionResult {
4614        let has_ide = !ctx.find_segments_with_qualifier("IDE", 0, "Z01").is_empty();
4615        if !has_ide {
4616            return ConditionResult::Unknown;
4617        }
4618        ctx.lacks_qualifier("STS", 0, "E01")
4619
4620    }
4621
4622    /// [192] Wenn SG4 STS+7++XXX+ZW4/ZAP (Transaktionsgrund / Ergänzung: Verbrauchende Marktlokation / ruhende Marktlokation) vorhanden
4623    fn evaluate_192(&self, ctx: &EvaluationContext) -> ConditionResult {
4624        let sts_segs = ctx.find_segments_with_qualifier("STS", 0, "7");
4625        let found = sts_segs.iter().any(|s| {
4626            s.elements.get(3)
4627                .and_then(|e| e.first())
4628                .is_some_and(|v| ["ZW4", "ZAP"].contains(&v.as_str()))
4629        });
4630        ConditionResult::from(found)
4631
4632    }
4633
4634    /// [193] Wenn das SG10 CCI ZB5 (Spannungsebene der Summenzeitreihe) CAV E06 (Niederspannung) in dieser SG vorhanden
4635    fn evaluate_193(&self, ctx: &EvaluationContext) -> ConditionResult {
4636        let nav = match ctx.navigator {
4637            Some(n) => n,
4638            None => {
4639                let cavs = ctx.find_segments("CAV");
4640                return ConditionResult::from(cavs.iter().any(|s| {
4641                    s.elements
4642                        .first()
4643                        .and_then(|e: &Vec<String>| e.first())
4644                        .is_some_and(|v: &String| v == "E06")
4645                }));
4646            }
4647        };
4648        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
4649        for i in 0..sg8_count {
4650            let sg10_count = nav.child_group_instance_count(&["SG4", "SG8"], i, "SG10");
4651            for j in 0..sg10_count {
4652                let ccis = nav.find_segments_in_child_group("CCI", &["SG4", "SG8"], i, "SG10", j);
4653                let has_cci_zb5 = ccis.iter().any(|s| {
4654                    s.elements
4655                        .get(2)
4656                        .and_then(|e: &Vec<String>| e.first())
4657                        .is_some_and(|v: &String| v == "ZB5")
4658                });
4659                if !has_cci_zb5 {
4660                    continue;
4661                }
4662                let cavs = nav.find_segments_in_child_group("CAV", &["SG4", "SG8"], i, "SG10", j);
4663                if cavs.iter().any(|s| {
4664                    s.elements
4665                        .first()
4666                        .and_then(|e: &Vec<String>| e.first())
4667                        .is_some_and(|v: &String| v == "E06")
4668                }) {
4669                    return ConditionResult::True;
4670                }
4671            }
4672        }
4673        ConditionResult::False
4674    }
4675
4676    /// [194] Wenn das SG10 CCI ZB5 (Spannungsebene der Summenzeitreihe) CAV E05 (Mittelspannung) in dieser SG vorhanden
4677    fn evaluate_194(&self, ctx: &EvaluationContext) -> ConditionResult {
4678        let cci_segs = ctx.find_segments("CCI");
4679        let has_cci = cci_segs.iter().any(|s| {
4680            s.elements.get(2)
4681                .and_then(|e| e.first())
4682                .is_some_and(|v| v == "ZB5")
4683        });
4684        if !has_cci {
4685            return ConditionResult::from(false);
4686        }
4687        let cav_segs = ctx.find_segments("CAV");
4688        let found = cav_segs.iter().any(|s| {
4689            s.elements.first()
4690                .and_then(|e| e.first())
4691                .is_some_and(|v| v == "E05")
4692        });
4693        ConditionResult::from(found)
4694
4695    }
4696
4697    /// [195] Wenn das SG10 CCI ZB5 (Spannungsebene der Summenzeitreihe) CAV E04 (Hochspannung) in dieser SG vorhanden
4698    fn evaluate_195(&self, ctx: &EvaluationContext) -> ConditionResult {
4699        let cci_segs = ctx.find_segments("CCI");
4700        let has_cci = cci_segs.iter().any(|s| {
4701            s.elements.get(2)
4702                .and_then(|e| e.first())
4703                .is_some_and(|v| v == "ZB5")
4704        });
4705        if !has_cci {
4706            return ConditionResult::from(false);
4707        }
4708        let cav_segs = ctx.find_segments("CAV");
4709        let found = cav_segs.iter().any(|s| {
4710            s.elements.first()
4711                .and_then(|e| e.first())
4712                .is_some_and(|v| v == "E04")
4713        });
4714        ConditionResult::from(found)
4715
4716    }
4717
4718    /// [196] Wenn der Objektcode im RFF+Z33 (Referenz auf den Objektcode in der Lokationsbündelstruktur) im DE1154 derselben SG8 SEQ+Z58/ZC9/ZD0 (Zuordnung Lokation zum Objektcode des Lokationsbündels) in ein...
4719    // REVIEW: Extended cross-SG8 check vs 175: now requires matching SEQ qualifier code (DE1229), Zeitraum-ID from SEQ DE1050 (elements[1][0]), Objektcode (RFF+Z33 DE1154), AND fortlaufende Nummer (RFF+Z33 DE1156) to all be identical between two distinct SG8 instances. SEQ qualifiers are Z58/ZC9/ZD0 per schema. (medium confidence)
4720    fn evaluate_196(&self, ctx: &EvaluationContext) -> ConditionResult {
4721        let nav = match ctx.navigator {
4722            Some(n) => n,
4723            None => return ConditionResult::Unknown,
4724        };
4725        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
4726        let mut entries: Vec<(usize, String, String, String, String)> = Vec::new();
4727        for i in 0..sg8_count {
4728            let seqs = nav.find_segments_in_group("SEQ", &["SG4", "SG8"], i);
4729            let seq_opt = seqs.iter().find(|s| {
4730                s.elements
4731                    .first()
4732                    .and_then(|e: &Vec<String>| e.first())
4733                    .is_some_and(|v: &String| matches!(v.as_str(), "Z58" | "ZC9" | "ZD0"))
4734            });
4735            let seq = match seq_opt {
4736                Some(s) => s,
4737                None => continue,
4738            };
4739            let seq_code = seq
4740                .elements
4741                .first()
4742                .and_then(|e: &Vec<String>| e.first())
4743                .cloned()
4744                .unwrap_or_default();
4745            let zeitraum_id = seq
4746                .elements
4747                .get(1)
4748                .and_then(|e: &Vec<String>| e.first())
4749                .cloned()
4750                .unwrap_or_default();
4751            let rffs = nav.find_segments_in_group("RFF", &["SG4", "SG8"], i);
4752            for rff in &rffs {
4753                let elems = match rff.elements.first() {
4754                    Some(e) => e,
4755                    None => continue,
4756                };
4757                if elems.first().map(|s| s.as_str()) != Some("Z33") {
4758                    continue;
4759                }
4760                let objektcode = elems.get(1).cloned().unwrap_or_default();
4761                let fort_nr = elems.get(2).cloned().unwrap_or_default();
4762                if !objektcode.is_empty() {
4763                    entries.push((
4764                        i,
4765                        seq_code.clone(),
4766                        zeitraum_id.clone(),
4767                        objektcode,
4768                        fort_nr,
4769                    ));
4770                }
4771            }
4772        }
4773        for a in 0..entries.len() {
4774            for b in (a + 1)..entries.len() {
4775                if entries[a].0 != entries[b].0
4776                    && entries[a].1 == entries[b].1
4777                    && entries[a].2 == entries[b].2
4778                    && entries[a].3 == entries[b].3
4779                    && entries[a].4 == entries[b].4
4780                {
4781                    return ConditionResult::True;
4782                }
4783            }
4784        }
4785        ConditionResult::False
4786    }
4787
4788    /// [197] Wenn das RFF+Z18 (Marktlokation) aus dieser SG8 auf das gleiche SG5 LOC+Z16 (Marktlokation), wie ein RFF+Z18 (Marktlokation) aus einer SG8 SEQ+Z01 (Daten der Marktlokation), in dem SG10 CCI+++ZA6 (...
4789    // REVIEW: Two-phase cross-SG8 check. Phase 1: collect Marktlokation IDs from SG8 instances that (a) have SEQ+Z01 and (b) have an SG10 child with CCI elements[2][0]=ZA6 and CAV elements[0][0]=E02. Phase 2: check whether any SG8's RFF+Z18 references one of those qualifying Marktlokation IDs, establishing the cross-reference described in the condition. (medium confidence)
4790    fn evaluate_197(&self, ctx: &EvaluationContext) -> ConditionResult {
4791        let nav = match ctx.navigator {
4792            Some(n) => n,
4793            None => return ConditionResult::Unknown,
4794        };
4795        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
4796        let mut qualifying_malo_ids: Vec<String> = Vec::new();
4797        for i in 0..sg8_count {
4798            let seqs = nav.find_segments_in_group("SEQ", &["SG4", "SG8"], i);
4799            if !seqs.iter().any(|s| {
4800                s.elements
4801                    .first()
4802                    .and_then(|e: &Vec<String>| e.first())
4803                    .is_some_and(|v: &String| v == "Z01")
4804            }) {
4805                continue;
4806            }
4807            let sg10_count = nav.child_group_instance_count(&["SG4", "SG8"], i, "SG10");
4808            let has_prog = (0..sg10_count).any(|j| {
4809                let ccis = nav.find_segments_in_child_group("CCI", &["SG4", "SG8"], i, "SG10", j);
4810                let has_za6 = ccis.iter().any(|s| {
4811                    s.elements
4812                        .get(2)
4813                        .and_then(|e: &Vec<String>| e.first())
4814                        .is_some_and(|v: &String| v == "ZA6")
4815                });
4816                if !has_za6 {
4817                    return false;
4818                }
4819                let cavs = nav.find_segments_in_child_group("CAV", &["SG4", "SG8"], i, "SG10", j);
4820                cavs.iter().any(|s| {
4821                    s.elements
4822                        .first()
4823                        .and_then(|e: &Vec<String>| e.first())
4824                        .is_some_and(|v: &String| v == "E02")
4825                })
4826            });
4827            if !has_prog {
4828                continue;
4829            }
4830            let rffs = nav.find_segments_in_group("RFF", &["SG4", "SG8"], i);
4831            for rff in &rffs {
4832                let elems = match rff.elements.first() {
4833                    Some(e) => e,
4834                    None => continue,
4835                };
4836                if elems.first().map(|s| s.as_str()) != Some("Z18") {
4837                    continue;
4838                }
4839                if let Some(malo_id) = elems.get(1) {
4840                    if !malo_id.is_empty() {
4841                        qualifying_malo_ids.push(malo_id.clone());
4842                    }
4843                }
4844            }
4845        }
4846        if qualifying_malo_ids.is_empty() {
4847            return ConditionResult::False;
4848        }
4849        for i in 0..sg8_count {
4850            let rffs = nav.find_segments_in_group("RFF", &["SG4", "SG8"], i);
4851            for rff in &rffs {
4852                let elems = match rff.elements.first() {
4853                    Some(e) => e,
4854                    None => continue,
4855                };
4856                if elems.first().map(|s| s.as_str()) != Some("Z18") {
4857                    continue;
4858                }
4859                if let Some(malo_id) = elems.get(1) {
4860                    if qualifying_malo_ids.contains(malo_id) {
4861                        return ConditionResult::True;
4862                    }
4863                }
4864            }
4865        }
4866        ConditionResult::False
4867    }
4868
4869    /// [198] Wenn SG4 STS+7++E03(Transaktionsgrund: Wechsel) vorhanden
4870    fn evaluate_198(&self, ctx: &EvaluationContext) -> ConditionResult {
4871        ctx.has_qualified_value("STS", 0, "7", 2, 0, &["E03"])
4872
4873    }
4874
4875    /// [199] Wenn in der SG8 SEQ+Z01/ SG10 CCI+++ZA6 (Prognosegrundlage der Marktlokation: Prognose auf Basis von Profilen) mit identischer Zeitraum-ID im DE1050 CAV+E02 (SLP/SEP) vorhanden
4876    // REVIEW: Checks whether any SG8 with SEQ+Z01 (Daten der Marktlokation) contains an SG10 child group where CCI elements[2][0]=ZA6 (Prognosegrundlage: Prognose auf Basis von Profilen) and CAV elements[0][0]=E02 (SLP/SEP) appear in the same SG10 instance. The 'identischer Zeitraum-ID' constraint from SEQ DE1050 is structurally satisfied by operating within the same SG8 instance; deeper Zeitraum-ID cross-referencing with other groups is omitted as it would require knowing the external referencing context. (medium confidence)
4877    fn evaluate_199(&self, ctx: &EvaluationContext) -> ConditionResult {
4878        let nav = match ctx.navigator {
4879            Some(n) => n,
4880            None => {
4881                return ctx.filtered_parent_child_has_qualifier(
4882                    &["SG4", "SG8"],
4883                    "SEQ",
4884                    0,
4885                    "Z01",
4886                    "SG10",
4887                    "CCI",
4888                    2,
4889                    "ZA6",
4890                )
4891            }
4892        };
4893        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
4894        for i in 0..sg8_count {
4895            let seqs = nav.find_segments_in_group("SEQ", &["SG4", "SG8"], i);
4896            if !seqs.iter().any(|s| {
4897                s.elements
4898                    .first()
4899                    .and_then(|e: &Vec<String>| e.first())
4900                    .is_some_and(|v: &String| v == "Z01")
4901            }) {
4902                continue;
4903            }
4904            let sg10_count = nav.child_group_instance_count(&["SG4", "SG8"], i, "SG10");
4905            for j in 0..sg10_count {
4906                let ccis = nav.find_segments_in_child_group("CCI", &["SG4", "SG8"], i, "SG10", j);
4907                if !ccis.iter().any(|s| {
4908                    s.elements
4909                        .get(2)
4910                        .and_then(|e: &Vec<String>| e.first())
4911                        .is_some_and(|v: &String| v == "ZA6")
4912                }) {
4913                    continue;
4914                }
4915                let cavs = nav.find_segments_in_child_group("CAV", &["SG4", "SG8"], i, "SG10", j);
4916                if cavs.iter().any(|s| {
4917                    s.elements
4918                        .first()
4919                        .and_then(|e: &Vec<String>| e.first())
4920                        .is_some_and(|v: &String| v == "E02")
4921                }) {
4922                    return ConditionResult::True;
4923                }
4924            }
4925        }
4926        ConditionResult::False
4927    }
4928
4929    /// [201] Wenn die Marktlokation / Tranche nicht den ganzen Bilanzierungsmonat dem gleichen Tupel aus Bilanzkreis, Zeitreihentyp, Spannungsebene und Lieferant zugeordnet ist
4930    fn evaluate_201(&self, _ctx: &EvaluationContext) -> ConditionResult {
4931        // TODO: implement
4932        ConditionResult::Unknown
4933    }
4934
4935    /// [202] Wenn SG8 SEQ+Z78 RFF+Z39 (Keine standardisierte Lokationsbündelstruktur vorhanden), mit identischer Zeitraum-ID im DE1050 wie im DE3224 dieses Segments
4936    // REVIEW: Collects Zeitraum-IDs from SG5 LOC.DE3224 (elements[1][3]), checks SEQ+Z78 DE1050 (elements[1][0]) for a match, then verifies RFF+Z39 exists. Co-occurrence of SEQ+Z78 and RFF+Z39 in the same SG8 instance is not verified without navigator group-instance access. (medium confidence)
4937    fn evaluate_202(&self, ctx: &EvaluationContext) -> ConditionResult {
4938        use std::collections::HashSet;
4939        // Collect Zeitraum-IDs from all SG5 LOC segments (DE3224 at elements[1][3])
4940        let loc_zeitraum_ids: HashSet<String> = ctx
4941            .find_segments("LOC")
4942            .into_iter()
4943            .filter_map(|s| s.elements.get(1)?.get(3).cloned())
4944            .filter(|v| !v.is_empty())
4945            .collect();
4946        if loc_zeitraum_ids.is_empty() {
4947            return ConditionResult::False;
4948        }
4949        // Check if any SEQ+Z78 (SG8 Lokationsbündelstruktur) has DE1050 matching a LOC DE3224
4950        let matching_seq = ctx
4951            .find_segments_with_qualifier("SEQ", 0, "Z78")
4952            .into_iter()
4953            .any(|s| {
4954                s.elements
4955                    .get(1)
4956                    .and_then(|e: &Vec<String>| e.first())
4957                    .is_some_and(|v: &String| {
4958                        !v.is_empty() && loc_zeitraum_ids.contains(v.as_str())
4959                    })
4960            });
4961        if !matching_seq {
4962            return ConditionResult::False;
4963        }
4964        // Verify RFF+Z39 also exists (co-occurrence in same SG8 not verified without navigator)
4965        ctx.has_qualifier("RFF", 0, "Z39")
4966    }
4967
4968    /// [203] Wenn STS+7++E06 / Z39 / ZC6 / ZC7/ ZT6/ ZT7 vorhanden
4969    fn evaluate_203(&self, ctx: &EvaluationContext) -> ConditionResult {
4970        let sts_segs = ctx.find_segments_with_qualifier("STS", 0, "7");
4971        let found = sts_segs.iter().any(|s| {
4972            s.elements.get(2)
4973                .and_then(|e| e.first())
4974                .is_some_and(|v| ["E06", "Z39", "ZC6", "ZC7", "ZT6", "ZT7"].contains(&v.as_str()))
4975        });
4976        ConditionResult::from(found)
4977
4978    }
4979
4980    /// [204] Wenn im SG8+SEQ+Z03/ ZA3/ ZA4 (Zähleinrichtungsdaten) mit der selben Zeitraum-ID im SG8 SEQ DE1050, sowie identischer Qualität im DE1229 der SG8 SEQ für die in diesem RFF DE1154 genannte Geräte...
4981    // REVIEW: Checks each Zähleinrichtungsdaten SEQ qualifier (Z03/ZA3/ZA4) — if any SG8 instance has that SEQ qualifier but no RFF+Z14 (Smartmeter-Gateway reference), the condition is true. The additional Zeitraum-ID and Gerätenummer cross-matching from the full condition text cannot be implemented without per-instance SG8 segment access. (medium confidence)
4982    fn evaluate_204(&self, ctx: &EvaluationContext) -> ConditionResult {
4983        // True if any SG8 with Zähleinrichtungsdaten (SEQ+Z03/ZA3/ZA4) lacks RFF+Z14 (Smartmeter-Gateway)
4984        for qual in &["Z03", "ZA3", "ZA4"] {
4985            if matches!(
4986                ctx.any_group_has_qualifier_without(
4987                    "SEQ",
4988                    0,
4989                    qual,
4990                    "RFF",
4991                    0,
4992                    "Z14",
4993                    &["SG4", "SG8"]
4994                ),
4995                ConditionResult::True
4996            ) {
4997                return ConditionResult::True;
4998            }
4999        }
5000        ConditionResult::False
5001    }
5002
5003    /// [205] Wenn SG5 LOC+Z19 (Steuerbare Ressource) vorhanden
5004    fn evaluate_205(&self, ctx: &EvaluationContext) -> ConditionResult {
5005        ctx.has_qualifier("LOC", 0, "Z19")
5006
5007    }
5008
5009    /// [209] Wenn im selben Segment im DE2379 der Code 303 vorhanden ist
5010    fn evaluate_209(&self, ctx: &EvaluationContext) -> ConditionResult {
5011        let dtm_segs = ctx.find_segments("DTM");
5012        let found = dtm_segs.iter().any(|s| {
5013            s.elements.first()
5014                .and_then(|e| e.get(2))
5015                .is_some_and(|v| v == "303")
5016        });
5017        ConditionResult::from(found)
5018
5019    }
5020
5021    /// [210] Wenn SG10 CCI+++ZA6 (Prognose auf Basis von Profilen) in dieser SG8 vorhanden
5022    fn evaluate_210(&self, ctx: &EvaluationContext) -> ConditionResult {
5023        ctx.has_qualifier("CCI", 2, "ZA6")
5024
5025    }
5026
5027    /// [212] Wenn im selben SG12 NAD DE3124 nicht vorhanden
5028    fn evaluate_212(&self, ctx: &EvaluationContext) -> ConditionResult {
5029        let nad_segs = ctx.find_segments("NAD");
5030        let found = nad_segs.iter().any(|s| {
5031            s.elements.get(3)
5032                .and_then(|e| e.first())
5033                .is_some_and(|v| !v.is_empty())
5034        });
5035        ConditionResult::from(!found)
5036
5037    }
5038
5039    /// [213] Wenn SG12 NAD+Z09 (Kunde des Lieferanten) vorhanden
5040    fn evaluate_213(&self, ctx: &EvaluationContext) -> ConditionResult {
5041        ctx.has_qualifier("NAD", 0, "Z09")
5042
5043    }
5044
5045    /// [215] Wenn in derselben SG8 (Zähleinrichtungsdaten) SG10 CCI+++E13 CAV+MME (Zählertyp: mME) vorhanden
5046    fn evaluate_215(&self, ctx: &EvaluationContext) -> ConditionResult {
5047        let cci_segs = ctx.find_segments("CCI");
5048        let has_code = cci_segs.iter().any(|s| {
5049            s.elements.get(2)
5050                .and_then(|e| e.first())
5051                .is_some_and(|v| v == "E13")
5052        });
5053        if !has_code {
5054            return ConditionResult::from(false);
5055        }
5056        let cav_segs = ctx.find_segments("CAV");
5057        let found = cav_segs.iter().any(|s| {
5058            s.elements.first()
5059                .and_then(|e| e.first())
5060                .is_some_and(|v| ["MME"].contains(&v.as_str()))
5061        });
5062        ConditionResult::from(found)
5063
5064    }
5065
5066    /// [216] Wenn CCI+++Z88 (Netznutzung) CAV+Z74:::Z08 (Netznutzungsvertrag: Direkter Vertrag zwischen Kunden und NB) vorhanden
5067    fn evaluate_216(&self, ctx: &EvaluationContext) -> ConditionResult {
5068        let cci_segs = ctx.find_segments("CCI");
5069        let has_code = cci_segs.iter().any(|s| {
5070            s.elements.get(2)
5071                .and_then(|e| e.first())
5072                .is_some_and(|v| v == "Z88")
5073        });
5074        if !has_code {
5075            return ConditionResult::from(false);
5076        }
5077        let cav_segs = ctx.find_segments("CAV");
5078        let found = cav_segs.iter().any(|s| {
5079            s.elements.first()
5080                .and_then(|e| e.first())
5081                .is_some_and(|v| ["Z74"].contains(&v.as_str()))
5082        });
5083        ConditionResult::from(found)
5084
5085    }
5086
5087    /// [217] Wenn SG6 RFF+Z49 (Verwendungszeitraum der Daten: \"Gültige Daten\") vorhanden
5088    fn evaluate_217(&self, ctx: &EvaluationContext) -> ConditionResult {
5089        ctx.has_qualifier("RFF", 0, "Z49")
5090
5091    }
5092
5093    /// [219] Wenn SG12 NAD+Z65 (Informativer Kunde des Lieferanten) vorhanden
5094    fn evaluate_219(&self, ctx: &EvaluationContext) -> ConditionResult {
5095        ctx.has_qualifier("NAD", 0, "Z65")
5096
5097    }
5098
5099    /// [220] Wenn SG8 SEQ+Z01/ Z98 (Daten der Marktlokation) SG10 CCI+Z30++Z06 (Lieferrichtung: Erzeugung) in dieser SG8 vorhanden
5100    fn evaluate_220(&self, ctx: &EvaluationContext) -> ConditionResult {
5101        let cci_segs = ctx.find_segments_with_qualifier("CCI", 0, "Z30");
5102        let found = cci_segs.iter().any(|s| {
5103            s.elements.get(2)
5104                .and_then(|e| e.first())
5105                .is_some_and(|v| ["Z06"].contains(&v.as_str()))
5106        });
5107        ConditionResult::from(found)
5108
5109    }
5110
5111    /// [221] Wenn kein SG8 SEQ+Z15 (Daten der Tranche) auf die gleiche Marktlokation-ID mit dem RFF+Z18 (Marktlokation) referenziert wie dieses SG8 SEQ+Z01 (Daten der Marktlokation)
5112    fn evaluate_221(&self, ctx: &EvaluationContext) -> ConditionResult {
5113        // Check: no SG8 with SEQ+Z15 has RFF+Z18 referencing same Marktlokation
5114        let has_seq_z15 = !ctx.find_segments_with_qualifier("SEQ", 0, "Z15").is_empty();
5115        if !has_seq_z15 {
5116            return ConditionResult::True;
5117        }
5118        // Approximate: SEQ+Z15 exists, check if it references via RFF+Z18
5119        ctx.any_group_has_qualifier_without(
5120            "SEQ", 0, "Z15", "RFF", 0, "Z18", &["SG4", "SG8"],
5121        )
5122    }
5123
5124    /// [223] Wenn das RFF+Z18 (Marktlokation) aus dieser SG8 auf das gleiche SG5 LOC+Z16 (Marktlokation), wie ein RFF+Z18 (Marktlokation) aus einer SG8 SEQ+Z01 (Daten der Marktlokation) verweist, und dort das S...
5125    fn evaluate_223(&self, ctx: &EvaluationContext) -> ConditionResult {
5126        // Check: RFF+Z18 value matches LOC+Z16 ID from SG5
5127        let rff_z18 = ctx.find_segments_with_qualifier("RFF", 0, "Z18");
5128        let loc_z16 = ctx.find_segments_with_qualifier("LOC", 0, "Z16");
5129        if rff_z18.is_empty() || loc_z16.is_empty() {
5130            return ConditionResult::Unknown;
5131        }
5132        // Check if any RFF+Z18 value matches any LOC+Z16 DE3225
5133        let loc_ids: Vec<&str> = loc_z16.iter()
5134            .filter_map(|s| s.elements.get(1).and_then(|e| e.first()).map(|v| v.as_str()))
5135            .collect();
5136        let found = rff_z18.iter().any(|s| {
5137            s.elements.first().and_then(|e| e.get(1))
5138                .is_some_and(|v| loc_ids.contains(&v.as_str()))
5139        });
5140        ConditionResult::from(found)
5141    }
5142
5143    /// [224] Wenn kein SG8 SEQ+ZE7 (Informative Daten der Tranche) auf die gleiche Marktlokation-ID mit dem RFF+Z18 (Marktlokation) referenziert wie dieses SG8 SEQ+Z98 (Informative Daten der Marktlokation)
5144    fn evaluate_224(&self, ctx: &EvaluationContext) -> ConditionResult {
5145        // Check: no SG8 with SEQ+ZE7 has RFF+Z18 referencing same Marktlokation
5146        let has_seq_ze7 = !ctx.find_segments_with_qualifier("SEQ", 0, "ZE7").is_empty();
5147        if !has_seq_ze7 {
5148            return ConditionResult::True;
5149        }
5150        ctx.any_group_has_qualifier_without(
5151            "SEQ", 0, "ZE7", "RFF", 0, "Z18", &["SG4", "SG8"],
5152        )
5153    }
5154
5155    /// [227] Wenn im SG8 SEQ+Z98 (Daten der Marktlokation) das SG10 CCI+++ZA6 (Prognosegrundlage der Marktlokation: Prognose auf Basis von Profilen) CAV+E14 (TLP/TEP) vorhanden
5156    fn evaluate_227(&self, ctx: &EvaluationContext) -> ConditionResult {
5157        let cci_segs = ctx.find_segments("CCI");
5158        let has_code = cci_segs.iter().any(|s| {
5159            s.elements.get(2)
5160                .and_then(|e| e.first())
5161                .is_some_and(|v| v == "ZA6")
5162        });
5163        if !has_code {
5164            return ConditionResult::from(false);
5165        }
5166        let cav_segs = ctx.find_segments("CAV");
5167        let found = cav_segs.iter().any(|s| {
5168            s.elements.first()
5169                .and_then(|e| e.first())
5170                .is_some_and(|v| ["E14"].contains(&v.as_str()))
5171        });
5172        ConditionResult::from(found)
5173
5174    }
5175
5176    /// [229] Wenn der Objektcode im RFF+Z33 (Referenz auf den Objektcode in der Lokationsbündelstruktur) im DE1154 derselben SG8 SEQ+Z58/ ZC9/ ZD0/ ZD6 (Zuordnung Lokation zum Objektcode des Lokationsbündels)...
5177    // REVIEW: Detects if the same (SEQ-qualifier, Zeitraum-ID, RFF+Z33-Objektcode) triple appears in two different SG8 instances — which is what the condition guards against. Uses collect_group_values to correlate SEQ and RFF data by SG8 instance index. Limitation: collect_group_values may only return the first RFF per SG8 instance, so Z33 may be missed if it is not the first RFF in the group. (medium confidence)
5178    fn evaluate_229(&self, ctx: &EvaluationContext) -> ConditionResult {
5179        use std::collections::HashSet;
5180        let target_quals: HashSet<&str> = ["Z58", "ZC9", "ZD0", "ZD6"].iter().cloned().collect();
5181        // Collect per-SG8-instance: SEQ qualifier and Zeitraum-ID
5182        let seq_quals = ctx.collect_group_values("SEQ", 0, 0, &["SG4", "SG8"]);
5183        let seq_zeitraums = ctx.collect_group_values("SEQ", 1, 0, &["SG4", "SG8"]);
5184        // Collect per-SG8-instance: RFF qualifier and DE1154 value
5185        let rff_quals = ctx.collect_group_values("RFF", 0, 0, &["SG4", "SG8"]);
5186        let rff_codes = ctx.collect_group_values("RFF", 0, 1, &["SG4", "SG8"]);
5187        // Detect duplicate (seq_qual, zeitraum_id, objektcode) combinations across SG8 instances
5188        let mut seen: HashSet<(String, String, String)> = HashSet::new();
5189        for (idx, seq_qual) in &seq_quals {
5190            if seq_qual.is_empty() || !target_quals.contains(seq_qual.as_str()) {
5191                continue;
5192            }
5193            let zeitraum_id = seq_zeitraums
5194                .iter()
5195                .find(|(i, _)| i == idx)
5196                .map(|(_, v)| v.as_str())
5197                .unwrap_or("");
5198            // Find RFF+Z33 in this SG8 instance (correlation by instance index)
5199            for (ridx, rff_qual) in rff_quals.iter() {
5200                if ridx != idx || rff_qual != "Z33" {
5201                    continue;
5202                }
5203                if let Some((_, obj_code)) = rff_codes.iter().find(|(i, _)| i == ridx) {
5204                    if obj_code.is_empty() {
5205                        continue;
5206                    }
5207                    let key = (seq_qual.clone(), zeitraum_id.to_string(), obj_code.clone());
5208                    if seen.contains(&key) {
5209                        return ConditionResult::True;
5210                    }
5211                    seen.insert(key);
5212                }
5213            }
5214        }
5215        ConditionResult::False
5216    }
5217
5218    /// [232] Wenn SG10 CCI+++Z83 (Messtechnische Einordnung der Marktlokation) CAV+Z52 (iMS) vorhanden
5219    fn evaluate_232(&self, ctx: &EvaluationContext) -> ConditionResult {
5220        let cci_segs = ctx.find_segments("CCI");
5221        let has_code = cci_segs.iter().any(|s| {
5222            s.elements.get(2)
5223                .and_then(|e| e.first())
5224                .is_some_and(|v| v == "Z83")
5225        });
5226        if !has_code {
5227            return ConditionResult::from(false);
5228        }
5229        let cav_segs = ctx.find_segments("CAV");
5230        let found = cav_segs.iter().any(|s| {
5231            s.elements.first()
5232                .and_then(|e| e.first())
5233                .is_some_and(|v| ["Z52"].contains(&v.as_str()))
5234        });
5235        ConditionResult::from(found)
5236
5237    }
5238
5239    /// [233] Wenn MP-ID in SG2 NAD+MR (Nachrichtenempfänger) in der Rolle NB
5240    fn evaluate_233(&self, ctx: &EvaluationContext) -> ConditionResult {
5241        ctx.external.evaluate("recipient_role_check")
5242    }
5243
5244    /// [234] Wenn SG10 CCI+++Z83 (Messtechnische Einordnung der Marktlokation) CAV+Z53 (kME/ mME) vorhanden
5245    fn evaluate_234(&self, ctx: &EvaluationContext) -> ConditionResult {
5246        let cci_segs = ctx.find_segments("CCI");
5247        let has_code = cci_segs.iter().any(|s| {
5248            s.elements.get(2)
5249                .and_then(|e| e.first())
5250                .is_some_and(|v| v == "Z83")
5251        });
5252        if !has_code {
5253            return ConditionResult::from(false);
5254        }
5255        let cav_segs = ctx.find_segments("CAV");
5256        let found = cav_segs.iter().any(|s| {
5257            s.elements.first()
5258                .and_then(|e| e.first())
5259                .is_some_and(|v| ["Z53"].contains(&v.as_str()))
5260        });
5261        ConditionResult::from(found)
5262
5263    }
5264
5265    /// [237] Wenn in derselben SG8 im DE7140 des PIA+Z02 (Gruppenartikel-ID / Artikel-ID) eine Gruppenartikel-ID / Artikel-ID mit 1-06 (entspricht Entgelte des Messstellenbetriebs bei kME) beginnt
5266    fn evaluate_237(&self, ctx: &EvaluationContext) -> ConditionResult {
5267        // Check: PIA+Z02 with code starting with 1-08 and containing AGS
5268        let pia_segs = ctx.find_segments("PIA");
5269        let found = pia_segs.iter().any(|s| {
5270            s.elements.first().and_then(|e| e.first()).is_some_and(|v| v == "Z02")
5271                && s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
5272                    v.starts_with("1-08") && v.contains("AGS")
5273                })
5274        });
5275        ConditionResult::from(found)
5276    }
5277
5278    /// [238] Wenn SG4 IDE+24 (Vorgang) nicht vorhanden
5279    fn evaluate_238(&self, ctx: &EvaluationContext) -> ConditionResult {
5280        ctx.lacks_qualifier("IDE", 0, "24")
5281
5282    }
5283
5284    /// [239] Wenn in derselben SG10 das CCI+Z17 (Stromverbrauchsart) im CAV+Z65 (Wärme/Kälte) vorhanden
5285    fn evaluate_239(&self, ctx: &EvaluationContext) -> ConditionResult {
5286        let cci_segs = ctx.find_segments_with_qualifier("CCI", 0, "Z17");
5287        if cci_segs.is_empty() {
5288            return ConditionResult::from(false);
5289        }
5290        let cav_segs = ctx.find_segments("CAV");
5291        let found = cav_segs.iter().any(|s| {
5292            s.elements.first()
5293                .and_then(|e| e.first())
5294                .is_some_and(|v| ["Z65"].contains(&v.as_str()))
5295        });
5296        ConditionResult::from(found)
5297
5298    }
5299
5300    /// [240] Wenn SG8 SEQ+Z01 (Daten der Marktlokation) CCI+Z22++Z91 (Veräußerungsform der erzeugenden Marktlokation:: Marktprämie) vorhanden
5301    fn evaluate_240(&self, ctx: &EvaluationContext) -> ConditionResult {
5302        let cci_segs = ctx.find_segments_with_qualifier("CCI", 0, "Z22");
5303        let found = cci_segs.iter().any(|s| {
5304            s.elements.get(2)
5305                .and_then(|e| e.first())
5306                .is_some_and(|v| ["Z91"].contains(&v.as_str()))
5307        });
5308        ConditionResult::from(found)
5309
5310    }
5311
5312    /// [241] Wenn MP-ID in SG2 NAD+MR (Nachrichtenempfänger) in der Rolle MSB
5313    fn evaluate_241(&self, ctx: &EvaluationContext) -> ConditionResult {
5314        ctx.external.evaluate("recipient_role_check")
5315    }
5316
5317    /// [243] Wenn SG10 CCI+6++ZA8 (Aggreg.verantw. NB) in dieser SG8 vorhanden
5318    fn evaluate_243(&self, ctx: &EvaluationContext) -> ConditionResult {
5319        let cci_segs = ctx.find_segments_with_qualifier("CCI", 0, "6");
5320        let found = cci_segs.iter().any(|s| {
5321            s.elements.get(2)
5322                .and_then(|e| e.first())
5323                .is_some_and(|v| ["ZA8"].contains(&v.as_str()))
5324        });
5325        ConditionResult::from(found)
5326
5327    }
5328
5329    /// [244] Wenn SG10 CCI+6++ZA9 (Aggreg.verantw. ÜNB) in dieser SG8 vorhanden
5330    fn evaluate_244(&self, ctx: &EvaluationContext) -> ConditionResult {
5331        let cci_segs = ctx.find_segments_with_qualifier("CCI", 0, "6");
5332        let found = cci_segs.iter().any(|s| {
5333            s.elements.get(2)
5334                .and_then(|e| e.first())
5335                .is_some_and(|v| ["ZA9"].contains(&v.as_str()))
5336        });
5337        ConditionResult::from(found)
5338
5339    }
5340
5341    /// [248] Wenn die Inbetriebnahme der verbrauchenden Marktlokation innerhalb der letzten drei Jahre war und die Energie der Marktlokation mit iMS ausgestatteten Messlokationen erfolgt
5342    fn evaluate_248(&self, _ctx: &EvaluationContext) -> ConditionResult {
5343        // TODO: implement
5344        ConditionResult::Unknown
5345    }
5346
5347    /// [249] Innerhalb eines SG4 IDE müssen alle DE1131 der SG4 STS+E01 (Status der Antwort) den identischen Wert enthalten
5348    fn evaluate_249(&self, ctx: &EvaluationContext) -> ConditionResult {
5349        // Check: all STS+E01 DE1131 values are identical
5350        let sts_segs = ctx.find_segments_with_qualifier("STS", 0, "E01");
5351        if sts_segs.len() <= 1 {
5352            return ConditionResult::True;
5353        }
5354        let first_val = sts_segs[0].elements.first().and_then(|e| e.get(1));
5355        let all_same = sts_segs.iter().all(|s| {
5356            s.elements.first().and_then(|e| e.get(1)) == first_val
5357        });
5358        ConditionResult::from(all_same)
5359    }
5360
5361    /// [251] Wenn MP-ID in SG2 NAD+MS (Nachrichtensender) in der Rolle BIKO
5362    fn evaluate_251(&self, ctx: &EvaluationContext) -> ConditionResult {
5363        ctx.external.evaluate("sender_role_check")
5364    }
5365
5366    /// [252] Wenn DE0068 vorhanden
5367    fn evaluate_252(&self, ctx: &EvaluationContext) -> ConditionResult {
5368        // Check: DE0068 (Versionsnummer) vorhanden
5369        // DE0068 is typically in UNH segment
5370        let unh_segs = ctx.find_segments("UNH");
5371        let found = unh_segs.iter().any(|s| {
5372            s.elements.get(1).and_then(|e| e.get(4)).is_some_and(|v| !v.is_empty())
5373        });
5374        if found { ConditionResult::True } else { ConditionResult::Unknown }
5375    }
5376
5377    /// [254] Es ist der Code \"AUA\" einzutragen
5378    fn evaluate_254(&self, _ctx: &EvaluationContext) -> ConditionResult {
5379        // Check: the code "AUA" is present
5380        ConditionResult::True // AUA is always the required code when this condition applies
5381    }
5382
5383    /// [255] Wenn Marktlokation am Redispatch 2.0 teilnimmt
5384    fn evaluate_255(&self, _ctx: &EvaluationContext) -> ConditionResult {
5385        // TODO: implement
5386        ConditionResult::Unknown
5387    }
5388
5389    /// [256] Wenn in derselben SG8 SEQ (OBIS-Daten der Zähleinrichtung / Smartmeter-Gateway) das PIA+5+1-b?:1.8.e / 1-b?:2.8.e vorhanden
5390    fn evaluate_256(&self, ctx: &EvaluationContext) -> ConditionResult {
5391        // Check: PIA+5 has OBIS code matching 1-?:1.8.? or 1-?:2.8.? pattern
5392        let pia_segs = ctx.find_segments("PIA");
5393        let found = pia_segs.iter().any(|s| {
5394            s.elements.first().and_then(|e| e.first()).is_some_and(|v| v == "5")
5395                && s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
5396                    (v.contains(":1.8.") || v.contains(":2.8.")) && v.starts_with("1-")
5397                })
5398        });
5399        ConditionResult::from(found)
5400    }
5401
5402    /// [257] Wenn in derselben SG8 SEQ (OBIS-Daten der Marktlokation) das PIA+5+1-b?:1.9.e vorhanden
5403    fn evaluate_257(&self, ctx: &EvaluationContext) -> ConditionResult {
5404        // Check: PIA+5 has OBIS code matching 1-?:1.9.? pattern
5405        let pia_segs = ctx.find_segments("PIA");
5406        let found = pia_segs.iter().any(|s| {
5407            s.elements.first().and_then(|e| e.first()).is_some_and(|v| v == "5")
5408                && s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
5409                    v.contains(":1.9.") && v.starts_with("1-")
5410                })
5411        });
5412        ConditionResult::from(found)
5413    }
5414
5415    /// [259] Wenn in diesem PIA der Code im DE7140 mit 1-01, 1-03, 1-05 enthalten ist
5416    fn evaluate_259(&self, ctx: &EvaluationContext) -> ConditionResult {
5417        // Check: PIA code starts with 1-01, 1-03, or 1-05
5418        let pia_segs = ctx.find_segments("PIA");
5419        let found = pia_segs.iter().any(|s| {
5420            s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
5421                v.starts_with("1-01") || v.starts_with("1-03") || v.starts_with("1-05")
5422            })
5423        });
5424        ConditionResult::from(found)
5425    }
5426
5427    /// [261] Wenn in diesem PIA der Code im DE7140 mit 1-07-1/2, 1-10-1/2/4/5/6/7/8/9, 1-11-1 beginnt
5428    fn evaluate_261(&self, ctx: &EvaluationContext) -> ConditionResult {
5429        // Check: PIA code starts with 1-07-1/2, 1-10-1/2/4-9, or 1-11-1
5430        let pia_segs = ctx.find_segments("PIA");
5431        let found = pia_segs.iter().any(|s| {
5432            s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
5433                v.starts_with("1-07-1") || v.starts_with("1-07-2")
5434                    || v.starts_with("1-10-") || v.starts_with("1-11-1")
5435            })
5436        });
5437        ConditionResult::from(found)
5438    }
5439
5440    /// [262] Wenn Produkt an der Marktlokation vorhanden ist
5441    fn evaluate_262(&self, _ctx: &EvaluationContext) -> ConditionResult {
5442        // TODO: implement
5443        ConditionResult::Unknown
5444    }
5445
5446    /// [265] Wenn im SG8 SEQ+Z01/ Z98 (Daten der Marktlokation) das SG10 CCI+++ZA6 (Prognosegrundlage der Marktlokation: Prognose auf Basis von Profilen) CAV+Z36 (TEP m. Referenzmessung) vorhanden
5447    fn evaluate_265(&self, ctx: &EvaluationContext) -> ConditionResult {
5448        let cci_segs = ctx.find_segments("CCI");
5449        let has_code = cci_segs.iter().any(|s| {
5450            s.elements.get(2)
5451                .and_then(|e| e.first())
5452                .is_some_and(|v| v == "ZA6")
5453        });
5454        if !has_code {
5455            return ConditionResult::from(false);
5456        }
5457        let cav_segs = ctx.find_segments("CAV");
5458        let found = cav_segs.iter().any(|s| {
5459            s.elements.first()
5460                .and_then(|e| e.first())
5461                .is_some_and(|v| ["Z36"].contains(&v.as_str()))
5462        });
5463        ConditionResult::from(found)
5464
5465    }
5466
5467    /// [266] Wenn im SG8 SEQ+Z01 (Daten der Marktlokation) mit identischer Zeitraum-ID im SG8 SEQ+Z01 DE1050 wie in dieser SG8 SEQ, das SG10 CCI+++ZA6 (Prognosegrundlage der Marktlokation: Prognose auf Basis vo...
5468    // REVIEW: Navigates all SG8 instances and their SG10 children, checking for CCI with elements[2][0]=ZA6 (Prognosegrundlage) co-occurring with CAV with elements[0][0]=Z36 (TEP m. Referenzmessung) in the same SG10 instance. The parent SG8 SEQ+Z01 filter and Zeitraum-ID cross-matching are not verified due to lack of direct SG8-level segment access; relies on CCI+ZA6/CAV+Z36 being specific enough to Marktlokation groups. (medium confidence)
5469    fn evaluate_266(&self, ctx: &EvaluationContext) -> ConditionResult {
5470        let nav = match ctx.navigator {
5471            Some(n) => n,
5472            None => {
5473                // Fallback: check any SG8+SEQ+Z01 child SG10 has CAV+Z36
5474                return ctx.filtered_parent_child_has_qualifier(
5475                    &["SG4", "SG8"],
5476                    "SEQ",
5477                    0,
5478                    "Z01",
5479                    "SG10",
5480                    "CAV",
5481                    0,
5482                    "Z36",
5483                );
5484            }
5485        };
5486        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
5487        for i in 0..sg8_count {
5488            let sg10_count = nav.child_group_instance_count(&["SG4", "SG8"], i, "SG10");
5489            for j in 0..sg10_count {
5490                // CCI+++ZA6 means elements[2][0] == "ZA6"
5491                let ccis = nav.find_segments_in_child_group("CCI", &["SG4", "SG8"], i, "SG10", j);
5492                let has_cci_za6 = ccis.iter().any(|s| {
5493                    s.elements
5494                        .get(2)
5495                        .and_then(|e: &Vec<String>| e.first())
5496                        .is_some_and(|v: &String| v == "ZA6")
5497                });
5498                if has_cci_za6 {
5499                    let cavs =
5500                        nav.find_segments_in_child_group("CAV", &["SG4", "SG8"], i, "SG10", j);
5501                    if cavs.iter().any(|s| {
5502                        s.elements
5503                            .first()
5504                            .and_then(|e: &Vec<String>| e.first())
5505                            .is_some_and(|v: &String| v == "Z36")
5506                    }) {
5507                        return ConditionResult::True;
5508                    }
5509                }
5510            }
5511        }
5512        ConditionResult::False
5513    }
5514
5515    /// [267] Wenn SG8 SEQ+Z38 (Referenzprofildaten) nicht vorhanden
5516    fn evaluate_267(&self, ctx: &EvaluationContext) -> ConditionResult {
5517        ctx.lacks_qualifier("SEQ", 0, "Z38")
5518
5519    }
5520
5521    /// [268] Wenn der Code im DE3207 in der \"EDI@Energy Codeliste der europäischen Ländercodes\" in der Spalte \"PLZ vorhanden\" ein \"X\" aufgeführt ist
5522    fn evaluate_268(&self, _ctx: &EvaluationContext) -> ConditionResult {
5523        ConditionResult::True
5524    }
5525
5526    /// [269] Es sind alle OBIS-Kennzahlen gem. EDI@Energy Codeliste der OBIS-Kennzahlen und Medien für den deutschen Energiemarkt Kap.3 anzugeben welche an der Tranche erforderlich sind
5527    fn evaluate_269(&self, _ctx: &EvaluationContext) -> ConditionResult {
5528        // TODO: implement
5529        ConditionResult::Unknown
5530    }
5531
5532    /// [270] Wenn in diesem PIA der Code im DE7140 mit 1-02, 1-04, 1-06, 1-09 beginnt
5533    fn evaluate_270(&self, ctx: &EvaluationContext) -> ConditionResult {
5534        // Check: PIA code starts with 1-02, 1-04, 1-06, or 1-09
5535        let pia_segs = ctx.find_segments("PIA");
5536        let found = pia_segs.iter().any(|s| {
5537            s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
5538                v.starts_with("1-02") || v.starts_with("1-04")
5539                    || v.starts_with("1-06") || v.starts_with("1-09")
5540            })
5541        });
5542        ConditionResult::from(found)
5543    }
5544
5545    /// [273] Wenn in derselben SG8 SEQ+Z20 (OBIS-Daten der Zähleinrichtung / Smartmeter-Gateway) das PIA+5+1-b?:1.8.e / 1-b?:2.8.e / 1-b?:3.8.e / 1-b?:4.8.e / 1-b?:5.8.e / 1-b?:6.8.e / 1-b?:7.8.e / 1-b?:8.8.e ...
5546    fn evaluate_273(&self, ctx: &EvaluationContext) -> ConditionResult {
5547        // Check: PIA+5 has OBIS code matching energy quantity patterns in SG8 SEQ+Z20
5548        let pia_segs = ctx.find_segments("PIA");
5549        let found = pia_segs.iter().any(|s| {
5550            s.elements.first().and_then(|e| e.first()).is_some_and(|v| v == "5")
5551                && s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
5552                    v.starts_with("1-") && (v.contains(":1.8.") || v.contains(":2.8.") || v.contains(":1.29."))
5553                })
5554        });
5555        ConditionResult::from(found)
5556    }
5557
5558    /// [279] Wenn STS+7++xxx+ZW4 / ZAP (Transaktionsgrundergänzung  verbrauchende Marktlokation / ruhende Marktlokation) vorhanden
5559    fn evaluate_279(&self, ctx: &EvaluationContext) -> ConditionResult {
5560        let sts_segs = ctx.find_segments_with_qualifier("STS", 0, "7");
5561        let found = sts_segs.iter().any(|s| {
5562            s.elements.get(3)
5563                .and_then(|e| e.first())
5564                .is_some_and(|v| ["ZW4", "ZAP"].contains(&v.as_str()))
5565        });
5566        ConditionResult::from(found)
5567
5568    }
5569
5570    /// [280] Nur MP-ID aus Sparte Strom
5571    fn evaluate_280(&self, ctx: &EvaluationContext) -> ConditionResult {
5572        // Check: Marktpartner-ID is from Sparte Strom
5573        // Approximate: check NAD+MS C082 for Strom MP-ID format
5574        ctx.external.evaluate("sector_check")
5575    }
5576
5577    /// [282] Wenn in diesem PIA der Code im DE7140 1-01-6-005, 1-01-9-001/002, 1-07-3-001, 1- 08-1-001, 1-08-3-001, 1-08-4-001/002/003/004, 1-10-3-001 enthalten ist
5578    fn evaluate_282(&self, ctx: &EvaluationContext) -> ConditionResult {
5579        // Check: PIA DE7140 has one of the specific codes
5580        let pia_segs = ctx.find_segments("PIA");
5581        let found = pia_segs.iter().any(|s| {
5582            s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
5583                v.starts_with("1-01-6-005") || v.starts_with("1-01-9-001") || v.starts_with("1-01-9-002")
5584                    || v.starts_with("1-07-3-001") || v.starts_with("1-08-1-001")
5585                    || v.starts_with("1-08-3-001") || v.starts_with("1-08-4-001")
5586                    || v.starts_with("1-08-4-002") || v.starts_with("1-08-4-003")
5587                    || v.starts_with("1-08-5")
5588            })
5589        });
5590        ConditionResult::from(found)
5591    }
5592
5593    /// [284] Erlaubte Codes aus der Codeliste der Gruppenartikel- und Artikel-ID sind in der Spalte UTILMD/Codeverwendung mit X gekennzeichnet
5594    fn evaluate_284(&self, _ctx: &EvaluationContext) -> ConditionResult {
5595        // TODO: implement
5596        ConditionResult::Unknown
5597    }
5598
5599    /// [285] Wenn in diesem PIA der Code im DE7140 mit 1-08-3-AGS angegeben ist
5600    fn evaluate_285(&self, ctx: &EvaluationContext) -> ConditionResult {
5601        // Check: PIA DE7140 starts with 1-08-3-AGS
5602        let pia_segs = ctx.find_segments("PIA");
5603        let found = pia_segs.iter().any(|s| {
5604            s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| v.contains("1-08-3") && v.contains("AGS"))
5605        });
5606        ConditionResult::from(found)
5607    }
5608
5609    /// [286] Wenn in diesem PIA der Code im DE7140 mit 1-08-1/2/4/5-AGS-KG
5610    fn evaluate_286(&self, ctx: &EvaluationContext) -> ConditionResult {
5611        // Check: PIA DE7140 matches 1-08-1/2/4/5-AGS-KG pattern
5612        let pia_segs = ctx.find_segments("PIA");
5613        let found = pia_segs.iter().any(|s| {
5614            s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
5615                (v.starts_with("1-08-1") || v.starts_with("1-08-2")
5616                    || v.starts_with("1-08-4") || v.starts_with("1-08-5"))
5617                    && v.contains("AGS") && v.contains("KG")
5618            })
5619        });
5620        ConditionResult::from(found)
5621    }
5622
5623    /// [287] Erlaubte Codes aus der PRICAT BGM+ Z70 (Preisblatt Netznutzung) des verantwortlichen NB
5624    fn evaluate_287(&self, _ctx: &EvaluationContext) -> ConditionResult {
5625        // TODO: implement
5626        ConditionResult::Unknown
5627    }
5628
5629    /// [288] Wenn in derselben SG8 (OBIS-Daten der Marktlokation) das PIA+5+1-b?:2.9.e vorhanden
5630    fn evaluate_288(&self, ctx: &EvaluationContext) -> ConditionResult {
5631        // Check: PIA+5 has OBIS code matching 1-?:2.9.? pattern
5632        let pia_segs = ctx.find_segments("PIA");
5633        let found = pia_segs.iter().any(|s| {
5634            s.elements.first().and_then(|e| e.first()).is_some_and(|v| v == "5")
5635                && s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
5636                    v.contains(":2.9.") && v.starts_with("1-")
5637                })
5638        });
5639        ConditionResult::from(found)
5640    }
5641
5642    /// [291] Wenn in derselben SG8 (Zuordnung Lokation zum Objektcode des Lokationsbündels) das RFF+Z37 vorhanden
5643    fn evaluate_291(&self, ctx: &EvaluationContext) -> ConditionResult {
5644        ctx.has_qualifier("RFF", 0, "Z37")
5645
5646    }
5647
5648    /// [292] Es sind nur die Produkt-Codes erlaubt, die in der Codeliste der Konfigurationen im Kapitel 6.1 \"Anmeldung einer Zuordnung des LFN (UTILMD)\" in der Spalte \"Anmeldung einer Zuordnung des LFN (UTIL...
5649    fn evaluate_292(&self, ctx: &EvaluationContext) -> ConditionResult {
5650        ctx.external.evaluate("code_list_membership_check")
5651    }
5652
5653    /// [293] Wenn SG5 LOC+Z22 (ruhende Marktlokation) vorhanden
5654    fn evaluate_293(&self, ctx: &EvaluationContext) -> ConditionResult {
5655        ctx.has_qualifier("LOC", 0, "Z22")
5656
5657    }
5658
5659    /// [294] Wenn SG5 LOC+Z18 (Netzlokation) vorhanden
5660    fn evaluate_294(&self, ctx: &EvaluationContext) -> ConditionResult {
5661        ctx.has_qualifier("LOC", 0, "Z18")
5662
5663    }
5664
5665    /// [295] Wenn SG5 LOC+Z15 (MaBiS-Zählpunkt) nicht vorhanden
5666    fn evaluate_295(&self, ctx: &EvaluationContext) -> ConditionResult {
5667        ctx.lacks_qualifier("LOC", 0, "Z15")
5668
5669    }
5670
5671    /// [296] Wenn SG5 LOC+Z20 (Technische Ressource) vorhanden
5672    fn evaluate_296(&self, ctx: &EvaluationContext) -> ConditionResult {
5673        ctx.has_qualifier("LOC", 0, "Z20")
5674
5675    }
5676
5677    /// [298] Wenn SG5 LOC+Z20 (Technische Ressource) nicht vorhanden
5678    fn evaluate_298(&self, ctx: &EvaluationContext) -> ConditionResult {
5679        ctx.lacks_qualifier("LOC", 0, "Z20")
5680
5681    }
5682
5683    /// [300] Wenn SG8 SEQ+Z15/ ZE7 (Daten der Tranche) nicht vorhanden
5684    fn evaluate_300(&self, ctx: &EvaluationContext) -> ConditionResult {
5685        let seq_segs = ctx.find_segments("SEQ");
5686        let found = seq_segs.iter().any(|s| {
5687            s.elements.first()
5688                .and_then(|e| e.first())
5689                .is_some_and(|v| ["Z15", "ZE7"].contains(&v.as_str()))
5690        });
5691        ConditionResult::from(!found)
5692
5693    }
5694
5695    /// [301] Wenn BGM+E03 (Änderungsmeldung) vorhanden
5696    fn evaluate_301(&self, ctx: &EvaluationContext) -> ConditionResult {
5697        ctx.has_qualifier("BGM", 0, "E03")
5698
5699    }
5700
5701    /// [302] Wenn SG8 SEQ+Z08 (Profilschardaten) nicht vorhanden
5702    fn evaluate_302(&self, ctx: &EvaluationContext) -> ConditionResult {
5703        ctx.lacks_qualifier("SEQ", 0, "Z08")
5704
5705    }
5706
5707    /// [303] Wenn SG8 SEQ+Z21 (Profildaten) nicht vorhanden
5708    fn evaluate_303(&self, ctx: &EvaluationContext) -> ConditionResult {
5709        ctx.lacks_qualifier("SEQ", 0, "Z21")
5710
5711    }
5712
5713    /// [304] Wenn SG8 SEQ+Z22 (Daten der Summenzeitreihe) vorhanden
5714    fn evaluate_304(&self, ctx: &EvaluationContext) -> ConditionResult {
5715        ctx.has_qualifier("SEQ", 0, "Z22")
5716
5717    }
5718
5719    /// [305] Wenn SG8 SEQ+Z24 (Daten der Überführungszeitreihe) vorhanden
5720    fn evaluate_305(&self, ctx: &EvaluationContext) -> ConditionResult {
5721        ctx.has_qualifier("SEQ", 0, "Z24")
5722
5723    }
5724
5725    /// [306] Wenn SG5 LOC+Z22 (ruhende Marktlokation) mit derselben Zeitraum-ID im DE3224 vorhanden wie in dieser SG8 im DE1050 (Referenz auf Zeitraum-ID) des SEQ+Z58
5726    fn evaluate_306(&self, ctx: &EvaluationContext) -> ConditionResult {
5727        use std::collections::HashSet;
5728        // Collect Zeitraum-IDs from SG5 LOC+Z22 (Ruhende Marktlokation) — DE3224 at elements[1][3]
5729        let loc_z22_ids: HashSet<String> = ctx
5730            .find_segments_with_qualifier("LOC", 0, "Z22")
5731            .into_iter()
5732            .filter_map(|s| s.elements.get(1)?.get(3).cloned())
5733            .filter(|v| !v.is_empty())
5734            .collect();
5735        if loc_z22_ids.is_empty() {
5736            return ConditionResult::False;
5737        }
5738        // Check if any SG8 SEQ+Z58 has DE1050 (elements[1][0]) matching a LOC+Z22 DE3224
5739        let has_match = ctx
5740            .find_segments_with_qualifier("SEQ", 0, "Z58")
5741            .iter()
5742            .any(|s| {
5743                s.elements
5744                    .get(1)
5745                    .and_then(|e: &Vec<String>| e.first())
5746                    .is_some_and(|v: &String| !v.is_empty() && loc_z22_ids.contains(v.as_str()))
5747            });
5748        ConditionResult::from(has_match)
5749    }
5750
5751    /// [307] Wenn SG5 LOC+Z22 (ruhende Marktlokation) mit derselben Zeitraum-ID im DE3224 nicht vorhanden wie in dieser SG8 im DE1050 (Referenz auf Zeitraum-ID) des SEQ+Z58
5752    // REVIEW: Collects Zeitraum-IDs from SG5 LOC+Z22 segments (DE3224 = elements[1][3]) and SEQ+Z58 segments (DE1050 = elements[1][0]). Returns True when any SEQ+Z58 has a Zeitraum-ID with no matching LOC+Z22 entry. Message-wide fallback since group-level Zeitraum-ID isolation is not possible without navigator. (medium confidence)
5753    fn evaluate_307(&self, ctx: &EvaluationContext) -> ConditionResult {
5754        let seq_segs = ctx.find_segments_with_qualifier("SEQ", 0, "Z58");
5755        if seq_segs.is_empty() {
5756            return ConditionResult::Unknown;
5757        }
5758        let loc_segs = ctx.find_segments_with_qualifier("LOC", 0, "Z22");
5759        let loc_z22_zeitraum_ids: Vec<&str> = loc_segs
5760            .iter()
5761            .filter_map(|s| s.elements.get(1).and_then(|e| e.get(3)).map(|v| v.as_str()))
5762            .filter(|v| !v.is_empty())
5763            .collect();
5764        ConditionResult::from(seq_segs.iter().any(|seq| {
5765            let zid = seq
5766                .elements
5767                .get(1)
5768                .and_then(|e: &Vec<String>| e.first())
5769                .map(|v| v.as_str())
5770                .unwrap_or("");
5771            !zid.is_empty() && !loc_z22_zeitraum_ids.contains(&zid)
5772        }))
5773    }
5774
5775    /// [309] Wenn SG5 LOC+Z22 (ruhende Marktlokation) nicht vorhanden
5776    fn evaluate_309(&self, ctx: &EvaluationContext) -> ConditionResult {
5777        ctx.lacks_qualifier("LOC", 0, "Z22")
5778
5779    }
5780
5781    /// [314] Es sind alle OBIS-Kennzahlen gem. Codeliste der OBIS-Kennzahlen und Medien Kapitel 3 anzugeben welche an der Marktlokation erforderlich sind. Der Mindestumfang der OBIS-Kennzahlen ergibt sich aus d...
5782    fn evaluate_314(&self, ctx: &EvaluationContext) -> ConditionResult {
5783        ctx.external.evaluate("code_list_membership_check")
5784    }
5785
5786    /// [315] Wenn BGM+Z88 (Datenclearing) vorhanden
5787    fn evaluate_315(&self, ctx: &EvaluationContext) -> ConditionResult {
5788        ctx.external.evaluate("data_clearing_required")
5789    }
5790
5791    /// [316] Wenn in derselben SG8 das RFF+Z14 (Smartmeter-Gateway) nicht vorhanden
5792    fn evaluate_316(&self, ctx: &EvaluationContext) -> ConditionResult {
5793        ctx.any_group_has_qualifier_without("SEQ", 0, "Z03", "RFF", 0, "Z14", &["SG4", "SG8"])
5794    }
5795
5796    /// [317] Es ist derselbe Wert wie im DE2380 von DTM+92 (Datum Vertragsbeginn) einzutragen
5797    fn evaluate_317(&self, ctx: &EvaluationContext) -> ConditionResult {
5798        // Check: DTM+92 (Datum Vertragsbeginn) exists — value identity is structural
5799        let found = !ctx.find_segments_with_qualifier("DTM", 0, "92").is_empty();
5800        if found { ConditionResult::True } else { ConditionResult::Unknown }
5801    }
5802
5803    /// [318] Es ist derselbe Wert wie im DE2380 von DTM+93 (Datum Vertragende) einzutragen
5804    fn evaluate_318(&self, ctx: &EvaluationContext) -> ConditionResult {
5805        // Check: DTM+93 (Datum Vertragende) exists — value identity is structural
5806        let found = !ctx.find_segments_with_qualifier("DTM", 0, "93").is_empty();
5807        if found { ConditionResult::True } else { ConditionResult::Unknown }
5808    }
5809
5810    /// [321] Wenn im DE3155 in demselben COM der Code EM vorhanden ist
5811    fn evaluate_321(&self, ctx: &EvaluationContext) -> ConditionResult {
5812        // Check if any COM segment has qualifier "EM" in element[0][1] (DE3155)
5813        let com_segs = ctx.find_segments("COM");
5814        let has_em = com_segs.iter().any(|s| {
5815            s.elements.first()
5816                .and_then(|e| e.get(1))
5817                .is_some_and(|v| v == "EM")
5818        });
5819        ConditionResult::from(has_em)
5820    }
5821
5822    /// [322] Wenn im DE3155 in demselben COM der Code TE / FX / AJ / AL vorhanden ist
5823    fn evaluate_322(&self, ctx: &EvaluationContext) -> ConditionResult {
5824        // Check if any COM segment has qualifier TE/FX/AJ/AL in element[0][1] (DE3155)
5825        let com_segs = ctx.find_segments("COM");
5826        let has_phone = com_segs.iter().any(|s| {
5827            s.elements.first()
5828                .and_then(|e| e.get(1))
5829                .is_some_and(|v| matches!(v.as_str(), "TE" | "FX" | "AJ" | "AL"))
5830        });
5831        ConditionResult::from(has_phone)
5832    }
5833
5834    /// [323] Es sind alle OBIS-Kennzahlen gem. EDI@Energy Codeliste der OBIS-Kennzahlen und Medien für den deutschen Energiemarkt Kap. 3. anzugeben, welche an der Zähleinrichtung genutzt werden. Der Mindestum...
5835    fn evaluate_323(&self, ctx: &EvaluationContext) -> ConditionResult {
5836        ctx.external.evaluate("code_list_membership_check")
5837    }
5838
5839    /// [327] Wenn die Zeitraum-ID im DE1050 dieser SG8 SEQ, welche mit der Zeitraum-ID einer SG6 RFF+Z47/Z48/Z49 (Verwendungszeitraum der Daten) identisch ist und in deren SG das SG6 DTM+Z25 (Verwendung der Dat...
5840    // REVIEW: Step 1: Collect all SEQ DE1050 Zeitraum-IDs. Step 2: Find RFF+Z47/Z48/Z49 (Verwendungszeitraum) whose DE1156 (elements[0][2]) matches. Step 3: Check co-located DTM+Z25 DE2380 (elements[0][1]) >= threshold after normalising EDIFACT escape '?+' to '+'. Falls back to message-wide DTM search which may give false positives if multiple SG6 groups are present, but is sufficient for typical single-transaction messages. (medium confidence)
5841    fn evaluate_327(&self, ctx: &EvaluationContext) -> ConditionResult {
5842        const THRESHOLD: &str = "202212312300+00";
5843        let seq_zeitraum_ids: Vec<String> = ctx
5844            .find_segments("SEQ")
5845            .iter()
5846            .filter_map(|s| {
5847                s.elements
5848                    .get(1)
5849                    .and_then(|e: &Vec<String>| e.first())
5850                    .cloned()
5851            })
5852            .filter(|v| !v.is_empty())
5853            .collect();
5854        if seq_zeitraum_ids.is_empty() {
5855            return ConditionResult::Unknown;
5856        }
5857        let rff_segs = ctx.find_segments("RFF");
5858        let matching_rff = rff_segs.iter().any(|rff| {
5859            let qual = rff
5860                .elements
5861                .first()
5862                .and_then(|e: &Vec<String>| e.first())
5863                .map(|v| v.as_str())
5864                .unwrap_or("");
5865            if !["Z47", "Z48", "Z49"].contains(&qual) {
5866                return false;
5867            }
5868            let zid = rff
5869                .elements
5870                .first()
5871                .and_then(|e| e.get(2))
5872                .map(|v| v.as_str())
5873                .unwrap_or("");
5874            !zid.is_empty() && seq_zeitraum_ids.iter().any(|id| id.as_str() == zid)
5875        });
5876        if !matching_rff {
5877            return ConditionResult::False;
5878        }
5879        for dtm in ctx.find_segments_with_qualifier("DTM", 0, "Z25") {
5880            let val = dtm
5881                .elements
5882                .first()
5883                .and_then(|e| e.get(1))
5884                .map(|v| v.replace("?+", "+"))
5885                .unwrap_or_default();
5886            if !val.is_empty() && val.as_str() >= THRESHOLD {
5887                return ConditionResult::True;
5888            }
5889        }
5890        ConditionResult::False
5891    }
5892
5893    /// [329] Wenn in dieser SG4 IDE+24 das STS+E01++A03: E_0096 bzw. A04:E_0097 (Status der Antwort) nicht vorhanden
5894    fn evaluate_329(&self, ctx: &EvaluationContext) -> ConditionResult {
5895        match ctx.has_qualified_value("STS", 0, "E01", 2, 0, &["A03"]) {
5896            ConditionResult::True => ConditionResult::False,
5897            ConditionResult::False => ConditionResult::True,
5898            other => other,
5899        }
5900
5901    }
5902
5903    /// [332] Wenn im SG8 SEQ+Z01 (Daten der Marktlokation) mit identischer Zeitraum-ID im DE1050 wie in dieser SG8, das SG10 CCI+6++ZA9 (Aggreg. verantw. ÜNB) nicht vorhanden
5904    // REVIEW: Checks whether any SG8 instance lacks CCI+6++ZA9 (Aggregationsverantwortung ÜNB, elements[0]='6' and elements[2][0]='ZA9') in its SG10 children. With navigator: iterates SG8 instances and their SG10 children. Without navigator: falls back to message-wide CCI+6 with ZA9 check. The Zeitraum-ID cross-reference between this SG8 and the Z01 SG8 cannot be fully verified without direct SG8-level segment access via navigator, so this is an approximation that may produce false positives in multi-SG4-group messages. (medium confidence)
5905    fn evaluate_332(&self, ctx: &EvaluationContext) -> ConditionResult {
5906        let nav = match ctx.navigator {
5907            Some(n) => n,
5908            None => {
5909                if ctx.find_segments_with_qualifier("SEQ", 0, "Z01").is_empty() {
5910                    return ConditionResult::Unknown;
5911                }
5912                let has_cci_za9 = ctx
5913                    .find_segments_with_qualifier("CCI", 0, "6")
5914                    .iter()
5915                    .any(|s| {
5916                        s.elements
5917                            .get(2)
5918                            .and_then(|e: &Vec<String>| e.first())
5919                            .map(|v| v.as_str())
5920                            == Some("ZA9")
5921                    });
5922                return ConditionResult::from(!has_cci_za9);
5923            }
5924        };
5925        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
5926        if sg8_count == 0 {
5927            return ConditionResult::Unknown;
5928        }
5929        for sg8_idx in 0..sg8_count {
5930            let sg10_count = nav.child_group_instance_count(&["SG4", "SG8"], sg8_idx, "SG10");
5931            let mut has_cci_za9 = false;
5932            for sg10_idx in 0..sg10_count {
5933                let ccis = nav.find_segments_in_child_group(
5934                    "CCI",
5935                    &["SG4", "SG8"],
5936                    sg8_idx,
5937                    "SG10",
5938                    sg10_idx,
5939                );
5940                if ccis.iter().any(|s| {
5941                    s.elements
5942                        .first()
5943                        .and_then(|e: &Vec<String>| e.first())
5944                        .map(|v| v.as_str())
5945                        == Some("6")
5946                        && s.elements
5947                            .get(2)
5948                            .and_then(|e: &Vec<String>| e.first())
5949                            .map(|v| v.as_str())
5950                            == Some("ZA9")
5951                }) {
5952                    has_cci_za9 = true;
5953                    break;
5954                }
5955            }
5956            if !has_cci_za9 {
5957                return ConditionResult::True;
5958            }
5959        }
5960        ConditionResult::False
5961    }
5962
5963    /// [333] Wenn in diesem RFF \"Referenz auf die Lokationsbündelstruktur\" im DE1153 der Code Z31 \"Lokationsbündelstruktur\" vorhanden ist
5964    fn evaluate_333(&self, ctx: &EvaluationContext) -> ConditionResult {
5965        ctx.has_qualifier("RFF", 0, "Z31")
5966
5967    }
5968
5969    /// [334] Nur MP-ID aus Sparte Gas
5970    fn evaluate_334(&self, ctx: &EvaluationContext) -> ConditionResult {
5971        // Check: Marktpartner-ID is from Sparte Gas
5972        ctx.external.evaluate("sector_check")
5973    }
5974
5975    /// [335] Wenn in dieser SG8 im SEQ+Z58 (Zuordnung Lokation zum Objektcode des Lokationsbündels) im DE1050 eine Zeitraum-ID angegeben wird, wie in einer SG8 SEQ+Z78 RFF+Z31 (Referenz auf die Lokationsbünde...
5976    // REVIEW: Collects Zeitraum-IDs from SEQ+Z58 DE1050 (elements[1][0]). Verifies an SG8 with SEQ+Z78 exists (entry for Lokationsbündelstruktur). Collects IDs from RFF+Z31 DE1154 (elements[0][1]) and checks if any SEQ+Z58 Zeitraum-ID matches. Co-location of RFF+Z31 with SEQ+Z78 in the same SG8 instance is approximated message-wide; exact co-location verification would require navigator SG8-level segment access. (medium confidence)
5977    fn evaluate_335(&self, ctx: &EvaluationContext) -> ConditionResult {
5978        let z58_ids: Vec<String> = ctx
5979            .find_segments_with_qualifier("SEQ", 0, "Z58")
5980            .iter()
5981            .filter_map(|s| {
5982                s.elements
5983                    .get(1)
5984                    .and_then(|e: &Vec<String>| e.first())
5985                    .cloned()
5986            })
5987            .filter(|v| !v.is_empty())
5988            .collect();
5989        if z58_ids.is_empty() {
5990            return ConditionResult::Unknown;
5991        }
5992        if ctx.find_segments_with_qualifier("SEQ", 0, "Z78").is_empty() {
5993            return ConditionResult::False;
5994        }
5995        let rff_z31_ids: Vec<String> = ctx
5996            .find_segments("RFF")
5997            .iter()
5998            .filter(|rff| {
5999                rff.elements
6000                    .first()
6001                    .and_then(|e: &Vec<String>| e.first())
6002                    .map(|v| v.as_str())
6003                    == Some("Z31")
6004            })
6005            .filter_map(|rff| rff.elements.first().and_then(|e| e.get(1)).cloned())
6006            .filter(|v| !v.is_empty())
6007            .collect();
6008        ConditionResult::from(z58_ids.iter().any(|id| rff_z31_ids.contains(id)))
6009    }
6010
6011    /// [336] Wenn in Änderungsmeldung gefüllt
6012    fn evaluate_336(&self, _ctx: &EvaluationContext) -> ConditionResult {
6013        // TODO: implement
6014        ConditionResult::Unknown
6015    }
6016
6017    /// [337] Wenn SG10 CCI+++ZB4 (Bezeichnung der Summenzeitreihe) CAV+Z95/ Z96 vorhanden
6018    fn evaluate_337(&self, ctx: &EvaluationContext) -> ConditionResult {
6019        let cci_segs = ctx.find_segments("CCI");
6020        let has_code = cci_segs.iter().any(|s| {
6021            s.elements.get(2)
6022                .and_then(|e| e.first())
6023                .is_some_and(|v| v == "ZB4")
6024        });
6025        if !has_code {
6026            return ConditionResult::from(false);
6027        }
6028        let cav_segs = ctx.find_segments("CAV");
6029        let found = cav_segs.iter().any(|s| {
6030            s.elements.first()
6031                .and_then(|e| e.first())
6032                .is_some_and(|v| ["Z95", "Z96"].contains(&v.as_str()))
6033        });
6034        ConditionResult::from(found)
6035
6036    }
6037
6038    /// [338] Wenn CCI+++ZB4 (Bezeichnung der Summenzeitreihe) CAV+Z95/ Z96/ Z97/ Z99/ ZA1/ ZA3/ ZA4/ZG7 vorhanden
6039    fn evaluate_338(&self, ctx: &EvaluationContext) -> ConditionResult {
6040        let cci_segs = ctx.find_segments("CCI");
6041        let has_code = cci_segs.iter().any(|s| {
6042            s.elements.get(2)
6043                .and_then(|e| e.first())
6044                .is_some_and(|v| v == "ZB4")
6045        });
6046        if !has_code {
6047            return ConditionResult::from(false);
6048        }
6049        let cav_segs = ctx.find_segments("CAV");
6050        let found = cav_segs.iter().any(|s| {
6051            s.elements.first()
6052                .and_then(|e| e.first())
6053                .is_some_and(|v| ["Z95", "Z96", "Z97", "Z99", "ZA1", "ZA3", "ZA4", "ZG7"].contains(&v.as_str()))
6054        });
6055        ConditionResult::from(found)
6056
6057    }
6058
6059    /// [339] Wenn CCI+++ZB4 (Bezeichnung der Summenzeitreihe) CAV+Z97/ Z98/ Z99/ ZA0/ ZA1/ ZA2/ ZA3/ ZA6/ ZG7 vorhanden
6060    fn evaluate_339(&self, ctx: &EvaluationContext) -> ConditionResult {
6061        let cci_segs = ctx.find_segments("CCI");
6062        let has_code = cci_segs.iter().any(|s| {
6063            s.elements.get(2)
6064                .and_then(|e| e.first())
6065                .is_some_and(|v| v == "ZB4")
6066        });
6067        if !has_code {
6068            return ConditionResult::from(false);
6069        }
6070        let cav_segs = ctx.find_segments("CAV");
6071        let found = cav_segs.iter().any(|s| {
6072            s.elements.first()
6073                .and_then(|e| e.first())
6074                .is_some_and(|v| ["Z97", "Z98", "Z99", "ZA0", "ZA1", "ZA2", "ZA3", "ZA6", "ZG7"].contains(&v.as_str()))
6075        });
6076        ConditionResult::from(found)
6077
6078    }
6079
6080    /// [340] Wenn CCI+++ZB4 (Bezeichnung der Summenzeitreihe) CAV+Z95/ Z97/ Z98/ Z99/ ZA1/ ZA2/ ZA3/ ZA4/ ZA5/ ZA6 vorhanden
6081    fn evaluate_340(&self, ctx: &EvaluationContext) -> ConditionResult {
6082        let cci_segs = ctx.find_segments("CCI");
6083        let has_code = cci_segs.iter().any(|s| {
6084            s.elements.get(2)
6085                .and_then(|e| e.first())
6086                .is_some_and(|v| v == "ZB4")
6087        });
6088        if !has_code {
6089            return ConditionResult::from(false);
6090        }
6091        let cav_segs = ctx.find_segments("CAV");
6092        let found = cav_segs.iter().any(|s| {
6093            s.elements.first()
6094                .and_then(|e| e.first())
6095                .is_some_and(|v| ["Z95", "Z97", "Z98", "Z99", "ZA1", "ZA2", "ZA3", "ZA4", "ZA5", "ZA6"].contains(&v.as_str()))
6096        });
6097        ConditionResult::from(found)
6098
6099    }
6100
6101    /// [341] Wenn CCI+++ZB4 (Bezeichnung der Summenzeitreihe) CAV+Z96/ ZA0/ ZG7 vorhanden
6102    fn evaluate_341(&self, ctx: &EvaluationContext) -> ConditionResult {
6103        let cci_segs = ctx.find_segments("CCI");
6104        let has_code = cci_segs.iter().any(|s| {
6105            s.elements.get(2)
6106                .and_then(|e| e.first())
6107                .is_some_and(|v| v == "ZB4")
6108        });
6109        if !has_code {
6110            return ConditionResult::from(false);
6111        }
6112        let cav_segs = ctx.find_segments("CAV");
6113        let found = cav_segs.iter().any(|s| {
6114            s.elements.first()
6115                .and_then(|e| e.first())
6116                .is_some_and(|v| ["Z96", "ZA0", "ZG7"].contains(&v.as_str()))
6117        });
6118        ConditionResult::from(found)
6119
6120    }
6121
6122    /// [342] Wenn aktiver MaBiS-ZP für den Betrachtungszeitraum vorhanden
6123    fn evaluate_342(&self, _ctx: &EvaluationContext) -> ConditionResult {
6124        // TODO: implement
6125        ConditionResult::Unknown
6126    }
6127
6128    /// [344] Wenn in dieser SG8 das SG10 CCI+++ZB4 (Bezeichnung der Summenzeitreihe) CAV+ZF1 (Netzgangzeitreihe (NGZ)) vorhanden
6129    fn evaluate_344(&self, ctx: &EvaluationContext) -> ConditionResult {
6130        let cci_segs = ctx.find_segments("CCI");
6131        let has_code = cci_segs.iter().any(|s| {
6132            s.elements.get(2)
6133                .and_then(|e| e.first())
6134                .is_some_and(|v| v == "ZB4")
6135        });
6136        if !has_code {
6137            return ConditionResult::from(false);
6138        }
6139        let cav_segs = ctx.find_segments("CAV");
6140        let found = cav_segs.iter().any(|s| {
6141            s.elements.first()
6142                .and_then(|e| e.first())
6143                .is_some_and(|v| ["ZF1"].contains(&v.as_str()))
6144        });
6145        ConditionResult::from(found)
6146
6147    }
6148
6149    /// [345] Wenn SG5 LOC+Z17 (Messlokation) vorhanden
6150    fn evaluate_345(&self, ctx: &EvaluationContext) -> ConditionResult {
6151        ctx.has_qualifier("LOC", 0, "Z17")
6152
6153    }
6154
6155    /// [347] Wenn SG5 LOC+Z16 (Marktlokation) nicht vorhanden
6156    fn evaluate_347(&self, ctx: &EvaluationContext) -> ConditionResult {
6157        ctx.lacks_qualifier("LOC", 0, "Z16")
6158
6159    }
6160
6161    /// [348] Wenn SG5 LOC+Z21 (Tranche) nicht vorhanden
6162    fn evaluate_348(&self, ctx: &EvaluationContext) -> ConditionResult {
6163        ctx.lacks_qualifier("LOC", 0, "Z21")
6164
6165    }
6166
6167    /// [349] Wenn SG5 LOC+Z17 (Messlokation) nicht vorhanden
6168    fn evaluate_349(&self, ctx: &EvaluationContext) -> ConditionResult {
6169        ctx.lacks_qualifier("LOC", 0, "Z17")
6170
6171    }
6172
6173    /// [350] Wenn SG8 SEQ+Z78/ZD5 RFF+Z31 (Referenz auf die Lokationsbündelstruktur) vorhanden
6174    fn evaluate_350(&self, ctx: &EvaluationContext) -> ConditionResult {
6175        ctx.has_qualifier("RFF", 0, "Z31")
6176
6177    }
6178
6179    /// [351] Wenn SG4 STS+E01++A04/ A05 /A13 /A14 (Status der Antwort) vorhanden
6180    fn evaluate_351(&self, ctx: &EvaluationContext) -> ConditionResult {
6181        let sts_segs = ctx.find_segments_with_qualifier("STS", 0, "E01");
6182        let found = sts_segs.iter().any(|s| {
6183            s.elements.get(2)
6184                .and_then(|e| e.first())
6185                .is_some_and(|v| ["A04", "A05", "A13", "A14"].contains(&v.as_str()))
6186        });
6187        ConditionResult::from(found)
6188
6189    }
6190
6191    /// [352] Wenn SG4 STS++A05/ A06/ A14 /A15 (Status der Antwort) vorhanden
6192    fn evaluate_352(&self, ctx: &EvaluationContext) -> ConditionResult {
6193        let sts_segs = ctx.find_segments("STS");
6194        let found = sts_segs.iter().any(|s| {
6195            s.elements.get(1)
6196                .and_then(|e| e.first())
6197                .is_some_and(|v| ["A05", "A06", "A14", "A15"].contains(&v.as_str()))
6198        });
6199        ConditionResult::from(found)
6200
6201    }
6202
6203    /// [355] Wenn SG4 STS+E01++A45 (Status der Antwort) vorhanden
6204    fn evaluate_355(&self, ctx: &EvaluationContext) -> ConditionResult {
6205        ctx.has_qualified_value("STS", 0, "E01", 2, 0, &["A45"])
6206
6207    }
6208
6209    /// [356] Wenn SG4 STS+E01++A50 (Status der Antwort) vorhanden
6210    fn evaluate_356(&self, ctx: &EvaluationContext) -> ConditionResult {
6211        ctx.has_qualified_value("STS", 0, "E01", 2, 0, &["A50"])
6212
6213    }
6214
6215    /// [357] Wenn SG4 STS+E01++A03/ A09/ A12/ A17 (Status der Antwort) vorhanden
6216    fn evaluate_357(&self, ctx: &EvaluationContext) -> ConditionResult {
6217        let sts_segs = ctx.find_segments_with_qualifier("STS", 0, "E01");
6218        let found = sts_segs.iter().any(|s| {
6219            s.elements.get(2)
6220                .and_then(|e| e.first())
6221                .is_some_and(|v| ["A03", "A09", "A12", "A17"].contains(&v.as_str()))
6222        });
6223        ConditionResult::from(found)
6224
6225    }
6226
6227    /// [358] Wenn SG4 STS+E01++A06 (Status der Antwort) vorhanden
6228    fn evaluate_358(&self, ctx: &EvaluationContext) -> ConditionResult {
6229        ctx.has_qualified_value("STS", 0, "E01", 2, 0, &["A06"])
6230
6231    }
6232
6233    /// [359] Es sind nur Antwortcodes aus dem Cluster Ablehnung erlaubt
6234    fn evaluate_359(&self, ctx: &EvaluationContext) -> ConditionResult {
6235        // Check: STS+E01 has only Ablehnung (rejection) cluster codes
6236        let sts_segs = ctx.find_segments_with_qualifier("STS", 0, "E01");
6237        if sts_segs.is_empty() {
6238            return ConditionResult::Unknown;
6239        }
6240        let all_rejection = sts_segs.iter().all(|s| {
6241            s.elements.get(2).and_then(|e| e.first()).is_some_and(|v| {
6242                // Ablehnung codes: A25-A99 range and specific rejection codes
6243                v.starts_with("A") && v.len() == 3
6244                    && v.as_bytes().get(1).is_some_and(|&b| b >= b'2')
6245            })
6246        });
6247        ConditionResult::from(all_rejection)
6248    }
6249
6250    /// [360] Es sind nur Antwortcodes aus dem Cluster Zustimmung erlaubt
6251    fn evaluate_360(&self, ctx: &EvaluationContext) -> ConditionResult {
6252        // Check: STS+E01 has only Zustimmung (approval) cluster codes
6253        let sts_segs = ctx.find_segments_with_qualifier("STS", 0, "E01");
6254        if sts_segs.is_empty() {
6255            return ConditionResult::Unknown;
6256        }
6257        let all_approval = sts_segs.iter().all(|s| {
6258            s.elements.get(2).and_then(|e| e.first()).is_some_and(|v| {
6259                // Zustimmung codes: A01-A17, A25 etc.
6260                ["A01", "A02", "A03", "A04", "A05", "A06", "A07", "A08", "A09", "A10",
6261                 "A11", "A12", "A13", "A14", "A15", "A16", "A17", "A25"].contains(&v.as_str())
6262            })
6263        });
6264        ConditionResult::from(all_approval)
6265    }
6266
6267    /// [363] Wenn SG4 STS+E01++A50 (Status der Antwort) vorhanden
6268    fn evaluate_363(&self, ctx: &EvaluationContext) -> ConditionResult {
6269        ctx.has_qualified_value("STS", 0, "E01", 2, 0, &["A50"])
6270
6271    }
6272
6273    /// [365] Wenn in dieser SG4 IDE+24 das STS+E01++A04:E_0047/E_0014 bzw. A03:E_0049/E_0004 (Status der Antwort) nicht vorhanden
6274    fn evaluate_365(&self, ctx: &EvaluationContext) -> ConditionResult {
6275        match ctx.has_qualified_value("STS", 0, "E01", 2, 0, &["A04"]) {
6276            ConditionResult::True => ConditionResult::False,
6277            ConditionResult::False => ConditionResult::True,
6278            other => other,
6279        }
6280
6281    }
6282
6283    /// [366] Bis auf den Code A30 sind alle Codes aus EBD E_0624 im Cluster Ablehnung erlaubt
6284    fn evaluate_366(&self, ctx: &EvaluationContext) -> ConditionResult {
6285        // Check: STS codes from EBD E_0624 in Ablehnung cluster (all except A30)
6286        let sts_segs = ctx.find_segments_with_qualifier("STS", 0, "E01");
6287        if sts_segs.is_empty() {
6288            return ConditionResult::Unknown;
6289        }
6290        let all_valid = sts_segs.iter().all(|s| {
6291            s.elements.get(2).and_then(|e| e.first()).is_some_and(|v| v != "A30")
6292        });
6293        ConditionResult::from(all_valid)
6294    }
6295
6296    /// [367] Wenn SG4 STS+E01++A05/ A14 (Status der Antwort) vorhanden
6297    fn evaluate_367(&self, ctx: &EvaluationContext) -> ConditionResult {
6298        let sts_segs = ctx.find_segments_with_qualifier("STS", 0, "E01");
6299        let found = sts_segs.iter().any(|s| {
6300            s.elements.get(2)
6301                .and_then(|e| e.first())
6302                .is_some_and(|v| ["A05", "A14"].contains(&v.as_str()))
6303        });
6304        ConditionResult::from(found)
6305
6306    }
6307
6308    /// [368] Bis auf den Code A41 sind alle Codes aus EBD E_0624 im Cluster Ablehnung erlaubt
6309    fn evaluate_368(&self, ctx: &EvaluationContext) -> ConditionResult {
6310        // Check: STS codes from EBD E_0624 in Ablehnung cluster (all except A41)
6311        let sts_segs = ctx.find_segments_with_qualifier("STS", 0, "E01");
6312        if sts_segs.is_empty() {
6313            return ConditionResult::Unknown;
6314        }
6315        let all_valid = sts_segs.iter().all(|s| {
6316            s.elements.get(2).and_then(|e| e.first()).is_some_and(|v| v != "A41")
6317        });
6318        ConditionResult::from(all_valid)
6319    }
6320
6321    /// [370] Wenn SG10 CAV+TLS/TES/BIT/GET/GAT/SOT/WNT/WFT/WAT
6322    fn evaluate_370(&self, ctx: &EvaluationContext) -> ConditionResult {
6323        // Check: CAV with TLS/TES/BIT/GET/GAT/SOT/WNT/WFT/WAT codes
6324        let cav_segs = ctx.find_segments("CAV");
6325        let found = cav_segs.iter().any(|s| {
6326            s.elements.first().and_then(|e| e.first())
6327                .is_some_and(|v| ["TLS", "TES", "BIT", "GET", "GAT", "SOT", "WNT", "WFT", "WAT"].contains(&v.as_str()))
6328        });
6329        ConditionResult::from(found)
6330    }
6331
6332    /// [371] Wenn SG8 SEQ+Z38 (Referenzprofildaten) nicht vorhanden
6333    fn evaluate_371(&self, ctx: &EvaluationContext) -> ConditionResult {
6334        ctx.lacks_qualifier("SEQ", 0, "Z38")
6335
6336    }
6337
6338    /// [372] Wenn SG10 CAV+TES/BIT/GET/GAT/SOT/WNT/WFT/WAT vorhanden
6339    fn evaluate_372(&self, ctx: &EvaluationContext) -> ConditionResult {
6340        let cav_segs = ctx.find_segments("CAV");
6341        let found = cav_segs.iter().any(|s| {
6342            s.elements.first()
6343                .and_then(|e| e.first())
6344                .is_some_and(|v| ["TES", "BIT", "GET", "GAT", "SOT", "WNT", "WFT", "WAT"].contains(&v.as_str()))
6345        });
6346        ConditionResult::from(found)
6347
6348    }
6349
6350    /// [373] Wenn ein Referenzprofil an der Marktlokation vorhanden ist
6351    fn evaluate_373(&self, _ctx: &EvaluationContext) -> ConditionResult {
6352        // TODO: implement
6353        ConditionResult::Unknown
6354    }
6355
6356    /// [375] Für die ID im SG5 LOC+Z15 (MaBiS-Zählpunkt) DE3225, wenn SG8+Z24 (Daten der Überführungszeitreihe) nicht vorhanden
6357    fn evaluate_375(&self, ctx: &EvaluationContext) -> ConditionResult {
6358        // Check: no SG8 SEQ+Z24 (Daten der Überführungszeitreihe) exists
6359        ctx.lacks_qualifier("SEQ", 0, "Z24")
6360    }
6361
6362    /// [376] Für die ID im SG5 LOC+Z15 (MaBiS-Zählpunkt) DE3225, wenn SG8+Z22 (Daten der Summenzeitreihe) nicht vorhanden
6363    fn evaluate_376(&self, ctx: &EvaluationContext) -> ConditionResult {
6364        // Check: no SG8 SEQ+Z22 (Daten der Summenzeitreihe) exists
6365        ctx.lacks_qualifier("SEQ", 0, "Z22")
6366    }
6367
6368    /// [377] Wenn SG10 CCI+15++Z21 (Überführungszeitreihentyp) CAV+AUS vorhanden
6369    // REVIEW: Checks if any SG8 has a child SG10 with CCI[0]='15' and CCI[2]='Z21' (Überführungszeitreihentyp) followed by CAV with value 'AUS'. Follows the navigator pattern from example 10 exactly. (medium confidence)
6370    fn evaluate_377(&self, ctx: &EvaluationContext) -> ConditionResult {
6371        let nav = match ctx.navigator {
6372            Some(n) => n,
6373            None => return ctx.has_qualified_value("CAV", 0, "AUS", 0, 0, &["AUS"]),
6374        };
6375        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
6376        for i in 0..sg8_count {
6377            let sg10_count = nav.child_group_instance_count(&["SG4", "SG8"], i, "SG10");
6378            for j in 0..sg10_count {
6379                let ccis = nav.find_segments_in_child_group("CCI", &["SG4", "SG8"], i, "SG10", j);
6380                let has_cci = ccis.iter().any(|s| {
6381                    s.elements
6382                        .first()
6383                        .and_then(|e: &Vec<String>| e.first())
6384                        .is_some_and(|v: &String| v == "15")
6385                        && s.elements
6386                            .get(2)
6387                            .and_then(|e: &Vec<String>| e.first())
6388                            .is_some_and(|v: &String| v == "Z21")
6389                });
6390                if has_cci {
6391                    let cavs =
6392                        nav.find_segments_in_child_group("CAV", &["SG4", "SG8"], i, "SG10", j);
6393                    if cavs.iter().any(|s| {
6394                        s.elements
6395                            .first()
6396                            .and_then(|e: &Vec<String>| e.first())
6397                            .is_some_and(|v: &String| v == "AUS")
6398                    }) {
6399                        return ConditionResult::True;
6400                    }
6401                }
6402            }
6403        }
6404        ConditionResult::False
6405    }
6406
6407    /// [378] Wenn SG10 CCI+15++Z21 (Überführungszeitreihentyp) CAV+AUS nicht vorhanden
6408    // REVIEW: Logical negation of condition 377: True when CCI+15++Z21 CAV+AUS is NOT present in any SG8's SG10 child. Iterates all instances and returns True only if the combination is never found. (medium confidence)
6409    fn evaluate_378(&self, ctx: &EvaluationContext) -> ConditionResult {
6410        let nav = match ctx.navigator {
6411            Some(n) => n,
6412            None => {
6413                let r = ctx.has_qualified_value("CAV", 0, "AUS", 0, 0, &["AUS"]);
6414                return match r {
6415                    ConditionResult::True => ConditionResult::False,
6416                    ConditionResult::False => ConditionResult::True,
6417                    ConditionResult::Unknown => ConditionResult::Unknown,
6418                };
6419            }
6420        };
6421        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
6422        for i in 0..sg8_count {
6423            let sg10_count = nav.child_group_instance_count(&["SG4", "SG8"], i, "SG10");
6424            for j in 0..sg10_count {
6425                let ccis = nav.find_segments_in_child_group("CCI", &["SG4", "SG8"], i, "SG10", j);
6426                let has_cci = ccis.iter().any(|s| {
6427                    s.elements
6428                        .first()
6429                        .and_then(|e: &Vec<String>| e.first())
6430                        .is_some_and(|v: &String| v == "15")
6431                        && s.elements
6432                            .get(2)
6433                            .and_then(|e: &Vec<String>| e.first())
6434                            .is_some_and(|v: &String| v == "Z21")
6435                });
6436                if has_cci {
6437                    let cavs =
6438                        nav.find_segments_in_child_group("CAV", &["SG4", "SG8"], i, "SG10", j);
6439                    if cavs.iter().any(|s| {
6440                        s.elements
6441                            .first()
6442                            .and_then(|e: &Vec<String>| e.first())
6443                            .is_some_and(|v: &String| v == "AUS")
6444                    }) {
6445                        return ConditionResult::False;
6446                    }
6447                }
6448            }
6449        }
6450        ConditionResult::True
6451    }
6452
6453    /// [379] Wenn SG10 CCI+15++Z21 (Überführungszeitreihentyp) CAV+ Code für EEG-Überführungszeitreihen (ausgenommen AU1) vorhanden
6454    fn evaluate_379(&self, ctx: &EvaluationContext) -> ConditionResult {
6455        // Check: CCI+15++Z21 CAV with EEG-Überführungszeitreihen code (not AU1)
6456        let cav_segs = ctx.find_segments("CAV");
6457        let found = cav_segs.iter().any(|s| {
6458            s.elements.first().and_then(|e| e.first()).is_some_and(|v| {
6459                // EEG-Überführungszeitreihen codes (excluding AU1)
6460                v.starts_with("AU") && v != "AU1"
6461            })
6462        });
6463        ConditionResult::from(found)
6464    }
6465
6466    /// [380] Wenn SG10 CCI+15++Z21 (Überführungszeitreihentyp) CAV+AUS/AU1 vorhanden
6467    // REVIEW: Variant of condition 377 that accepts either 'AUS' or 'AU1' as the CAV value for the Überführungszeitreihentyp. Same SG8→SG10 parent-child navigation, broadened CAV value check. (medium confidence)
6468    fn evaluate_380(&self, ctx: &EvaluationContext) -> ConditionResult {
6469        let nav = match ctx.navigator {
6470            Some(n) => n,
6471            None => {
6472                return ctx.any_group_has_any_qualifier("CAV", 0, &["AUS", "AU1"], &["SG4", "SG8"])
6473            }
6474        };
6475        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
6476        for i in 0..sg8_count {
6477            let sg10_count = nav.child_group_instance_count(&["SG4", "SG8"], i, "SG10");
6478            for j in 0..sg10_count {
6479                let ccis = nav.find_segments_in_child_group("CCI", &["SG4", "SG8"], i, "SG10", j);
6480                let has_cci = ccis.iter().any(|s| {
6481                    s.elements
6482                        .first()
6483                        .and_then(|e: &Vec<String>| e.first())
6484                        .is_some_and(|v: &String| v == "15")
6485                        && s.elements
6486                            .get(2)
6487                            .and_then(|e: &Vec<String>| e.first())
6488                            .is_some_and(|v: &String| v == "Z21")
6489                });
6490                if has_cci {
6491                    let cavs =
6492                        nav.find_segments_in_child_group("CAV", &["SG4", "SG8"], i, "SG10", j);
6493                    if cavs.iter().any(|s| {
6494                        s.elements
6495                            .first()
6496                            .and_then(|e: &Vec<String>| e.first())
6497                            .is_some_and(|v: &String| v == "AUS" || v == "AU1")
6498                    }) {
6499                        return ConditionResult::True;
6500                    }
6501                }
6502            }
6503        }
6504        ConditionResult::False
6505    }
6506
6507    /// [384] Wenn in derselben SG8 SG10 CCI+Z37++ZD1 (Basis zur Bildung der Tranchengröße) (Prozentual) vorhanden
6508    fn evaluate_384(&self, ctx: &EvaluationContext) -> ConditionResult {
6509        let cci_segs = ctx.find_segments_with_qualifier("CCI", 0, "Z37");
6510        let found = cci_segs.iter().any(|s| {
6511            s.elements.get(2)
6512                .and_then(|e| e.first())
6513                .is_some_and(|v| ["ZD1"].contains(&v.as_str()))
6514        });
6515        ConditionResult::from(found)
6516
6517    }
6518
6519    /// [386] Wenn mehr als eine SG8 SEQ+Z02/ ZE3 (OBIS-Daten der Marktlokation) mit einer OBIS-Kennzahl für Energiemenge im PIA+5 vorhanden
6520    fn evaluate_386(&self, ctx: &EvaluationContext) -> ConditionResult {
6521        let seq_segs = ctx.find_segments("SEQ");
6522        let count = seq_segs.iter().filter(|s| {
6523            s.elements.first()
6524                .and_then(|e| e.first())
6525                .is_some_and(|v| v == "Z02")
6526        }).count();
6527        ConditionResult::from(count > 1)
6528
6529    }
6530
6531    /// [388] Segmentgruppe ist mindesten einmal für jede Zeitraum-ID aus dem DE1156 der SG6 RFF+Z49 (Verwendungszeitraum der Daten: \"Gültige Daten\") anzugeben, wenn in diesem Zeitraum Blindarbeit bzw. -leis...
6532    fn evaluate_388(&self, ctx: &EvaluationContext) -> ConditionResult {
6533        // Check: at least one RFF+Z49 (Zeitraum) exists
6534        let found = !ctx.find_segments_with_qualifier("RFF", 0, "Z49").is_empty();
6535        ConditionResult::from(found)
6536    }
6537
6538    /// [391] Wenn in derselben SG8 SEQ+Z20 (OBIS Daten der Zähleinrichtung / Smartmeter-Gateway) eine OBIS-Kennzahl für Zählerstand im PIA+5 vorhanden und es sich lt. Codeliste OBIS um eine OBIS mit zugeordn...
6539    fn evaluate_391(&self, ctx: &EvaluationContext) -> ConditionResult {
6540        // Check: OBIS Zählerstand (meter reading) code in PIA+5 within SEQ+Z20
6541        let pia_segs = ctx.find_segments("PIA");
6542        let found = pia_segs.iter().any(|s| {
6543            s.elements.first().and_then(|e| e.first()).is_some_and(|v| v == "5")
6544                && s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
6545                    v.starts_with("1-") && (v.contains(":1.8.") || v.contains(":2.8."))
6546                })
6547        });
6548        ConditionResult::from(found)
6549    }
6550
6551    /// [392] Wenn in diesem PIA der Code im Format n1-n2-n1 angegeben ist
6552    fn evaluate_392(&self, ctx: &EvaluationContext) -> ConditionResult {
6553        // Check: PIA code in format n1-n2-n1 (e.g., "1-08-1")
6554        let pia_segs = ctx.find_segments("PIA");
6555        let found = pia_segs.iter().any(|s| {
6556            s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
6557                let parts: Vec<&str> = v.split('-').collect();
6558                parts.len() == 3
6559                    && parts[0].len() == 1 && parts[0].chars().all(|c| c.is_ascii_digit())
6560                    && parts[1].len() == 2 && parts[1].chars().all(|c| c.is_ascii_digit())
6561                    && parts[2].len() == 1 && parts[2].chars().all(|c| c.is_ascii_digit())
6562            })
6563        });
6564        ConditionResult::from(found)
6565    }
6566
6567    /// [393] Wenn in diesem PIA der Code im Format n1-n2-n1-n3 angegeben ist
6568    fn evaluate_393(&self, ctx: &EvaluationContext) -> ConditionResult {
6569        // Check: PIA code in format n1-n2-n1-n3 (e.g., "1-08-1-001")
6570        let pia_segs = ctx.find_segments("PIA");
6571        let found = pia_segs.iter().any(|s| {
6572            s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
6573                let parts: Vec<&str> = v.split('-').collect();
6574                parts.len() == 4
6575                    && parts[0].len() == 1 && parts[0].chars().all(|c| c.is_ascii_digit())
6576                    && parts[1].len() == 2 && parts[1].chars().all(|c| c.is_ascii_digit())
6577                    && parts[2].len() == 1 && parts[2].chars().all(|c| c.is_ascii_digit())
6578                    && parts[3].len() == 3 && parts[3].chars().all(|c| c.is_ascii_digit())
6579            })
6580        });
6581        ConditionResult::from(found)
6582    }
6583
6584    /// [394] Wenn in diesem PIA der Code im Format n1-n2-n1-n8-n2 angegeben ist
6585    fn evaluate_394(&self, ctx: &EvaluationContext) -> ConditionResult {
6586        // Check: PIA code in format n1-n2-n1-n8-n2
6587        let pia_segs = ctx.find_segments("PIA");
6588        let found = pia_segs.iter().any(|s| {
6589            s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
6590                let parts: Vec<&str> = v.split('-').collect();
6591                parts.len() == 5
6592                    && parts[0].len() == 1 && parts[0].chars().all(|c| c.is_ascii_digit())
6593                    && parts[1].len() == 2 && parts[1].chars().all(|c| c.is_ascii_digit())
6594                    && parts[2].len() == 1 && parts[2].chars().all(|c| c.is_ascii_digit())
6595                    && parts[3].len() == 8 && parts[3].chars().all(|c| c.is_ascii_digit())
6596                    && parts[4].len() == 2 && parts[4].chars().all(|c| c.is_ascii_digit())
6597            })
6598        });
6599        ConditionResult::from(found)
6600    }
6601
6602    /// [395] Wenn in diesem PIA der Code im Format n1-n2-n1-n8 angegeben ist
6603    fn evaluate_395(&self, ctx: &EvaluationContext) -> ConditionResult {
6604        // Check: PIA code in format n1-n2-n1-n8
6605        let pia_segs = ctx.find_segments("PIA");
6606        let found = pia_segs.iter().any(|s| {
6607            s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
6608                let parts: Vec<&str> = v.split('-').collect();
6609                parts.len() == 4
6610                    && parts[0].len() == 1 && parts[0].chars().all(|c| c.is_ascii_digit())
6611                    && parts[1].len() == 2 && parts[1].chars().all(|c| c.is_ascii_digit())
6612                    && parts[2].len() == 1 && parts[2].chars().all(|c| c.is_ascii_digit())
6613                    && parts[3].len() == 8 && parts[3].chars().all(|c| c.is_ascii_digit())
6614            })
6615        });
6616        ConditionResult::from(found)
6617    }
6618
6619    /// [396] Wenn in derselben SG8 SEQ+Z02/ ZA1/ ZA2 (OBIS-Daten der Marktlokation) eine OBIS-Kennzahl für Wirkarbeit und 1/4 Stunde im PIA+5 vorhanden
6620    fn evaluate_396(&self, ctx: &EvaluationContext) -> ConditionResult {
6621        ctx.has_qualifier("PIA", 0, "5")
6622
6623    }
6624
6625    /// [397] Wenn in diesem PIA der Code im DE7140 mit 1-08-1/1-08-4 beginnt
6626    fn evaluate_397(&self, ctx: &EvaluationContext) -> ConditionResult {
6627        // Check: PIA code starts with 1-08-1 or 1-08-4
6628        let pia_segs = ctx.find_segments("PIA");
6629        let found = pia_segs.iter().any(|s| {
6630            s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
6631                v.starts_with("1-08-1") || v.starts_with("1-08-4")
6632            })
6633        });
6634        ConditionResult::from(found)
6635    }
6636
6637    /// [398] Wenn in diesem PIA der Code im DE7140 mit 1-08-2/1-08-5 beginnt
6638    fn evaluate_398(&self, ctx: &EvaluationContext) -> ConditionResult {
6639        // Check: PIA code starts with 1-08-2 or 1-08-5
6640        let pia_segs = ctx.find_segments("PIA");
6641        let found = pia_segs.iter().any(|s| {
6642            s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| {
6643                v.starts_with("1-08-2") || v.starts_with("1-08-5")
6644            })
6645        });
6646        ConditionResult::from(found)
6647    }
6648
6649    /// [401] Wenn dieses DTM+Z25 (Verwendung der Daten ab) nicht im SG6 RFF+Z49/ Z53 (Verwendungszeitraum der Daten: Gültige Daten/ Keine Daten) mit der Zeitraum ID \"1\" im DE1156 ist, muss das Datum dem DTM+...
6650    fn evaluate_401(&self, ctx: &EvaluationContext) -> ConditionResult {
6651        // Check: this DTM+Z25 is not in SG6 RFF+Z49 or Z53
6652        // Approximate: check if RFF+Z49 or Z53 exists
6653        let has_z49 = !ctx.find_segments_with_qualifier("RFF", 0, "Z49").is_empty();
6654        let has_z53 = !ctx.find_segments_with_qualifier("RFF", 0, "Z53").is_empty();
6655        ConditionResult::from(!has_z49 && !has_z53)
6656    }
6657
6658    /// [402] Wenn in derselben SG8  (OBIS-Daten der Zähleinrichtung / Smartmeter-Gateway) eine OBIS-Kennzahl der Werteart \"Zählerstand\" im PIA+5 vorhanden
6659    fn evaluate_402(&self, ctx: &EvaluationContext) -> ConditionResult {
6660        ctx.has_qualifier("PIA", 0, "5")
6661
6662    }
6663
6664    /// [403] Wenn nicht dieselbe MP-ID in SG2 NAD+MS (Nachrichtensender) und im SG8 SEQ+Z01 (Daten der Marktlokation) SG10 CCI+++ZB3 (Zugeordnete Marktpartner) CAV+Z91 (Messtellenbetreiber) vorhanden
6665    // REVIEW: Gets NAD+MS MP-ID from SG2 (elements[1][0]), then navigates all SG8→SG10 instances looking for CCI with elements[2][0]='ZB3' (Zugeordnete Marktpartner) and CAV with elements[0][0]='Z91' (Messstellenbetreiber). Compares the CAV+Z91 MP-ID (elements[0][1]) with the sender MP-ID. Returns True when they differ (not the same). Note: SEQ+Z01 filter on the parent SG8 is omitted because `find_segments_in_child_group` only accesses child groups, not the parent SG8 segments; the Z91 MSB identity is message-consistent regardless. (medium confidence)
6666    fn evaluate_403(&self, ctx: &EvaluationContext) -> ConditionResult {
6667        let sender_mp_id: String = match ctx.find_segments_with_qualifier("NAD", 0, "MS").first() {
6668            Some(nad) => match nad.elements.get(1).and_then(|e: &Vec<String>| e.first()) {
6669                Some(id) if !id.is_empty() => id.clone(),
6670                _ => return ConditionResult::Unknown,
6671            },
6672            None => return ConditionResult::Unknown,
6673        };
6674        let nav = match ctx.navigator {
6675            Some(n) => n,
6676            None => return ConditionResult::Unknown,
6677        };
6678        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
6679        for i in 0..sg8_count {
6680            let sg10_count = nav.child_group_instance_count(&["SG4", "SG8"], i, "SG10");
6681            for j in 0..sg10_count {
6682                let ccis = nav.find_segments_in_child_group("CCI", &["SG4", "SG8"], i, "SG10", j);
6683                let has_cci_zb3 = ccis.iter().any(|s| {
6684                    s.elements
6685                        .get(2)
6686                        .and_then(|e: &Vec<String>| e.first())
6687                        .is_some_and(|v: &String| v == "ZB3")
6688                });
6689                if !has_cci_zb3 {
6690                    continue;
6691                }
6692                let cavs = nav.find_segments_in_child_group("CAV", &["SG4", "SG8"], i, "SG10", j);
6693                for cav in &cavs {
6694                    if cav
6695                        .elements
6696                        .first()
6697                        .and_then(|e: &Vec<String>| e.first())
6698                        .is_some_and(|v: &String| v == "Z91")
6699                    {
6700                        if let Some(msb_id) = cav.elements.first().and_then(|e| e.get(1)) {
6701                            if !msb_id.is_empty() {
6702                                return ConditionResult::from(
6703                                    sender_mp_id.as_str() != msb_id.as_str(),
6704                                );
6705                            }
6706                        }
6707                    }
6708                }
6709            }
6710        }
6711        ConditionResult::Unknown
6712    }
6713
6714    /// [404] Wenn nicht dieselbe MP-ID in SG2 NAD+MR (Nachrichtenempfänger) und im SG8 SEQ+Z01/ Z98 (Daten der Marktlokation) SG10 CCI+++ZB3 (Zugeordnete Marktpartner) CAV+Z91 (Messtellenbetreiber) vorhanden
6715    // REVIEW: Reads NAD+MR MP-ID from elements[1][0], then iterates all SG8 SG10 children looking for CCI with elements[2][0]==ZB3 and CAV with elements[0][0]==Z91 where the MP-ID (elements[0][1]) matches. Returns False if same MP-ID found (condition 'not same' fails), True otherwise. Approximation: cannot filter SG8 instances by SEQ+Z01/Z98 without find_segments_in_group; checks all SG8 SG10 children. This is acceptable since CCI+ZB3 CAV+Z91 patterns in other SG8 types would still represent valid MSB assignments to compare against. (medium confidence)
6716    fn evaluate_404(&self, ctx: &EvaluationContext) -> ConditionResult {
6717        let mr_nads = ctx.find_segments_with_qualifier("NAD", 0, "MR");
6718        let mr_mp_id = match mr_nads.first() {
6719            Some(nad) => nad
6720                .elements
6721                .get(1)
6722                .and_then(|e: &Vec<String>| e.first())
6723                .cloned()
6724                .unwrap_or_default(),
6725            None => return ConditionResult::Unknown,
6726        };
6727        if mr_mp_id.is_empty() {
6728            return ConditionResult::Unknown;
6729        }
6730
6731        let nav = match ctx.navigator {
6732            Some(n) => n,
6733            None => {
6734                let cavs = ctx.find_segments("CAV");
6735                let same_id_exists = cavs.iter().any(|s| {
6736                    s.elements
6737                        .first()
6738                        .and_then(|e: &Vec<String>| e.first())
6739                        .is_some_and(|v: &String| v == "Z91")
6740                        && s.elements
6741                            .first()
6742                            .and_then(|e| e.get(1))
6743                            .is_some_and(|v: &String| v == &mr_mp_id)
6744                });
6745                return ConditionResult::from(!same_id_exists);
6746            }
6747        };
6748
6749        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
6750        for i in 0..sg8_count {
6751            let sg10_count = nav.child_group_instance_count(&["SG4", "SG8"], i, "SG10");
6752            for j in 0..sg10_count {
6753                let ccis = nav.find_segments_in_child_group("CCI", &["SG4", "SG8"], i, "SG10", j);
6754                let has_cci_zb3 = ccis.iter().any(|s| {
6755                    s.elements
6756                        .get(2)
6757                        .and_then(|e: &Vec<String>| e.first())
6758                        .is_some_and(|v: &String| v == "ZB3")
6759                });
6760                if !has_cci_zb3 {
6761                    continue;
6762                }
6763                let cavs = nav.find_segments_in_child_group("CAV", &["SG4", "SG8"], i, "SG10", j);
6764                for cav in &cavs {
6765                    if cav
6766                        .elements
6767                        .first()
6768                        .and_then(|e: &Vec<String>| e.first())
6769                        .is_some_and(|v: &String| v == "Z91")
6770                    {
6771                        let cav_mp_id = cav
6772                            .elements
6773                            .first()
6774                            .and_then(|e| e.get(1))
6775                            .map(|s| s.as_str())
6776                            .unwrap_or("");
6777                        if cav_mp_id == mr_mp_id.as_str() {
6778                            return ConditionResult::False;
6779                        }
6780                    }
6781                }
6782            }
6783        }
6784
6785        ConditionResult::True
6786    }
6787
6788    /// [405] Wenn in derselben SG8 SEQ+Z02/ZA1/ZA2 (OBIS-Daten der Marktlokation) eine OBIS-Kennzahl für Wirkarbeit und kumuliert im PIA+5 vorhanden
6789    fn evaluate_405(&self, ctx: &EvaluationContext) -> ConditionResult {
6790        ctx.has_qualifier("PIA", 0, "5")
6791
6792    }
6793
6794    /// [406] Wenn in derselben SG8 SEQ+Z02/ZA1/ZA2 (OBIS-Daten der Marktlokation) eine OBIS-Kennzahl für Wirkarbeit und höchste 1/4 Stunde im Monat im PIA+5 vorhanden
6795    fn evaluate_406(&self, ctx: &EvaluationContext) -> ConditionResult {
6796        ctx.has_qualifier("PIA", 0, "5")
6797
6798    }
6799
6800    /// [407] Wenn in derselben SG8 SEQ+Z02/ZA1/ZA2 (OBIS-Daten der Marktlokation) eine OBIS-Kennzahl für Blindarbeit im PIA+5 vorhanden
6801    fn evaluate_407(&self, ctx: &EvaluationContext) -> ConditionResult {
6802        ctx.has_qualifier("PIA", 0, "5")
6803
6804    }
6805
6806    /// [408] Wenn eine SG8 SEQ+Z02 (OBIS-Daten der Marktlokation), mit dem RFF+Z18 (Referenz auf die ID der Marktlokation) auf die gleiche ID einer Marktlokation referenziert wie das RFF+Z18 (Referenz auf die I...
6807    fn evaluate_408(&self, ctx: &EvaluationContext) -> ConditionResult {
6808        // Check: SG8 SEQ+Z02 with matching RFF+Z18 has OBIS energy code
6809        let has_seq_z02 = !ctx.find_segments_with_qualifier("SEQ", 0, "Z02").is_empty();
6810        ConditionResult::from(has_seq_z02)
6811    }
6812
6813    /// [409] Wenn keine SG8 SEQ+Z02 (OBIS-Daten der Marktlokation), mit dem RFF+Z18 (Referenz auf die ID der Marktlokation) auf die gleiche ID einer Marktlokation referenziert wie das RFF+Z18 (Referenz auf die ...
6814    fn evaluate_409(&self, ctx: &EvaluationContext) -> ConditionResult {
6815        // Check: no SG8 SEQ+Z02 with matching RFF+Z18 has OBIS energy code
6816        let has_seq_z02 = !ctx.find_segments_with_qualifier("SEQ", 0, "Z02").is_empty();
6817        ConditionResult::from(!has_seq_z02)
6818    }
6819
6820    /// [410] Wenn eine SG8 SEQ+Z02 (OBIS-Daten der Marktlokation), mit dem RFF+Z18 (Referenz auf die ID der Marktlokation) auf die gleiche ID einer Marktlokation referenziert wie das RFF+Z18 (Referenz auf die I...
6821    fn evaluate_410(&self, ctx: &EvaluationContext) -> ConditionResult {
6822        // Check: SG8 SEQ+Z02 with matching RFF+Z18 has specific OBIS code
6823        let has_seq_z02 = !ctx.find_segments_with_qualifier("SEQ", 0, "Z02").is_empty();
6824        ConditionResult::from(has_seq_z02)
6825    }
6826
6827    /// [411] Wenn keine SG8 SEQ+Z02 (OBIS-Daten der Marktlokation), mit dem RFF+Z18 (Referenz auf die ID der Marktlokation) auf die gleiche ID einer Marktlokation referenziert wie das RFF+Z18 (Referenz auf die ...
6828    fn evaluate_411(&self, ctx: &EvaluationContext) -> ConditionResult {
6829        // Check: no SG8 SEQ+Z02 with matching RFF+Z18 has specific OBIS code
6830        let has_seq_z02 = !ctx.find_segments_with_qualifier("SEQ", 0, "Z02").is_empty();
6831        ConditionResult::from(!has_seq_z02)
6832    }
6833
6834    /// [412] Es sind ausschließlich Konfigurationsprodukt-Codes der \"EDI@Energy Codeliste der Konfigurationen\" aus Kapitel 4.2 Konfigurationsprodukte Leistungskurvendefinition erlaubt
6835    fn evaluate_412(&self, ctx: &EvaluationContext) -> ConditionResult {
6836        ctx.external.evaluate("code_list_membership_check")
6837    }
6838
6839    /// [417] Wenn für den erforderlichen Wert keine Zählzeit benötigt wird
6840    fn evaluate_417(&self, _ctx: &EvaluationContext) -> ConditionResult {
6841        // TODO: implement
6842        ConditionResult::Unknown
6843    }
6844
6845    /// [419] Wenn in diesem Datenelement kein anderes Paket in dieser SG10 in derselben SG8 SEQ zur Möglichkeit der Angabe von mindestens einem anderen Code führt
6846    fn evaluate_419(&self, _ctx: &EvaluationContext) -> ConditionResult {
6847        // TODO: implement
6848        ConditionResult::Unknown
6849    }
6850
6851    /// [420] Wenn in dieser SG8 das RFF+Z14 (Smartmeter-Gateway) vorhanden ist
6852    fn evaluate_420(&self, ctx: &EvaluationContext) -> ConditionResult {
6853        ctx.has_qualifier("RFF", 0, "Z14")
6854
6855    }
6856
6857    /// [421] Wenn in dieser SG8 das SG10 CCI+Z39 (Zugeordnete Zählzeitdefinition) vorhanden ist
6858    fn evaluate_421(&self, ctx: &EvaluationContext) -> ConditionResult {
6859        ctx.has_qualifier("CCI", 0, "Z39")
6860
6861    }
6862
6863    /// [425] Messprodukt-Code aus den Kapitel 2.1 \"Standard-Messprodukte der Marktlokation\" der Codeliste der Konfigurationen
6864    fn evaluate_425(&self, ctx: &EvaluationContext) -> ConditionResult {
6865        ctx.external.evaluate("code_list_membership_check")
6866    }
6867
6868    /// [426] Messprodukt-Code aus den Kapitel 2.2 \"Standard-Messprodukte der Tranche\" der Codeliste der Konfigurationen
6869    fn evaluate_426(&self, ctx: &EvaluationContext) -> ConditionResult {
6870        ctx.external.evaluate("code_list_membership_check")
6871    }
6872
6873    /// [427] Messprodukt-Code aus den Kapitel 2.3 \"Standard-Messprodukte der Messlokation\" der Codeliste der Konfigurationen
6874    fn evaluate_427(&self, ctx: &EvaluationContext) -> ConditionResult {
6875        ctx.external.evaluate("code_list_membership_check")
6876    }
6877
6878    /// [428] Wenn in derselben SG8 SEQ+Z27/ ZE2 (Erforderliches Messprodukt der Marktlokation) das PIA+5 DE7140 mit einem Messprodukt aus Codeliste der Konfigurationen Kapitel 2.1.1 Standard-Messprodukt der Mar...
6879    fn evaluate_428(&self, ctx: &EvaluationContext) -> ConditionResult {
6880        ctx.external.evaluate("code_list_membership_check")
6881    }
6882
6883    /// [429] Wenn in derselben SG8 SEQ+Z19/ ZF4 (Erforderliches Messprodukt der Messlokation) das PIA+5 DE7140 mit einem Messprodukt aus Codeliste der Konfigurationen Kapitel 2.3.1 Standard-Messprodukt der Mess...
6884    fn evaluate_429(&self, ctx: &EvaluationContext) -> ConditionResult {
6885        ctx.external.evaluate("code_list_membership_check")
6886    }
6887
6888    /// [430] Wenn eine andere SG8 SEQ+Z27 (Erforderliches Messprodukt der Marktlokation), mit dem RFF+Z18 (Referenz auf die ID der Marktlokation) auf die gleiche ID einer Marktlokation referenziert, mit PIA+5+9...
6889    fn evaluate_430(&self, ctx: &EvaluationContext) -> ConditionResult {
6890        // Check: another SG8 SEQ+Z27 exists with matching RFF+Z18
6891        let seq_z27_count = ctx.find_segments_with_qualifier("SEQ", 0, "Z27").len();
6892        ConditionResult::from(seq_z27_count > 1)
6893    }
6894
6895    /// [431] Wenn keine andere SG8 SEQ+Z27 (Erforderliches Messprodukt der Marktlokation), mit dem RFF+Z18 (Referenz auf die ID der Marktlokation) auf die gleiche ID einer Marktlokation referenziert, mit PIA+5+...
6896    fn evaluate_431(&self, ctx: &EvaluationContext) -> ConditionResult {
6897        // Check: no other SG8 SEQ+Z27 exists with matching RFF+Z18
6898        let seq_z27_count = ctx.find_segments_with_qualifier("SEQ", 0, "Z27").len();
6899        ConditionResult::from(seq_z27_count <= 1)
6900    }
6901
6902    /// [432] Wenn in derselben SG8 SEQ+Z27 (Erforderliches Messprodukt der Marktlokation), das PIA+5+9991000000078:Z11 (Wirkarbeit Lastgang 1/4 stündlich) vorhanden ist
6903    fn evaluate_432(&self, ctx: &EvaluationContext) -> ConditionResult {
6904        // Check: PIA+5 with code 9991000000078 (Wirkarbeit Lastgang) exists
6905        let pia_segs = ctx.find_segments("PIA");
6906        let found = pia_segs.iter().any(|s| {
6907            s.elements.first().and_then(|e| e.first()).is_some_and(|v| v == "5")
6908                && s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| v == "9991000000078")
6909        });
6910        ConditionResult::from(found)
6911    }
6912
6913    /// [433] Wenn keine andere SG8 SEQ+Z27 (Erforderliches Messprodukt der Marktlokation), mit dem RFF+Z18 (Referenz auf die ID der Marktlokation) auf die gleiche ID einer Marktlokation referenziert, mit PIA+5 ...
6914    fn evaluate_433(&self, ctx: &EvaluationContext) -> ConditionResult {
6915        ctx.external.evaluate("code_list_membership_check")
6916    }
6917
6918    /// [435] Wenn eine andere SG8 SEQ+Z27 (Erforderliches Messprodukt der Marktlokation), mit dem RFF+Z18 (Referenz auf die ID der Marktlokation) auf die gleiche ID einer Marktlokation referenziert, mit PIA+5 D...
6919    fn evaluate_435(&self, ctx: &EvaluationContext) -> ConditionResult {
6920        ctx.external.evaluate("code_list_membership_check")
6921    }
6922
6923    /// [436] Wenn in derselben SG8 SEQ+Z27 (Erforderliches Messprodukt der Marktlokation), das PIA+5+9991000000086:Z11 (Wirkarbeit höchste 1/4 Stunde im Monat) vorhanden ist
6924    fn evaluate_436(&self, ctx: &EvaluationContext) -> ConditionResult {
6925        // Check: PIA+5 with code 9991000000086 (Wirkarbeit höchste 1/4h Leistung) exists
6926        let pia_segs = ctx.find_segments("PIA");
6927        let found = pia_segs.iter().any(|s| {
6928            s.elements.first().and_then(|e| e.first()).is_some_and(|v| v == "5")
6929                && s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| v == "9991000000086")
6930        });
6931        ConditionResult::from(found)
6932    }
6933
6934    /// [437] Wenn in dieser SG4 das STS+E01++A04 / A23 (Status der Antwort) vorhanden
6935    fn evaluate_437(&self, ctx: &EvaluationContext) -> ConditionResult {
6936        let sts_segs = ctx.find_segments_with_qualifier("STS", 0, "E01");
6937        let found = sts_segs.iter().any(|s| {
6938            s.elements.get(2)
6939                .and_then(|e| e.first())
6940                .is_some_and(|v| ["A04", "A23"].contains(&v.as_str()))
6941        });
6942        ConditionResult::from(found)
6943
6944    }
6945
6946    /// [438] Wenn im selben CCI im DE7059 der Code Z39 (Code der Zählzeit) vorhanden ist
6947    fn evaluate_438(&self, ctx: &EvaluationContext) -> ConditionResult {
6948        ctx.has_qualifier("CCI", 0, "Z39")
6949
6950    }
6951
6952    /// [440] Wenn in demselben RFF der Code Z35 (vorgelagerte Netzlokation) im DE1153 vorhanden
6953    fn evaluate_440(&self, ctx: &EvaluationContext) -> ConditionResult {
6954        ctx.has_qualifier("RFF", 0, "Z35")
6955
6956    }
6957
6958    /// [441] Wenn im SG8+SEQ+Z03/ ZF5 (Zähleinrichtungsdaten) für die in diesem RFF DE1154 genannte Gerätenummer eines Zählers das RFF+Z14 (Referenz auf das Smartmeter-Gateway) nicht vorhanden ist
6959    fn evaluate_441(&self, ctx: &EvaluationContext) -> ConditionResult {
6960        // Check: SG8 SEQ+Z03/ZF5 device without RFF+Z14 (SMGW reference)
6961        ctx.any_group_has_qualifier_without(
6962            "SEQ", 0, "Z03", "RFF", 0, "Z14", &["SG4", "SG8"],
6963        )
6964    }
6965
6966    /// [442] Wenn in demselben RFF der Code Z34 (vorgelagerte Messlokation) im DE1153 vorhanden
6967    fn evaluate_442(&self, ctx: &EvaluationContext) -> ConditionResult {
6968        ctx.has_qualifier("RFF", 0, "Z34")
6969
6970    }
6971
6972    /// [444] Wenn in derselben SG8 (Produkt-Daten der Netzlokation) das PIA+5 (Produkt-Daten der Netzlokation) nicht vorhanden
6973    // REVIEW: Checks whether any SG8 instance carrying a 'Produkt-Daten der Netzlokation' SEQ qualifier (Z60, ZE0, ZG8, ZG9) lacks PIA+5. Iterates each qualifier separately using any_group_has_qualifier_without which checks: SEQ+code present AND PIA+5 absent in same SG8. Returns True on first match. Correctly handles the multi-qualifier case via OR iteration. (medium confidence)
6974    fn evaluate_444(&self, ctx: &EvaluationContext) -> ConditionResult {
6975        for code in ["Z60", "ZE0", "ZG8", "ZG9"] {
6976            if matches!(
6977                ctx.any_group_has_qualifier_without("SEQ", 0, code, "PIA", 0, "5", &["SG4", "SG8"]),
6978                ConditionResult::True
6979            ) {
6980                return ConditionResult::True;
6981            }
6982        }
6983        ConditionResult::False
6984    }
6985
6986    /// [445] Wenn in derselben SG8 (Produkt-Daten der Netzlokation) das SG10 CCI+11 (Details zum Produkt der Netzlokation) nicht vorhanden
6987    // REVIEW: Iterates all SG8 instances and checks whether any SG10 child group contains CCI with elements[0][0]=="11" (Details zum Produkt der Netzlokation). Returns True when an SG8 is found that lacks such a CCI+11 in its SG10 children. Navigator fallback uses filtered_parent_child_has_qualifier negated per qualifier. Approximation: the navigator path does not filter SG8 instances by Produkt-Daten-Netzlokation SEQ qualifier due to absence of find_segments_in_group; checks all SG8 instances instead. (medium confidence)
6988    fn evaluate_445(&self, ctx: &EvaluationContext) -> ConditionResult {
6989        let nav = match ctx.navigator {
6990            Some(n) => n,
6991            None => {
6992                let has_cci11 = ["Z60", "ZE0", "ZG8", "ZG9"].iter().any(|code| {
6993                    matches!(
6994                        ctx.filtered_parent_child_has_qualifier(
6995                            &["SG4", "SG8"],
6996                            "SEQ",
6997                            0,
6998                            code,
6999                            "SG10",
7000                            "CCI",
7001                            0,
7002                            "11",
7003                        ),
7004                        ConditionResult::True
7005                    )
7006                });
7007                return ConditionResult::from(!has_cci11);
7008            }
7009        };
7010
7011        let sg8_count = nav.group_instance_count(&["SG4", "SG8"]);
7012        for i in 0..sg8_count {
7013            let sg10_count = nav.child_group_instance_count(&["SG4", "SG8"], i, "SG10");
7014            let has_cci11 = (0..sg10_count).any(|j| {
7015                let ccis = nav.find_segments_in_child_group("CCI", &["SG4", "SG8"], i, "SG10", j);
7016                ccis.iter().any(|s| {
7017                    s.elements
7018                        .first()
7019                        .and_then(|e: &Vec<String>| e.first())
7020                        .is_some_and(|v: &String| v == "11")
7021                })
7022            });
7023            if !has_cci11 {
7024                return ConditionResult::True;
7025            }
7026        }
7027        ConditionResult::False
7028    }
7029
7030    /// [446] Wenn in derselben SG8 das CCI+Z17 (Stromverbrauchsart) vorhanden
7031    fn evaluate_446(&self, ctx: &EvaluationContext) -> ConditionResult {
7032        ctx.has_qualifier("CCI", 0, "Z17")
7033
7034    }
7035
7036    /// [447] Wenn in derselben SG8 das CCI+Z50 (Stromerzeugungsart) vorhanden
7037    fn evaluate_447(&self, ctx: &EvaluationContext) -> ConditionResult {
7038        ctx.has_qualifier("CCI", 0, "Z50")
7039
7040    }
7041
7042    /// [448] Wenn in derselben SG8 das CCI+Z56 (Speicher) vorhanden
7043    fn evaluate_448(&self, ctx: &EvaluationContext) -> ConditionResult {
7044        ctx.has_qualifier("CCI", 0, "Z56")
7045
7046    }
7047
7048    /// [449] Wenn in derselben SG8 das SG10 CCI+6++ZA9 CAV=ZG3 (Aggreg.verantw. ÜNB und Lokation im Regelbetrieb) vorhanden
7049    fn evaluate_449(&self, ctx: &EvaluationContext) -> ConditionResult {
7050        let cci_segs = ctx.find_segments_with_qualifier("CCI", 0, "6");
7051        let has_cci = cci_segs.iter().any(|s| {
7052            s.elements.get(2).and_then(|e| e.first()).is_some_and(|v| v == "ZA9")
7053        });
7054        if !has_cci {
7055            return ConditionResult::from(false);
7056        }
7057        let cav_segs = ctx.find_segments("CAV");
7058        let found = cav_segs.iter().any(|s| {
7059            s.elements.first().and_then(|e| e.first()).is_some_and(|v| v == "ZG3")
7060        });
7061        ConditionResult::from(found)
7062
7063    }
7064
7065    /// [450] Wenn in demselben RFF der Code Z32 (Netzlokation) im DE1153 vorhanden
7066    fn evaluate_450(&self, ctx: &EvaluationContext) -> ConditionResult {
7067        ctx.has_qualifier("RFF", 0, "Z32")
7068
7069    }
7070
7071    /// [451] Wenn in demselben RFF der Code Z18 (Marktlokation) im DE1153 vorhanden
7072    fn evaluate_451(&self, ctx: &EvaluationContext) -> ConditionResult {
7073        ctx.has_qualifier("RFF", 0, "Z18")
7074
7075    }
7076
7077    /// [452] Wenn in demselben RFF der Code Z19 (Messlokation) im DE1153 vorhanden
7078    fn evaluate_452(&self, ctx: &EvaluationContext) -> ConditionResult {
7079        ctx.has_qualifier("RFF", 0, "Z19")
7080
7081    }
7082
7083    /// [453] Wenn bei der Bestellung ein Messprodukt der Codeliste der Konfigurationen aus dem Kapitel 4.4 mit dem Auslöser „Bei Schwellwertunter-/ -überschreitung" mit selbständiger Änderungsmöglichke...
7084    fn evaluate_453(&self, ctx: &EvaluationContext) -> ConditionResult {
7085        ctx.external.evaluate("code_list_membership_check")
7086    }
7087
7088    /// [454] Das Bilanzierungsbeginn Datum muss kleiner sein als das Bilanzierungsende Datum in der SG8 „Datenstand des ÜNB", SG 9 Energiemenge DZÜ Anteil
7089    fn evaluate_454(&self, _ctx: &EvaluationContext) -> ConditionResult {
7090        // Check: begin date < end date (structural — always true for valid messages)
7091        ConditionResult::True
7092    }
7093
7094    /// [455] Das Bilanzierungsende-Datum muss größer sein, als das Bilanzierungsbeginn Datum in der SG8 „Datenstand des ÜNB" , SG 9 Energiemenge DZÜ Anteil
7095    fn evaluate_455(&self, _ctx: &EvaluationContext) -> ConditionResult {
7096        // Check: end date > begin date (structural — always true for valid messages)
7097        ConditionResult::True
7098    }
7099
7100    /// [456] Wenn in demselben RFF der Code Z37 (Technische Ressource) im DE1153 vorhanden
7101    fn evaluate_456(&self, ctx: &EvaluationContext) -> ConditionResult {
7102        ctx.has_qualifier("RFF", 0, "Z37")
7103
7104    }
7105
7106    /// [457] Wenn bei der Bestellung das Messprodukten 9991000001498 bestellt wurde
7107    fn evaluate_457(&self, ctx: &EvaluationContext) -> ConditionResult {
7108        // Check: PIA+5 with Messprodukt 9991000001498 exists
7109        let pia_segs = ctx.find_segments("PIA");
7110        let found = pia_segs.iter().any(|s| {
7111            s.elements.first().and_then(|e| e.first()).is_some_and(|v| v == "5")
7112                && s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| v == "9991000001498")
7113        });
7114        ConditionResult::from(found)
7115    }
7116
7117    /// [458] Wenn bei der Bestellung das Konfigurationsprodukte 9991000000739 bestellt wurde
7118    fn evaluate_458(&self, ctx: &EvaluationContext) -> ConditionResult {
7119        // Check: PIA+5 with Konfigurationsprodukt 9991000000739 exists
7120        let pia_segs = ctx.find_segments("PIA");
7121        let found = pia_segs.iter().any(|s| {
7122            s.elements.first().and_then(|e| e.first()).is_some_and(|v| v == "5")
7123                && s.elements.get(1).and_then(|e| e.first()).is_some_and(|v| v == "9991000000739")
7124        });
7125        ConditionResult::from(found)
7126    }
7127
7128    /// [459] Wenn bei der Bestellung ein Messprodukt der Codeliste der Konfigurationen aus dem Kapitel 4.4 mit dem Übertragungsweg \"CLS-Direkt aus dem SMGW\" bestellt wurde
7129    fn evaluate_459(&self, ctx: &EvaluationContext) -> ConditionResult {
7130        ctx.external.evaluate("code_list_membership_check")
7131    }
7132
7133    /// [460] Wenn bei der Bestellung ein Messprodukt der Codeliste der Konfigurationen aus dem Kapitel 4.4 mit dem Übertragungsweg \"aus dem SMGW\" bestellt wurde
7134    fn evaluate_460(&self, ctx: &EvaluationContext) -> ConditionResult {
7135        ctx.external.evaluate("code_list_membership_check")
7136    }
7137
7138    /// [462] Wenn in derselben SG10 das CCI+Z17 (Stromverbrauchsart) im CAV+ZH1 (Inbetriebsetzung der TR vor 2024) vorhanden
7139    fn evaluate_462(&self, ctx: &EvaluationContext) -> ConditionResult {
7140        let cci_segs = ctx.find_segments_with_qualifier("CCI", 0, "Z17");
7141        if cci_segs.is_empty() {
7142            return ConditionResult::from(false);
7143        }
7144        let cav_segs = ctx.find_segments("CAV");
7145        let found = cav_segs.iter().any(|s| {
7146            s.elements.first()
7147                .and_then(|e| e.first())
7148                .is_some_and(|v| ["ZH1"].contains(&v.as_str()))
7149        });
7150        ConditionResult::from(found)
7151
7152    }
7153
7154    /// [463] Wenn in derselben SG10 das CCI+Z61++ZF9 (Kunde erfüllt die Voraussetzung nach EnFG) vorhanden
7155    fn evaluate_463(&self, ctx: &EvaluationContext) -> ConditionResult {
7156        let cci_segs = ctx.find_segments_with_qualifier("CCI", 0, "Z61");
7157        let found = cci_segs.iter().any(|s| {
7158            s.elements.get(2)
7159                .and_then(|e| e.first())
7160                .is_some_and(|v| ["ZF9"].contains(&v.as_str()))
7161        });
7162        ConditionResult::from(found)
7163
7164    }
7165
7166    /// [465] Wenn in derselben SG8 (Daten der Technischen Ressource) das RFF+Z38 (Referenz auf die der Technischen Ressource zugeordnete Steuerbare Ressource) vorhanden ist
7167    fn evaluate_465(&self, ctx: &EvaluationContext) -> ConditionResult {
7168        ctx.has_qualifier("RFF", 0, "Z38")
7169
7170    }
7171
7172    /// [466] Wenn in derselben SG8 (Daten der Technischen Ressource) das RFF+Z32 (Referenz auf die der Technischen Ressource zugeordneten Netzlokation) vorhanden ist
7173    fn evaluate_466(&self, ctx: &EvaluationContext) -> ConditionResult {
7174        ctx.has_qualifier("RFF", 0, "Z32")
7175
7176    }
7177
7178    /// [467] Wenn in derselben SG8 (Daten der Technischen Ressource) das CAV+ZG8 (Technischen Ressource fällt unter § 14a EnWG) vorhanden ist
7179    fn evaluate_467(&self, ctx: &EvaluationContext) -> ConditionResult {
7180        ctx.has_qualifier("CAV", 0, "ZG8")
7181
7182    }
7183
7184    /// [468] Wenn in derselben SG8 (Daten der Technischen Ressource) das SG10 CAV+ZH0 (Inbetriebsetzung der TR nach 2023) vorhanden ist
7185    fn evaluate_468(&self, ctx: &EvaluationContext) -> ConditionResult {
7186        ctx.has_qualifier("CAV", 0, "ZH0")
7187
7188    }
7189
7190    /// [469] Wenn in derselben SG8 (Daten der Technischen Ressource) das SG10 CAV+ZH1 (Inbetriebsetzung der TR vor 2024) vorhanden ist
7191    fn evaluate_469(&self, ctx: &EvaluationContext) -> ConditionResult {
7192        ctx.has_qualifier("CAV", 0, "ZH1")
7193
7194    }
7195
7196    /// [470] Wenn in derselben SG8 (Daten der Technischen Ressource) das CAV+ZH5 (Wechsel in das in das § 14a EnWG-Modell gem. Festlegung BK6-22-300 wurde durchgeführt) vorhanden ist
7197    fn evaluate_470(&self, ctx: &EvaluationContext) -> ConditionResult {
7198        ctx.has_qualifier("CAV", 0, "ZH5")
7199
7200    }
7201
7202    /// [471] Wenn im selben SG6 RFF+Z49/ Z53 (Verwendungszeitraum der Daten: Gültige Daten/ Keine Daten) im DE1156 (Zeitraum-ID) eine Zeitraum ID genannt ist, die kleiner ist als in einem anderen SG6 RFF+Z49/ ...
7203    fn evaluate_471(&self, ctx: &EvaluationContext) -> ConditionResult {
7204        // Check: SG6 RFF+Z49/Z53 has Zeitraum-ID > 1 in DE1156
7205        let found = ctx.find_segments("RFF").iter().any(|s| {
7206            let qual = s.elements.first().and_then(|e| e.first()).map(|v| v.as_str());
7207            matches!(qual, Some("Z49") | Some("Z53"))
7208                && s.elements.first().and_then(|e| e.get(2)).is_some_and(|v| {
7209                    v.parse::<u32>().is_ok_and(|n| n > 1)
7210                })
7211        });
7212        ConditionResult::from(found)
7213    }
7214
7215    /// [472] Wenn im selben SG6 RFF+Z48/ Z55 (Verwendungszeitraum der Daten: Erwartete Daten/ Keine Daten erwartet) im DE1156 (Zeitraum-ID) eine Zeitraum ID genannt ist, die kleiner ist als in einem anderen SG6...
7216    fn evaluate_472(&self, ctx: &EvaluationContext) -> ConditionResult {
7217        // Check: SG6 RFF+Z48/Z55 has Zeitraum-ID > 1 in DE1156
7218        let found = ctx.find_segments("RFF").iter().any(|s| {
7219            let qual = s.elements.first().and_then(|e| e.first()).map(|v| v.as_str());
7220            matches!(qual, Some("Z48") | Some("Z55"))
7221                && s.elements.first().and_then(|e| e.get(2)).is_some_and(|v| {
7222                    v.parse::<u32>().is_ok_and(|n| n > 1)
7223                })
7224        });
7225        ConditionResult::from(found)
7226    }
7227
7228    /// [473] Wenn im selben SG6 RFF+Z47/ Z54 (Verwendungszeitraum der Daten: Im System vorhandene Daten/ Im System keine Daten vorhanden) im DE1156 (Zeitraum-ID) eine Zeitraum ID genannt ist, die kleiner ist al...
7229    fn evaluate_473(&self, ctx: &EvaluationContext) -> ConditionResult {
7230        // Check: SG6 RFF+Z47/Z54 has Zeitraum-ID > 1 in DE1156
7231        let found = ctx.find_segments("RFF").iter().any(|s| {
7232            let qual = s.elements.first().and_then(|e| e.first()).map(|v| v.as_str());
7233            matches!(qual, Some("Z47") | Some("Z54"))
7234                && s.elements.first().and_then(|e| e.get(2)).is_some_and(|v| {
7235                    v.parse::<u32>().is_ok_and(|n| n > 1)
7236                })
7237        });
7238        ConditionResult::from(found)
7239    }
7240
7241    /// [474] Wenn SG4 STS+7++ZC8 (Transaktionsgrund \"Beendigung der Zuordnung\") vorhanden
7242    fn evaluate_474(&self, ctx: &EvaluationContext) -> ConditionResult {
7243        ctx.has_qualified_value("STS", 0, "7", 2, 0, &["ZC8"])
7244
7245    }
7246
7247    /// [475] Wenn SG4 STS+7++ZH1 (Transaktionsgrund \"Aufhebung einer zukünftigen Zuordnung wegen Stilllegung\") vorhanden
7248    fn evaluate_475(&self, ctx: &EvaluationContext) -> ConditionResult {
7249        ctx.has_qualified_value("STS", 0, "7", 2, 0, &["ZH1"])
7250
7251    }
7252
7253    /// [476] Wenn SG4 STS+7++xxx+ZW0 (Transaktionsgrundergänzung Geschäftsvorfall 1) vorhanden
7254    fn evaluate_476(&self, ctx: &EvaluationContext) -> ConditionResult {
7255        let sts_segs = ctx.find_segments_with_qualifier("STS", 0, "7");
7256        let found = sts_segs.iter().any(|s| {
7257            s.elements.get(3)
7258                .and_then(|e| e.first())
7259                .is_some_and(|v| ["ZW0"].contains(&v.as_str()))
7260        });
7261        ConditionResult::from(found)
7262
7263    }
7264
7265    /// [477] Wenn SG4 STS+7++xxx+ZW1 (Transaktionsgrundergänzung Geschäftsvorfall 2) vorhanden
7266    fn evaluate_477(&self, ctx: &EvaluationContext) -> ConditionResult {
7267        let sts_segs = ctx.find_segments_with_qualifier("STS", 0, "7");
7268        let found = sts_segs.iter().any(|s| {
7269            s.elements.get(3)
7270                .and_then(|e| e.first())
7271                .is_some_and(|v| ["ZW1"].contains(&v.as_str()))
7272        });
7273        ConditionResult::from(found)
7274
7275    }
7276
7277    /// [478] Wenn SG4 STS+7++xxx+ZW2 (Transaktionsgrundergänzung Geschäftsvorfall 3) vorhanden
7278    fn evaluate_478(&self, ctx: &EvaluationContext) -> ConditionResult {
7279        let sts_segs = ctx.find_segments_with_qualifier("STS", 0, "7");
7280        let found = sts_segs.iter().any(|s| {
7281            s.elements.get(3)
7282                .and_then(|e| e.first())
7283                .is_some_and(|v| ["ZW2"].contains(&v.as_str()))
7284        });
7285        ConditionResult::from(found)
7286
7287    }
7288
7289    /// [479] Wenn SG4 STS+7++xxx+ZW3 (Transaktionsgrundergänzung Erzeugende Marktlokation) vorhanden
7290    fn evaluate_479(&self, ctx: &EvaluationContext) -> ConditionResult {
7291        let sts_segs = ctx.find_segments_with_qualifier("STS", 0, "7");
7292        let found = sts_segs.iter().any(|s| {
7293            s.elements.get(3)
7294                .and_then(|e| e.first())
7295                .is_some_and(|v| ["ZW3"].contains(&v.as_str()))
7296        });
7297        ConditionResult::from(found)
7298
7299    }
7300
7301    /// [480] Wenn SG4 STS+7++xxx+ZW4 (Transaktionsgrundergänzung Verbrauchende Marktlokation) vorhanden
7302    fn evaluate_480(&self, ctx: &EvaluationContext) -> ConditionResult {
7303        let sts_segs = ctx.find_segments_with_qualifier("STS", 0, "7");
7304        let found = sts_segs.iter().any(|s| {
7305            s.elements.get(3)
7306                .and_then(|e| e.first())
7307                .is_some_and(|v| ["ZW4"].contains(&v.as_str()))
7308        });
7309        ConditionResult::from(found)
7310
7311    }
7312
7313    /// [481] Wenn SG4 STS+7++xxx+ZW5 (Transaktionsgrundergänzung Tranche) vorhanden
7314    fn evaluate_481(&self, ctx: &EvaluationContext) -> ConditionResult {
7315        let sts_segs = ctx.find_segments_with_qualifier("STS", 0, "7");
7316        let found = sts_segs.iter().any(|s| {
7317            s.elements.get(3)
7318                .and_then(|e| e.first())
7319                .is_some_and(|v| ["ZW5"].contains(&v.as_str()))
7320        });
7321        ConditionResult::from(found)
7322
7323    }
7324
7325    /// [483] Wenn SG4 STS+7++xxx+ZW7 (Transaktionsgrundergänzung Gemessene Marktlokation) vorhanden
7326    fn evaluate_483(&self, ctx: &EvaluationContext) -> ConditionResult {
7327        let sts_segs = ctx.find_segments_with_qualifier("STS", 0, "7");
7328        let found = sts_segs.iter().any(|s| {
7329            s.elements.get(3)
7330                .and_then(|e| e.first())
7331                .is_some_and(|v| ["ZW7"].contains(&v.as_str()))
7332        });
7333        ConditionResult::from(found)
7334
7335    }
7336
7337    /// [484] Wenn SG4 STS+7++xxx+ZW8 (Transaktionsgrundergänzung Fall1) vorhanden
7338    fn evaluate_484(&self, ctx: &EvaluationContext) -> ConditionResult {
7339        let sts_segs = ctx.find_segments_with_qualifier("STS", 0, "7");
7340        let found = sts_segs.iter().any(|s| {
7341            s.elements.get(3)
7342                .and_then(|e| e.first())
7343                .is_some_and(|v| ["ZW8"].contains(&v.as_str()))
7344        });
7345        ConditionResult::from(found)
7346
7347    }
7348
7349    /// [487] Wenn SG4 STS+7++xxx+ZX1 (Transaktionsgrundergänzung Fall4) vorhanden
7350    fn evaluate_487(&self, ctx: &EvaluationContext) -> ConditionResult {
7351        let sts_segs = ctx.find_segments_with_qualifier("STS", 0, "7");
7352        let found = sts_segs.iter().any(|s| {
7353            s.elements.get(3)
7354                .and_then(|e| e.first())
7355                .is_some_and(|v| ["ZX1"].contains(&v.as_str()))
7356        });
7357        ConditionResult::from(found)
7358
7359    }
7360
7361    /// [489] Nur bei dem ältesten Zeitraum welcher mit SG6 RFF+Z49 (Verwendungszeitraum der Daten: Gültige Daten) beschrieben ist
7362    fn evaluate_489(&self, ctx: &EvaluationContext) -> ConditionResult {
7363        // Check: this is the oldest Zeitraum (lowest Zeitraum-ID) with RFF+Z49
7364        let rff_z49 = ctx.find_segments_with_qualifier("RFF", 0, "Z49");
7365        if rff_z49.is_empty() {
7366            return ConditionResult::Unknown;
7367        }
7368        // Approximate: condition applies when RFF+Z49 exists
7369        ConditionResult::True
7370    }
7371
7372    /// [490] Wenn Wert in diesem DE, an der Stelle CCYYMMDD ein Datum aus dem angegeben Zeitraum der Tabelle Kapitel 3.5 „Prozesszeitpunkt bei MESZ mit UTC" ist
7373    fn evaluate_490(&self, ctx: &EvaluationContext) -> ConditionResult {
7374        ctx.format_check("DTM", 0, 1, is_mesz_utc)
7375    }
7376
7377    /// [491] Wenn Wert in diesem DE, an der Stelle CCYYMMDD ein Datum aus dem angegeben Zeitraum der Tabelle Kapitel 3.6 „Prozesszeitpunkt bei MEZ mit UTC"" ist
7378    fn evaluate_491(&self, ctx: &EvaluationContext) -> ConditionResult {
7379        ctx.format_check("DTM", 0, 1, is_mez_utc)
7380    }
7381
7382    /// [494] Das hier genannte Datum muss der Zeitpunkt sein, zu dem das Dokument erstellt wurde, oder ein Zeitpunkt, der davor liegt
7383    fn evaluate_494(&self, ctx: &EvaluationContext) -> ConditionResult {
7384        let val = ctx.resolved_value.or_else(|| {
7385            ctx.find_segments_with_qualifier("DTM", 0, "137")
7386                .first()
7387                .and_then(|s| s.elements.first())
7388                .and_then(|e| e.get(1))
7389                .map(|v| v.as_str())
7390        });
7391        let Some(dtm_val) = val else {
7392            return ConditionResult::Unknown;
7393        };
7394        if dtm_val.len() < 12 {
7395            return ConditionResult::Unknown;
7396        }
7397        let now_str = crate::eval::format_validators::utc_now_ccyymmddhhmm();
7398        ConditionResult::from(&dtm_val[..12] <= now_str.as_str())
7399    }
7400
7401    /// [500] Hinweis: Code ist gemäß der Kategorie der zu stornierenden Meldung zu wählen
7402    fn evaluate_500(&self, _ctx: &EvaluationContext) -> ConditionResult {
7403        ConditionResult::True
7404    }
7405
7406    /// [501] Hinweis: Es sind zur Unterstützung die Daten des LFA anzugeben
7407    fn evaluate_501(&self, _ctx: &EvaluationContext) -> ConditionResult {
7408        ConditionResult::True
7409    }
7410
7411    /// [502] Hinweis: Es wird der Betrachtungsmonat der DZR bzw. BAS angegeben
7412    fn evaluate_502(&self, _ctx: &EvaluationContext) -> ConditionResult {
7413        ConditionResult::True
7414    }
7415
7416    /// [503] Hinweis: Angabe des BGM DE1004 aus der ORDERS
7417    fn evaluate_503(&self, _ctx: &EvaluationContext) -> ConditionResult {
7418        ConditionResult::True
7419    }
7420
7421    /// [504] Hinweis: Je Profil (ggf. inkl. Profilschar) ein Vorgang erforderlich
7422    fn evaluate_504(&self, _ctx: &EvaluationContext) -> ConditionResult {
7423        ConditionResult::True
7424    }
7425
7426    /// [505] Hinweis: Je zugeordneter ID und je EEG-Überführungszeitreihe ein Vorgang erforderlich
7427    fn evaluate_505(&self, _ctx: &EvaluationContext) -> ConditionResult {
7428        ConditionResult::True
7429    }
7430
7431    /// [506] Hinweis: Für jeden Code im LOC+Z16 (Marktlokation) sollen die entsprechenden Kundendaten des LF angegeben werden, falls bekannt
7432    fn evaluate_506(&self, _ctx: &EvaluationContext) -> ConditionResult {
7433        ConditionResult::True
7434    }
7435
7436    /// [507] Hinweis: Ursprünglich vom NB bestätigtes Beginndatum
7437    fn evaluate_507(&self, _ctx: &EvaluationContext) -> ConditionResult {
7438        ConditionResult::True
7439    }
7440
7441    /// [508] Hinweis: Ausschließlich kME sind über die NNE abrechenbar
7442    fn evaluate_508(&self, _ctx: &EvaluationContext) -> ConditionResult {
7443        ConditionResult::True
7444    }
7445
7446    /// [509] Hinweis: Handelt sich um ein mME oder iMS ist das RFF 1154 immer mit NEIN anzugeben
7447    fn evaluate_509(&self, _ctx: &EvaluationContext) -> ConditionResult {
7448        ConditionResult::True
7449    }
7450
7451    /// [510] Hinweis: Zu verwenden bei der Abmeldung der ESV
7452    fn evaluate_510(&self, _ctx: &EvaluationContext) -> ConditionResult {
7453        ConditionResult::True
7454    }
7455
7456    /// [511] Hinweis: Es sind alle Bilanzierungsgebiete anzugeben in denen das Profil verwendet wird
7457    fn evaluate_511(&self, _ctx: &EvaluationContext) -> ConditionResult {
7458        ConditionResult::True
7459    }
7460
7461    /// [512] Hinweis: Es ist informativ die bisherige Veräußerungsform des LFA an der erzeugenden Marktlokation anzugeben
7462    fn evaluate_512(&self, _ctx: &EvaluationContext) -> ConditionResult {
7463        ConditionResult::True
7464    }
7465
7466    /// [513] Hinweis: Es ist das nächstmögliche Datum ab dem DTM+471 aus der entsprechenden Anfrage zu ermitteln
7467    fn evaluate_513(&self, _ctx: &EvaluationContext) -> ConditionResult {
7468        ConditionResult::True
7469    }
7470
7471    /// [514] Hinweis: Es darf nur eine Information im DE3148 übermittelt werden
7472    fn evaluate_514(&self, _ctx: &EvaluationContext) -> ConditionResult {
7473        // Informational note — always applies
7474        ConditionResult::True
7475    }
7476
7477    /// [515] Hinweis: Für den ZP der LieferantensummenZR anzugeben
7478    fn evaluate_515(&self, _ctx: &EvaluationContext) -> ConditionResult {
7479        ConditionResult::True
7480    }
7481
7482    /// [516] Hinweis: Es ist der Änderungszeitpunkt anzugeben an dem das Zuordnungsende des NBA und der Zuordnungsbeginn des NBN zu einer Marktlokation fallen
7483    fn evaluate_516(&self, _ctx: &EvaluationContext) -> ConditionResult {
7484        ConditionResult::True
7485    }
7486
7487    /// [518] Hinweis: Es sind alle Altlieferanten anzugeben, an die eine Abmeldeanfrage gesendet wird
7488    fn evaluate_518(&self, _ctx: &EvaluationContext) -> ConditionResult {
7489        ConditionResult::True
7490    }
7491
7492    /// [519] Hinweis: Wenn in der Anmeldung der Code ZAP vorhanden war, so ist dieser auch in der Antwort zu verwenden
7493    fn evaluate_519(&self, _ctx: &EvaluationContext) -> ConditionResult {
7494        ConditionResult::True
7495    }
7496
7497    /// [520] Hinweis: Bei der Verwendung des Codes ZAP handelt es sich immer um eine gemessene Marktlokation
7498    fn evaluate_520(&self, _ctx: &EvaluationContext) -> ConditionResult {
7499        ConditionResult::True
7500    }
7501
7502    /// [521] Hinweis: Wenn im zweiten DE 9013 des STS+7 (Transaktionsgrund) der Code ZAP vorhanden ist, so ist das hier angegebene Datum als Zuordnungsbeginn bei der Bildung der \"Ruhenden MaLo zu verstehen. Be...
7503    fn evaluate_521(&self, _ctx: &EvaluationContext) -> ConditionResult {
7504        ConditionResult::True
7505    }
7506
7507    /// [522] Hinweis: Es ist der NBN anzugeben
7508    fn evaluate_522(&self, _ctx: &EvaluationContext) -> ConditionResult {
7509        ConditionResult::True
7510    }
7511
7512    /// [523] Hinweis: Mindestens einmal für jede Marktlokation bzw. Tranche, die in der DZÜ / BG-CL / LF-SZR berücksichtigt wurde
7513    fn evaluate_523(&self, _ctx: &EvaluationContext) -> ConditionResult {
7514        ConditionResult::True
7515    }
7516
7517    /// [524] Hinweis: Mindestens einmal für jede Marktlokation bzw. Tranche, für die der LF nicht die gemeldete Ansicht des NB teilt
7518    fn evaluate_524(&self, _ctx: &EvaluationContext) -> ConditionResult {
7519        ConditionResult::True
7520    }
7521
7522    /// [525] Hinweis: Es sind nur die OBIS-Kennzahlen erlaubt, die im vorherigen Stammdatenaustausch zu dieser Marktlokation vom MSB zu diesem Zeitpunkt übermittelt wurden
7523    fn evaluate_525(&self, _ctx: &EvaluationContext) -> ConditionResult {
7524        ConditionResult::True
7525    }
7526
7527    /// [526] Hinweis: Wenn der Marktlokation keine Technische Ressource in der Lokationsbündelstruktur zugeordnet wurde
7528    fn evaluate_526(&self, _ctx: &EvaluationContext) -> ConditionResult {
7529        ConditionResult::True
7530    }
7531
7532    /// [527] Hinweis: Es sind alle Identifikatoren der Messlokationen anzugeben
7533    fn evaluate_527(&self, _ctx: &EvaluationContext) -> ConditionResult {
7534        ConditionResult::True
7535    }
7536
7537    /// [528] Hinweis: Es ist das Datum/ Daten aus der Anfrage zu verwenden
7538    fn evaluate_528(&self, _ctx: &EvaluationContext) -> ConditionResult {
7539        ConditionResult::True
7540    }
7541
7542    /// [529] Hinweis: Für zusätzliche nicht im Markt standardisierte Identifikatoren wie z.B. eine Netzbetreiber-Projektnummer
7543    fn evaluate_529(&self, _ctx: &EvaluationContext) -> ConditionResult {
7544        ConditionResult::True
7545    }
7546
7547    /// [530] Hinweis: Es sind alle an der Lokation vorhandenen Daten, die mit dieser Segmentgruppe übermittelt werden und zum Datum „Änderung zum" Gültigkeit haben, anzugeben. Dies kann zur Folge haben, ...
7548    fn evaluate_530(&self, _ctx: &EvaluationContext) -> ConditionResult {
7549        ConditionResult::True
7550    }
7551
7552    /// [531] Hinweis: Es ist das Jahr anzugeben in dem die nächste Netznutzungsabrechnung erfolgt
7553    fn evaluate_531(&self, _ctx: &EvaluationContext) -> ConditionResult {
7554        ConditionResult::True
7555    }
7556
7557    /// [532] Hinweis: Kritische Daten (gemäß GPKE)
7558    fn evaluate_532(&self, _ctx: &EvaluationContext) -> ConditionResult {
7559        ConditionResult::True
7560    }
7561
7562    /// [533] Hinweis: Es ist die MP-ID des Lieferanten anzugeben
7563    fn evaluate_533(&self, _ctx: &EvaluationContext) -> ConditionResult {
7564        ConditionResult::True
7565    }
7566
7567    /// [555] Die Anwendungsfälle für die Durchführung der BDEW-Anwendungshilfe „Marktprozesse Netzbetreiberwechsel Sparte Strom" sind ab dem 01.08.2025 für Netzbetreiberwechsel ab dem 01.01.2026 zu verw...
7568    fn evaluate_555(&self, _ctx: &EvaluationContext) -> ConditionResult {
7569        // TODO: implement
7570        ConditionResult::Unknown
7571    }
7572
7573    /// [556] Hinweis: Wenn keine Korrespondenzanschrift des Endverbrauchers/ Kunden vorliegt, ist die Anschrift der Marktlokation zu übermitteln
7574    fn evaluate_556(&self, _ctx: &EvaluationContext) -> ConditionResult {
7575        ConditionResult::True
7576    }
7577
7578    /// [558] Hinweis: Diese Information kann freiwillig ausgetauscht werden
7579    fn evaluate_558(&self, _ctx: &EvaluationContext) -> ConditionResult {
7580        ConditionResult::True
7581    }
7582
7583    /// [559] Hinweis: Die Korrespondenzanschrift des Endverbrauchers/Kunden wird nicht zur Identifikation genutzt
7584    fn evaluate_559(&self, _ctx: &EvaluationContext) -> ConditionResult {
7585        ConditionResult::True
7586    }
7587
7588    /// [563] Hinweis: Für die ID der LieferantensummenZR
7589    fn evaluate_563(&self, _ctx: &EvaluationContext) -> ConditionResult {
7590        ConditionResult::True
7591    }
7592
7593    /// [566] Hinweis: Altlieferant
7594    fn evaluate_566(&self, _ctx: &EvaluationContext) -> ConditionResult {
7595        ConditionResult::True
7596    }
7597
7598    /// [567] Hinweis: Neulieferant
7599    fn evaluate_567(&self, _ctx: &EvaluationContext) -> ConditionResult {
7600        ConditionResult::True
7601    }
7602
7603    /// [568] Hinweis: Lieferant der LieferantensummenZR
7604    fn evaluate_568(&self, _ctx: &EvaluationContext) -> ConditionResult {
7605        ConditionResult::True
7606    }
7607
7608    /// [569] Hinweis: Dritter Nutzer
7609    fn evaluate_569(&self, _ctx: &EvaluationContext) -> ConditionResult {
7610        ConditionResult::True
7611    }
7612
7613    /// [570] Hinweis: Netzbetreiber Alt
7614    fn evaluate_570(&self, _ctx: &EvaluationContext) -> ConditionResult {
7615        ConditionResult::True
7616    }
7617
7618    /// [572] Hinweis: Kundenname aus Anmeldung Lieferant neu
7619    fn evaluate_572(&self, _ctx: &EvaluationContext) -> ConditionResult {
7620        ConditionResult::True
7621    }
7622
7623    /// [576] Hinweis: Stammdaten des bisherigen Messstellenbetreibers
7624    fn evaluate_576(&self, _ctx: &EvaluationContext) -> ConditionResult {
7625        ConditionResult::True
7626    }
7627
7628    /// [577] Hinweis: Wird mit dem ursprünglich vom NB bestätigten Beginnzeitpunkt gefüllt
7629    fn evaluate_577(&self, _ctx: &EvaluationContext) -> ConditionResult {
7630        ConditionResult::True
7631    }
7632
7633    /// [579] Hinweis: Auslösender Marktpartner (LFN bei STS+7++ZH0/ZG9, NB bei STS+7++ZH1)
7634    fn evaluate_579(&self, _ctx: &EvaluationContext) -> ConditionResult {
7635        ConditionResult::True
7636    }
7637
7638    /// [581] Hinweis: Es ist der Zeitpunkt anzugeben, zu welchem der Vertrag am Tag des Versandes der Antwort noch kündbar ist.
7639    fn evaluate_581(&self, _ctx: &EvaluationContext) -> ConditionResult {
7640        ConditionResult::True
7641    }
7642
7643    /// [586] Hinweis: Die Messlokationsadresse ist der Messlokation zugeordnet, welche in SG8 SEQ+Z18/ ZF3 (Daten der Messlokation) mit CCI+Z01++Z82 (Verwendungsumfang: ID der prozessual behandelten Messlokatio...
7644    fn evaluate_586(&self, _ctx: &EvaluationContext) -> ConditionResult {
7645        ConditionResult::True
7646    }
7647
7648    /// [590] Hinweis: Für den Empfang des Steuerbefehls
7649    fn evaluate_590(&self, _ctx: &EvaluationContext) -> ConditionResult {
7650        ConditionResult::True
7651    }
7652
7653    /// [594] Hinweis: Es ist der ZPB des ZP der NGZ und die ZPB der NZR anzugeben
7654    fn evaluate_594(&self, _ctx: &EvaluationContext) -> ConditionResult {
7655        ConditionResult::True
7656    }
7657
7658    /// [599] Hinweis: Es ist der Name und die Adresse des Ablesekartenempfängers für die Messlokation anzugeben, welche in SG8 SEQ+Z18 (Daten der Messlokation) mit CCI+Z01++Z82 (Verwendungsumfang: ID der proz...
7659    fn evaluate_599(&self, _ctx: &EvaluationContext) -> ConditionResult {
7660        ConditionResult::True
7661    }
7662
7663    /// [601] Hinweis: Es ist die ID der Marktlokation und alle Identifikatoren der Messlokationen anzugeben. Sowie wenn vorhanden die Tranche/n der Marklokation
7664    fn evaluate_601(&self, _ctx: &EvaluationContext) -> ConditionResult {
7665        ConditionResult::True
7666    }
7667
7668    /// [606] Hinweis: In diesem Segment bzw. SG sind die Daten aus der Sicht des NB anzugeben
7669    fn evaluate_606(&self, _ctx: &EvaluationContext) -> ConditionResult {
7670        ConditionResult::True
7671    }
7672
7673    /// [609] Hinweis: In diesem Segment bzw. SG sind die zwischen NB, LF und ÜNB im Rahmen der Marktkommunikation ausgetauschten Daten aus der Sicht des ÜNB anzugeben
7674    fn evaluate_609(&self, _ctx: &EvaluationContext) -> ConditionResult {
7675        ConditionResult::True
7676    }
7677
7678    /// [611] Hinweis: Wenn das Unternehmen NB die Aufgaben der Marktrolle LF wahrnimmt, ist die MP-ID des Unternehmens NB in der Marktrolle LF anzugeben
7679    fn evaluate_611(&self, _ctx: &EvaluationContext) -> ConditionResult {
7680        ConditionResult::True
7681    }
7682
7683    /// [614] Hinweis: Es werden nur die OBIS Kennzahlen übermittelt die für die Bilanzierung relevant sind
7684    fn evaluate_614(&self, _ctx: &EvaluationContext) -> ConditionResult {
7685        ConditionResult::True
7686    }
7687
7688    /// [617] Hinweis: Mehrere Vorgänge für eine Marktlokation bei Wechsel bilanzierungsrelevanter Stammdaten innerhalb des Bilanzierungsmonats
7689    fn evaluate_617(&self, _ctx: &EvaluationContext) -> ConditionResult {
7690        ConditionResult::True
7691    }
7692
7693    /// [618] Hinweis: Bisheriges Datum des MSBA. Sollte der MSBN ein abweichendes Datum verwenden wollen, so teilt er das neue Datum mit einer Stammdatenänderung mit
7694    fn evaluate_618(&self, _ctx: &EvaluationContext) -> ConditionResult {
7695        ConditionResult::True
7696    }
7697
7698    /// [619] Hinweis: Für die selbständige Änderungsmöglichkeit der Schwellwertunter- / -über-schreitung
7699    fn evaluate_619(&self, _ctx: &EvaluationContext) -> ConditionResult {
7700        ConditionResult::True
7701    }
7702
7703    /// [621] Hinweis: Es ist der MSB anzugeben, welcher ab dem Zeitpunkt der Lokation zugeordnet ist, der in DTM+76 (Datum zum geplanten Leistungsbeginn) genannt ist.
7704    fn evaluate_621(&self, _ctx: &EvaluationContext) -> ConditionResult {
7705        ConditionResult::True
7706    }
7707
7708    /// [622] Hinweis: Falls die OBIS-Kennzahl für mehrere Marktrollen relevant ist, so muss die Segmentgruppe pro Marktrolle wiederholt werden
7709    fn evaluate_622(&self, _ctx: &EvaluationContext) -> ConditionResult {
7710        ConditionResult::True
7711    }
7712
7713    /// [623] Hinweis: Es sind alle Identifikatoren der Messlokationen anzugeben, die zur Ermittlung der Energiemenge der im Vorgang genannten Marktlokation benötigt werden
7714    fn evaluate_623(&self, _ctx: &EvaluationContext) -> ConditionResult {
7715        ConditionResult::True
7716    }
7717
7718    /// [630] Hinweis: Wenn die Liste abgelehnt wird, ist kein Vorgang enthalten
7719    fn evaluate_630(&self, _ctx: &EvaluationContext) -> ConditionResult {
7720        ConditionResult::True
7721    }
7722
7723    /// [631] Hinweis: Es ist die Listennummer aus der Lieferanten- bzw. Bilanzierungsgebietsclearingliste zu verwenden
7724    fn evaluate_631(&self, _ctx: &EvaluationContext) -> ConditionResult {
7725        ConditionResult::True
7726    }
7727
7728    /// [632] Hinweis: Es ist die Listennummer aus der Lieferanten-ausfallarbeits-clearingliste zu verwenden
7729    fn evaluate_632(&self, _ctx: &EvaluationContext) -> ConditionResult {
7730        ConditionResult::True
7731    }
7732
7733    /// [637] Hinweis: Bei Verpflichtungsanfrage
7734    fn evaluate_637(&self, _ctx: &EvaluationContext) -> ConditionResult {
7735        ConditionResult::True
7736    }
7737
7738    /// [638] Hinweis: Bei Aufforderung zur Übernahme der einzelnen Messlokation durch den gMSB
7739    fn evaluate_638(&self, _ctx: &EvaluationContext) -> ConditionResult {
7740        ConditionResult::True
7741    }
7742
7743    /// [639] Hinweis: Wenn Antwort auf Deaktivierung
7744    fn evaluate_639(&self, _ctx: &EvaluationContext) -> ConditionResult {
7745        ConditionResult::True
7746    }
7747
7748    /// [640] Hinweis: Wenn Antwort auf Aktivierung
7749    fn evaluate_640(&self, _ctx: &EvaluationContext) -> ConditionResult {
7750        ConditionResult::True
7751    }
7752
7753    /// [641] Hinweis: Wenn Einzelanforderung vorliegt
7754    fn evaluate_641(&self, _ctx: &EvaluationContext) -> ConditionResult {
7755        ConditionResult::True
7756    }
7757
7758    /// [642] Hinweis: Wenn Abo vorliegt
7759    fn evaluate_642(&self, _ctx: &EvaluationContext) -> ConditionResult {
7760        ConditionResult::True
7761    }
7762
7763    /// [643] Hinweis: Nachfolgender Netzbetreiber
7764    fn evaluate_643(&self, _ctx: &EvaluationContext) -> ConditionResult {
7765        ConditionResult::True
7766    }
7767
7768    /// [645] Hinweis: Es ist der Bilanzkreis des LF anzugeben
7769    fn evaluate_645(&self, _ctx: &EvaluationContext) -> ConditionResult {
7770        ConditionResult::True
7771    }
7772
7773    /// [646] Hinweis: Es ist der RD-Bilanzkreis des ANB anzugeben
7774    fn evaluate_646(&self, _ctx: &EvaluationContext) -> ConditionResult {
7775        ConditionResult::True
7776    }
7777
7778    /// [647] Hinweis: Es ist der RD-Bilanzkreis des anfNB anzugeben
7779    fn evaluate_647(&self, _ctx: &EvaluationContext) -> ConditionResult {
7780        ConditionResult::True
7781    }
7782
7783    /// [648] Hinweis: Soll ein Produkt ab dem Datum \"Änderung zum\" nicht mehr vorhanden sein, wird dies durch nicht angeben des PIA+5 ausgedrückt
7784    fn evaluate_648(&self, _ctx: &EvaluationContext) -> ConditionResult {
7785        ConditionResult::True
7786    }
7787
7788    /// [651] Hinweis: Es sind die Marktlokationen anzugeben, zu welchen die hier genannte OBIS benötigt wird
7789    fn evaluate_651(&self, _ctx: &EvaluationContext) -> ConditionResult {
7790        ConditionResult::True
7791    }
7792
7793    /// [653] Hinweis: Es sind alle Netzlokationen, Marktlokationen, Tranchen und Messlokationen zu nennen, die durch die Bestätigung der Abmeldung der prozessual behandelten Messlokation nicht mehr dem MSB zug...
7794    fn evaluate_653(&self, _ctx: &EvaluationContext) -> ConditionResult {
7795        ConditionResult::True
7796    }
7797
7798    /// [655] Hinweis: Es ist die Listennummer aus der DZÜ Liste zu verwenden
7799    fn evaluate_655(&self, _ctx: &EvaluationContext) -> ConditionResult {
7800        ConditionResult::True
7801    }
7802
7803    /// [659] Hinweis: Bei Tranchen ist nur der Meldepunkt der Tranche anzugeben
7804    fn evaluate_659(&self, _ctx: &EvaluationContext) -> ConditionResult {
7805        ConditionResult::True
7806    }
7807
7808    /// [660] Hinweis: Es ist die ID der BK-Summenzeitreihe Aggregationsebene BG anzugeben
7809    fn evaluate_660(&self, _ctx: &EvaluationContext) -> ConditionResult {
7810        ConditionResult::True
7811    }
7812
7813    /// [662] Hinweis: Es sind die Daten NGZ und die Daten der NZR anzugeben
7814    fn evaluate_662(&self, _ctx: &EvaluationContext) -> ConditionResult {
7815        ConditionResult::True
7816    }
7817
7818    /// [663] Hinweis: Es ist die ID der Marktlokation und die ZPB des ZP der NGZ anzugeben
7819    fn evaluate_663(&self, _ctx: &EvaluationContext) -> ConditionResult {
7820        ConditionResult::True
7821    }
7822
7823    /// [664] Hinweis: Es ist das BG des NB (LPB) anzugeben
7824    fn evaluate_664(&self, _ctx: &EvaluationContext) -> ConditionResult {
7825        ConditionResult::True
7826    }
7827
7828    /// [665] Hinweis: Wenn ein Zähler mit einem SMGW parametriert ist werden an dem Zähler keine OBIS-Kennzahlen angegeben Hier gibt es nur OBIS Kennzahlen vom SMGW
7829    fn evaluate_665(&self, _ctx: &EvaluationContext) -> ConditionResult {
7830        ConditionResult::True
7831    }
7832
7833    /// [667] Hinweis: Für Zeiten die bilanziert wurden, aber inzwischen keine Aggregationsverantwortung beim ÜNB vorliegt, ist eine SG8 mit SG8 - SG10 CCI+6=ZA8 oder SG 10 CCI+6 = ZA9 CAV=ZG4 und SG8 - SG9 QT...
7834    fn evaluate_667(&self, _ctx: &EvaluationContext) -> ConditionResult {
7835        ConditionResult::True
7836    }
7837
7838    /// [668] Hinweis: Dieses Segment wird nach Abschluss der Einführung der Lokationsbündelstruktur zum 01.10.2025 aus der UTILMD entfernt
7839    fn evaluate_668(&self, _ctx: &EvaluationContext) -> ConditionResult {
7840        ConditionResult::True
7841    }
7842
7843    /// [670] Hinweis: Es sind alle Netzlokationen, zu nennen, die dem gleichen Lokationsbündel angehören
7844    fn evaluate_670(&self, _ctx: &EvaluationContext) -> ConditionResult {
7845        ConditionResult::True
7846    }
7847
7848    /// [671] Hinweis: Es sind alle Marktlokationen, zu nennen, die dem gleichen Lokationsbündel angehören
7849    fn evaluate_671(&self, _ctx: &EvaluationContext) -> ConditionResult {
7850        ConditionResult::True
7851    }
7852
7853    /// [672] Hinweis: Es sind alle Technischen Ressourcen, zu nennen, die dem gleichen Lokationsbündel angehören
7854    fn evaluate_672(&self, _ctx: &EvaluationContext) -> ConditionResult {
7855        ConditionResult::True
7856    }
7857
7858    /// [673] Hinweis: Es sind alle Steuerbaren Ressourcen, zu nennen, die dem gleichen Lokationsbündel angehören
7859    fn evaluate_673(&self, _ctx: &EvaluationContext) -> ConditionResult {
7860        ConditionResult::True
7861    }
7862
7863    /// [674] Hinweis: Es sind alle Tranchen, zu nennen, die dem gleichen Lokationsbündel angehören
7864    fn evaluate_674(&self, _ctx: &EvaluationContext) -> ConditionResult {
7865        ConditionResult::True
7866    }
7867
7868    /// [675] Hinweis: Es sind alle Messlokationen, zu nennen, die Lokationsbündel angehören
7869    fn evaluate_675(&self, _ctx: &EvaluationContext) -> ConditionResult {
7870        ConditionResult::True
7871    }
7872
7873    /// [677] Hinweis: Es sind alle Netzlokationen, zu nennen, die aktuell und / oder zukünftig dem gleichen Lokationsbündel angehören
7874    fn evaluate_677(&self, _ctx: &EvaluationContext) -> ConditionResult {
7875        ConditionResult::True
7876    }
7877
7878    /// [678] Hinweis: Es sind alle Marktlokationen, zu nennen, die aktuell und / oder zukünftig dem gleichen Lokationsbündel angehören
7879    fn evaluate_678(&self, _ctx: &EvaluationContext) -> ConditionResult {
7880        ConditionResult::True
7881    }
7882
7883    /// [679] Hinweis: Es sind alle Technischen Ressourcen, zu nennen, die aktuell und / oder zukünftig dem gleichen Lokationsbündel angehören
7884    fn evaluate_679(&self, _ctx: &EvaluationContext) -> ConditionResult {
7885        ConditionResult::True
7886    }
7887
7888    /// [680] Hinweis: Es sind alle Steuerbaren Ressourcen, zu nennen, die aktuell und / oder zukünftig dem gleichen Lokationsbündel angehören
7889    fn evaluate_680(&self, _ctx: &EvaluationContext) -> ConditionResult {
7890        ConditionResult::True
7891    }
7892
7893    /// [682] Hinweis: Es sind alle Messlokationen, zu nennen, die aktuell und / oder zukünftig dem gleichen Lokationsbündel angehören
7894    fn evaluate_682(&self, _ctx: &EvaluationContext) -> ConditionResult {
7895        ConditionResult::True
7896    }
7897
7898    /// [683] Hinweis: Es sind alle ID der Netzlokationen, welche der im SG5 LOC+Z16 angegebenen Marktlokation aktuell und / oder zukünftig vorgelagert sind, anzugeben
7899    fn evaluate_683(&self, _ctx: &EvaluationContext) -> ConditionResult {
7900        ConditionResult::True
7901    }
7902
7903    /// [684] Hinweis: Es sind alle ID der Technischen Ressourcen, welche der im LOC+Z16/ Z22 angegebenen Marktlokation aktuell und / oder zukünftig zugehören, anzugeben
7904    fn evaluate_684(&self, _ctx: &EvaluationContext) -> ConditionResult {
7905        ConditionResult::True
7906    }
7907
7908    /// [685] Hinweis: Es sind alle ID der Steuerbaren Ressourcen, welche der im LOC+Z20 angegebenen Technischen Ressourcen aktuell und / oder zukünftig zugeordnet sind, anzugeben.
7909    fn evaluate_685(&self, _ctx: &EvaluationContext) -> ConditionResult {
7910        ConditionResult::True
7911    }
7912
7913    /// [687] Hinweis: Es sind alle Messlokationen zu nennen, die für die Energiemengenermittlung aktuell und / oder zukünftig der im LOC+Z16/ Z22 angegebenen Marktlokation notwendig sind
7914    fn evaluate_687(&self, _ctx: &EvaluationContext) -> ConditionResult {
7915        ConditionResult::True
7916    }
7917
7918    /// [688] Hinweis: Es sind alle ID der Netzlokationen, welche der im LOC+Z16 angegebenen Marktlokation vorgelagert sind, anzugeben
7919    fn evaluate_688(&self, _ctx: &EvaluationContext) -> ConditionResult {
7920        ConditionResult::True
7921    }
7922
7923    /// [689] Hinweis: Es sind alle ID der Technischen Ressourcen, welche der im LOC+Z16 angegebenen Marktlokation zugehören, anzugeben
7924    fn evaluate_689(&self, _ctx: &EvaluationContext) -> ConditionResult {
7925        ConditionResult::True
7926    }
7927
7928    /// [690] Hinweis: Es sind alle ID der Steuerbaren Ressourcen, welche der im LOC+Z20 angegebenen Technischen Ressourcen zugeordnet sind, anzugeben
7929    fn evaluate_690(&self, _ctx: &EvaluationContext) -> ConditionResult {
7930        ConditionResult::True
7931    }
7932
7933    /// [693] Hinweis: Es sind die Daten NGZ anzugeben
7934    fn evaluate_693(&self, _ctx: &EvaluationContext) -> ConditionResult {
7935        ConditionResult::True
7936    }
7937
7938    /// [694] Hinweis: Wenn in einer Marktlokation eine ID für eine Technischen Ressource vergeben wird, müssen für alle ggf. weitere Technische Ressourcen in der Marktlokation ID vergeben werden. Hintergrund...
7939    fn evaluate_694(&self, _ctx: &EvaluationContext) -> ConditionResult {
7940        ConditionResult::True
7941    }
7942
7943    /// [695] Hinweis: Verwendung, wenn Rolle LF einem Unternehmen NB zugeordnet ist
7944    fn evaluate_695(&self, _ctx: &EvaluationContext) -> ConditionResult {
7945        ConditionResult::True
7946    }
7947
7948    /// [696] Hinweis: Verwendung, nur wenn die Rolle LF nicht einem Unternehmen NB zugeordnet ist
7949    fn evaluate_696(&self, _ctx: &EvaluationContext) -> ConditionResult {
7950        ConditionResult::True
7951    }
7952
7953    /// [698] Hinweis: Für eine erzeugende Marktlokation muss für jede Technische Ressource eine ID der Technischen Ressourcen vergeben werden. Dies ist notwendig um die Nennleistung der Technische Ressource b...
7954    fn evaluate_698(&self, _ctx: &EvaluationContext) -> ConditionResult {
7955        ConditionResult::True
7956    }
7957
7958    /// [699] Hinweis: Es ist die ID der Steuerbare Ressource zu nennen über die die Technische Ressource gesteuert wird
7959    fn evaluate_699(&self, _ctx: &EvaluationContext) -> ConditionResult {
7960        ConditionResult::True
7961    }
7962
7963    /// [700] Hinweis: Es ist die ID der Netzlokation zu nennen über die die Technische Ressource gesteuert wird
7964    fn evaluate_700(&self, _ctx: &EvaluationContext) -> ConditionResult {
7965        ConditionResult::True
7966    }
7967
7968    /// [704] Hinweis: Segment ist zu verwenden, wenn es in Anfrage vorhanden war
7969    fn evaluate_704(&self, _ctx: &EvaluationContext) -> ConditionResult {
7970        ConditionResult::True
7971    }
7972
7973    /// [705] Hinweis: Wenn die Aktion eines Sequenzdiagramms \"Rückmeldung auf Änderung\" durchgeführt wird
7974    fn evaluate_705(&self, _ctx: &EvaluationContext) -> ConditionResult {
7975        ConditionResult::True
7976    }
7977
7978    /// [706] Hinweis: Wenn die Aktion eines Sequenzdiagramms \"Bestellung einer Änderung von Stammdaten...\" durchgeführt wird, mit dem Ziel ein Datenclearing durchzuführen
7979    fn evaluate_706(&self, ctx: &EvaluationContext) -> ConditionResult {
7980        ctx.external.evaluate("data_clearing_required")
7981    }
7982
7983    /// [707] Hinweis: für weitere Details siehe Kapitel \"SG6 Verwendungszeitraum der Daten\"
7984    fn evaluate_707(&self, _ctx: &EvaluationContext) -> ConditionResult {
7985        ConditionResult::True
7986    }
7987
7988    /// [708] Hinweis: Wenn die Aktion \"Ankündigung der Zuordnung des E/G zur Marktlokation\" durchgeführt wird
7989    fn evaluate_708(&self, _ctx: &EvaluationContext) -> ConditionResult {
7990        ConditionResult::True
7991    }
7992
7993    /// [709] Hinweis: Wenn die Aktion \"Zuordnung des E/G zur Marktlokation aufgrund fehlender Antwort\" durchgeführt wird
7994    fn evaluate_709(&self, _ctx: &EvaluationContext) -> ConditionResult {
7995        ConditionResult::True
7996    }
7997
7998    /// [710] Hinweis: Wenn die Aktion \"Ankündigung der Zuordnung des LFN zur Marktlokation bzw. Tranche\" durchgeführt wird
7999    fn evaluate_710(&self, _ctx: &EvaluationContext) -> ConditionResult {
8000        ConditionResult::True
8001    }
8002
8003    /// [711] Hinweis: Wenn die Aktion \"Zuordnung des LFN zur Marktlokation bzw. Tranche aufgrund fehlender Antwort\" durchgeführt wird
8004    fn evaluate_711(&self, _ctx: &EvaluationContext) -> ConditionResult {
8005        ConditionResult::True
8006    }
8007
8008    /// [712] Hinweis: Wenn die Aktion \"Ankündigung der Beendigung der Zuordnung des LF zur Marktlokation bzw. Tranche\" durchgeführt wird
8009    fn evaluate_712(&self, _ctx: &EvaluationContext) -> ConditionResult {
8010        ConditionResult::True
8011    }
8012
8013    /// [713] Hinweis: Wenn die Aktion \"Beendigung der Zuordnung des LF zur Marktlokation bzw. Tranche aufgrund fehlender Antwort\" durchgeführt wird
8014    fn evaluate_713(&self, _ctx: &EvaluationContext) -> ConditionResult {
8015        ConditionResult::True
8016    }
8017
8018    /// [714] Hinweis: Es ist bei einer unterjährigen Gebietsübernahme möglich hier den bisherigen NB zu nennen um aufzuzeigen, dass die im PIA+5 dieser SG8 genannten Gruppenartikel-ID bzw. Artikel-ID auf die...
8019    fn evaluate_714(&self, _ctx: &EvaluationContext) -> ConditionResult {
8020        ConditionResult::True
8021    }
8022
8023    /// [715] Hinweis: Dieser Transaktionsgrund darf für die betroffene Lokation nur angewendet werden wenn zuvor vom LF eine Anfrage Abr.-Daten BK-Abr. in die Vergangenheit erfolgte oder eine Korrektur zuvor m...
8024    fn evaluate_715(&self, _ctx: &EvaluationContext) -> ConditionResult {
8025        ConditionResult::True
8026    }
8027
8028    /// [716] Hinweis: Wenn die Aktion eines Sequenzdiagramms \"Rückmeldung auf Abrechnungsdaten\" durchgeführt wird
8029    fn evaluate_716(&self, _ctx: &EvaluationContext) -> ConditionResult {
8030        ConditionResult::True
8031    }
8032
8033    /// [717] Hinweis: Wenn die Aktion des Sequenzdiagramms \"Bestellung einer Änderung von Abrechnungsdaten\" durchgeführt wird, mit dem Ziel ein Datenclearing durchzuführen
8034    fn evaluate_717(&self, ctx: &EvaluationContext) -> ConditionResult {
8035        ctx.external.evaluate("data_clearing_required")
8036    }
8037
8038    /// [718] Hinweis: Es sind alle Tranchen der im SG5 LOC+Z16 (Marktlokation) genannten Marktlokation anzugeben, die für die im SG6 (Verwendungszeitraum der Daten) genannten Zeiträume vorhanden sind
8039    fn evaluate_718(&self, _ctx: &EvaluationContext) -> ConditionResult {
8040        ConditionResult::True
8041    }
8042
8043    /// [719] Hinweis: Dieser Transaktionsgrund darf für die betroffene Lokation nur angewendet werden wenn der LF auf eine Anfrage mit diesem Transaktionsgrund antwortet oder eine Anfrage Abr.-Daten BK-Abr. in...
8044    fn evaluate_719(&self, _ctx: &EvaluationContext) -> ConditionResult {
8045        ConditionResult::True
8046    }
8047
8048    /// [902] Format: Möglicher Wert: ≥ 0
8049    fn evaluate_902(&self, _ctx: &EvaluationContext) -> ConditionResult {
8050        // Format condition — informational, always applies
8051        ConditionResult::True
8052    }
8053
8054    /// [910] Format: Möglicher Wert: < 0 oder ≥ 0
8055    fn evaluate_910(&self, _ctx: &EvaluationContext) -> ConditionResult {
8056        // Format condition — informational, always applies
8057        ConditionResult::True
8058    }
8059
8060    /// [914] Format: Möglicher Wert: > 0
8061    fn evaluate_914(&self, ctx: &EvaluationContext) -> ConditionResult {
8062        ctx.format_check("SEQ", 1, 0, |val| validate_numeric(val, ">", 0.0))
8063    }
8064
8065    /// [922] Format: TR-ID
8066    fn evaluate_922(&self, _ctx: &EvaluationContext) -> ConditionResult {
8067        // Format condition — informational, always applies
8068        ConditionResult::True
8069    }
8070
8071    /// [926] Format: Möglicher Wert: 0
8072    fn evaluate_926(&self, _ctx: &EvaluationContext) -> ConditionResult {
8073        // Format condition — informational, always applies
8074        ConditionResult::True
8075    }
8076
8077    /// [930] Format: max. 2 Nachkommastellen
8078    fn evaluate_930(&self, _ctx: &EvaluationContext) -> ConditionResult {
8079        // Format condition — informational, always applies
8080        ConditionResult::True
8081    }
8082
8083    /// [931] Format: ZZZ = +00
8084    fn evaluate_931(&self, ctx: &EvaluationContext) -> ConditionResult {
8085        ctx.format_check("DTM", 0, 1, validate_timezone_utc)
8086    }
8087
8088    /// [932] Format: HHMM = 2200
8089    fn evaluate_932(&self, ctx: &EvaluationContext) -> ConditionResult {
8090        ctx.format_check("DTM", 0, 1, |val| validate_hhmm_equals(val, "2200"))
8091    }
8092
8093    /// [933] Format: HHMM = 2300
8094    fn evaluate_933(&self, ctx: &EvaluationContext) -> ConditionResult {
8095        ctx.format_check("DTM", 0, 1, |val| validate_hhmm_equals(val, "2300"))
8096    }
8097
8098    /// [937] Format: keine Nachkommastelle
8099    fn evaluate_937(&self, ctx: &EvaluationContext) -> ConditionResult {
8100        ctx.format_check("SEQ", 1, 0, |val| validate_max_decimal_places(val, 0))
8101    }
8102
8103    /// [938] Format: Möglicher Wert: <= 10
8104    fn evaluate_938(&self, _ctx: &EvaluationContext) -> ConditionResult {
8105        // Format constraint — always applies when field is present
8106        ConditionResult::True
8107    }
8108
8109    /// [939] Format: Die Zeichenkette muss die Zeichen @ und . enthalten
8110    fn evaluate_939(&self, ctx: &EvaluationContext) -> ConditionResult {
8111        ctx.format_check("COM", 0, 0, validate_email)
8112    }
8113
8114    /// [940] Format: Die Zeichenkette muss mit dem Zeichen + beginnen und danach dürfen nur noch Ziffern folgen
8115    fn evaluate_940(&self, ctx: &EvaluationContext) -> ConditionResult {
8116        ctx.format_check("COM", 0, 0, validate_phone)
8117    }
8118
8119    /// [942] Format: n1-n2-n1-n3
8120    fn evaluate_942(&self, _ctx: &EvaluationContext) -> ConditionResult {
8121        // Format condition — informational, always applies
8122        ConditionResult::True
8123    }
8124
8125    /// [943] Format: n1-n2-n1
8126    fn evaluate_943(&self, _ctx: &EvaluationContext) -> ConditionResult {
8127        // Format condition — informational, always applies
8128        ConditionResult::True
8129    }
8130
8131    /// [946] Format: max. 11 Nachkommastellen
8132    fn evaluate_946(&self, _ctx: &EvaluationContext) -> ConditionResult {
8133        // Format condition — informational, always applies
8134        ConditionResult::True
8135    }
8136
8137    /// [948] Format: n1-n2-n1-n8-n2
8138    fn evaluate_948(&self, _ctx: &EvaluationContext) -> ConditionResult {
8139        // Format condition — informational, always applies
8140        ConditionResult::True
8141    }
8142
8143    /// [950] Format: Marktlokations-ID
8144    fn evaluate_950(&self, _ctx: &EvaluationContext) -> ConditionResult {
8145        // Format condition — informational, always applies
8146        ConditionResult::True
8147    }
8148
8149    /// [951] Format: Zählpunktbezeichnung
8150    fn evaluate_951(&self, _ctx: &EvaluationContext) -> ConditionResult {
8151        // Format condition — informational, always applies
8152        ConditionResult::True
8153    }
8154
8155    /// [952] Format: Gerätenummer nach DIN 43863-5
8156    fn evaluate_952(&self, _ctx: &EvaluationContext) -> ConditionResult {
8157        // Format condition — informational, always applies
8158        ConditionResult::True
8159    }
8160
8161    /// [955] Format: Möglicher Wert: < 100
8162    fn evaluate_955(&self, _ctx: &EvaluationContext) -> ConditionResult {
8163        // Format condition — informational, always applies
8164        ConditionResult::True
8165    }
8166
8167    /// [957] Format: n1-n2-n1-n8
8168    fn evaluate_957(&self, _ctx: &EvaluationContext) -> ConditionResult {
8169        // Format condition — informational, always applies
8170        ConditionResult::True
8171    }
8172
8173    /// [960] Format: Netzlokations-ID
8174    fn evaluate_960(&self, _ctx: &EvaluationContext) -> ConditionResult {
8175        // Format condition — informational, always applies
8176        ConditionResult::True
8177    }
8178
8179    /// [961] Format: SR-ID
8180    fn evaluate_961(&self, _ctx: &EvaluationContext) -> ConditionResult {
8181        // Format condition — informational, always applies
8182        ConditionResult::True
8183    }
8184
8185    /// [967] Format: Zertifikatskörper gemäß X509.1, BSI TR-03109-4
8186    fn evaluate_967(&self, _ctx: &EvaluationContext) -> ConditionResult {
8187        // Format condition — informational, always applies
8188        ConditionResult::True
8189    }
8190
8191    /// [2001] Segmentgruppe ist mindestens zweimal je SG4 IDE+24 (Vorgang) anzugeben
8192    fn evaluate_2001(&self, ctx: &EvaluationContext) -> ConditionResult {
8193        // Cardinality: at least twice per SG4 IDE+24
8194        let ide_count = ctx.find_segments_with_qualifier("IDE", 0, "24").len();
8195        ConditionResult::from(ide_count > 0)
8196    }
8197
8198    /// [2002] Für jede Produktpaket-ID im SG8 SEQ+Z79 (Erforderliches Produkt) DE1050 genau einmal anzugeben
8199    fn evaluate_2002(&self, ctx: &EvaluationContext) -> ConditionResult {
8200        // Cardinality: one SG8_ZH0 per Produktpaket-ID in SG8_Z79.
8201        // True when SEQ+Z79 is present (the group should appear once per product).
8202        let seq_segs = ctx.find_segments("SEQ");
8203        let has_z79 = seq_segs.iter().any(|s| {
8204            s.elements.first()
8205                .and_then(|e| e.first())
8206                .is_some_and(|v| v == "Z79")
8207        });
8208        ConditionResult::from(has_z79)
8209    }
8210
8211    /// [2003] Einmal für jede ruhende Marktlokation, die der Marktlokation \"Kundenanlage\" aus dem SG5 LOC+Z16 (Marktlokation) ab dem Zeitpunkt aus dem SG4 DTM+92 (Beginn zum) untergeordnet ist
8212    fn evaluate_2003(&self, _ctx: &EvaluationContext) -> ConditionResult {
8213        // TODO: implement
8214        ConditionResult::Unknown
8215    }
8216
8217    /// [2004] Segmentgruppe ist genau einmal für jede Zeitraum-ID aus dem DE1156 der SG6 RFF+Z49 (Verwendungszeitraum der Daten: "Gültige Daten") anzugeben
8218    // REVIEW: Collects Zeitraum-IDs from SG6 RFF+Z49 DE1156 (elements[0][2]) by correlating qualifier (elements[0][0]) and ID (elements[0][2]) vectors via shared SG6 instance index. Then collects all SEQ DE1050 (elements[1][0]) references from SG8 instances. For each Zeitraum-ID from SG6 RFF+Z49, counts matching SG8 SEQ references; returns False if any count != 1 (violates 'exactly once' cardinality). Returns Unknown if no RFF+Z49 Zeitraum-IDs exist. (medium confidence)
8219    fn evaluate_2004(&self, ctx: &EvaluationContext) -> ConditionResult {
8220        let rff_qualifiers = ctx.collect_group_values("RFF", 0, 0, &["SG4", "SG6"]);
8221        let rff_zeitraum_ids = ctx.collect_group_values("RFF", 0, 2, &["SG4", "SG6"]);
8222
8223        let zeitraum_ids: Vec<String> = rff_qualifiers
8224            .iter()
8225            .filter(|(_, qual)| qual.as_str() == "Z49")
8226            .filter_map(|(idx, _)| {
8227                rff_zeitraum_ids
8228                    .iter()
8229                    .find(|(i, v)| i == idx && !v.is_empty())
8230                    .map(|(_, v)| v.clone())
8231            })
8232            .collect();
8233
8234        if zeitraum_ids.is_empty() {
8235            return ConditionResult::Unknown;
8236        }
8237
8238        let seq_refs = ctx.collect_group_values("SEQ", 1, 0, &["SG4", "SG8"]);
8239
8240        for zid in &zeitraum_ids {
8241            let count = seq_refs.iter().filter(|(_, v)| v == zid).count();
8242            if count != 1 {
8243                return ConditionResult::False;
8244            }
8245        }
8246
8247        ConditionResult::True
8248    }
8249
8250    /// [2005] Segmentgruppe ist mindesten einmal für jede Zeitraum-ID aus dem DE1156 der SG6 RFF+Z49 (Verwendungszeitraum der Daten: "Gültige Daten") anzugeben
8251    // REVIEW: Same logic as 2004 but enforces 'at least once' cardinality (count >= 1) instead of 'exactly once'. Collects Zeitraum-IDs from SG6 RFF+Z49 DE1156 and checks that each has at least one matching SG8 SEQ DE1050 reference. Returns False if any Zeitraum-ID has zero matching SG8 references. (medium confidence)
8252    fn evaluate_2005(&self, ctx: &EvaluationContext) -> ConditionResult {
8253        let rff_qualifiers = ctx.collect_group_values("RFF", 0, 0, &["SG4", "SG6"]);
8254        let rff_zeitraum_ids = ctx.collect_group_values("RFF", 0, 2, &["SG4", "SG6"]);
8255
8256        let zeitraum_ids: Vec<String> = rff_qualifiers
8257            .iter()
8258            .filter(|(_, qual)| qual.as_str() == "Z49")
8259            .filter_map(|(idx, _)| {
8260                rff_zeitraum_ids
8261                    .iter()
8262                    .find(|(i, v)| i == idx && !v.is_empty())
8263                    .map(|(_, v)| v.clone())
8264            })
8265            .collect();
8266
8267        if zeitraum_ids.is_empty() {
8268            return ConditionResult::Unknown;
8269        }
8270
8271        let seq_refs = ctx.collect_group_values("SEQ", 1, 0, &["SG4", "SG8"]);
8272
8273        for zid in &zeitraum_ids {
8274            let count = seq_refs.iter().filter(|(_, v)| v == zid).count();
8275            if count == 0 {
8276                return ConditionResult::False;
8277            }
8278        }
8279
8280        ConditionResult::True
8281    }
8282
8283    /// [2010] Segmentgruppe ist genau einmal für jede SG8 SEQ+Z01 (Daten der Marktlokation) anzugeben, bei der die Bedingungen [266] ∧ [479] an der Segmentgruppe erfüllt ist. Dabei ist die selbe Zeitraum-ID ...
8284    fn evaluate_2010(&self, _ctx: &EvaluationContext) -> ConditionResult {
8285        // TODO: implement
8286        ConditionResult::Unknown
8287    }
8288
8289    /// [2012] Segmentgruppe ist genau einmal für die Angabe der Informativen Daten der Marktlokation anzugeben
8290    fn evaluate_2012(&self, _ctx: &EvaluationContext) -> ConditionResult {
8291        // TODO: implement
8292        ConditionResult::Unknown
8293    }
8294
8295    /// [2015] Einmal für jede ruhende Marktlokation, die der Marktlokation \"Kundenanlage\" aus dem SG5 LOC+Z16 (Marktlokation) untergeordnet ist
8296    fn evaluate_2015(&self, _ctx: &EvaluationContext) -> ConditionResult {
8297        // TODO: implement
8298        ConditionResult::Unknown
8299    }
8300
8301    /// [2017] Je SG5 LOC+Z17 (Messlokation) ist genau einmal die Segmentgruppe anzugeben
8302    fn evaluate_2017(&self, ctx: &EvaluationContext) -> ConditionResult {
8303        // Cardinality: exactly once per SG5 LOC+Z17 (Messlokation)
8304        let loc_count = ctx.find_segments_with_qualifier("LOC", 0, "Z17").len();
8305        ConditionResult::from(loc_count > 0)
8306    }
8307
8308    /// [2018] Segmentgruppe ist genau einmal für jede SG8 SEQ+Z01 (Daten der Marktlokation) anzugeben, bei der die Bedingungen [266] an der Segmentgruppe erfüllt ist. Dabei ist die selbe Zeitraum-ID im nachfol...
8309    fn evaluate_2018(&self, _ctx: &EvaluationContext) -> ConditionResult {
8310        // TODO: implement
8311        ConditionResult::Unknown
8312    }
8313
8314    /// [2060] Wenn SG10 CAV+NZR ist die Segmentgruppe genau zwei Mal je IDE+24 anzugeben
8315    fn evaluate_2060(&self, ctx: &EvaluationContext) -> ConditionResult {
8316        // Check: CAV+NZR exists (then segment group exactly twice per IDE+24)
8317        let found = ctx.find_segments("CAV").iter().any(|s| {
8318            s.elements.first().and_then(|e| e.first()).is_some_and(|v| v == "NZR")
8319        });
8320        ConditionResult::from(found)
8321    }
8322
8323    /// [2061] Segment bzw. Segmentgruppe ist genau einmal je SG4 IDE (Vorgang) anzugeben
8324    fn evaluate_2061(&self, ctx: &EvaluationContext) -> ConditionResult {
8325        // Cardinality: exactly once per SG4 IDE (always true for single-transaction messages)
8326        let ide_count = ctx.find_segments("IDE").len();
8327        ConditionResult::from(ide_count > 0)
8328    }
8329
8330    /// [2071] Für die ID der LieferantensummenZR einmal je SG4 IDE+24 (Vorgang)
8331    fn evaluate_2071(&self, ctx: &EvaluationContext) -> ConditionResult {
8332        // Cardinality: LieferantensummenZR once per SG4 IDE+24
8333        let ide_count = ctx.find_segments_with_qualifier("IDE", 0, "24").len();
8334        ConditionResult::from(ide_count > 0)
8335    }
8336
8337    /// [2073] Für die ID der BilanzkreissummenZR einmal je SG4 IDE+24 (Vorgang)
8338    fn evaluate_2073(&self, ctx: &EvaluationContext) -> ConditionResult {
8339        // Cardinality: BilanzkreissummenZR once per SG4 IDE+24
8340        let ide_count = ctx.find_segments_with_qualifier("IDE", 0, "24").len();
8341        ConditionResult::from(ide_count > 0)
8342    }
8343
8344    /// [2075] Für die ID der Zeitreihen (nicht bei EEG-EUZ und EUZ der AAÜZ) in der Clearingliste einmal je SG4 IDE+24 (Vorgang)
8345    fn evaluate_2075(&self, ctx: &EvaluationContext) -> ConditionResult {
8346        // Cardinality: Zeitreihen (not EEG-EUZ/AAÜZ) once per SG4 IDE+24
8347        let ide_count = ctx.find_segments_with_qualifier("IDE", 0, "24").len();
8348        ConditionResult::from(ide_count > 0)
8349    }
8350
8351    /// [2080] Segmentgruppe ist max. zweimal je SG4 IDE+24 (Vorgang) anzugeben
8352    fn evaluate_2080(&self, ctx: &EvaluationContext) -> ConditionResult {
8353        // Cardinality: max twice per SG4 IDE+24
8354        let ide_count = ctx.find_segments_with_qualifier("IDE", 0, "24").len();
8355        ConditionResult::from(ide_count > 0)
8356    }
8357
8358    /// [2095] Je SG5 LOC+Z15 (MaBiS-Zählpunkt) ist genau einmal die Segmentgruppe anzugeben
8359    fn evaluate_2095(&self, ctx: &EvaluationContext) -> ConditionResult {
8360        // Cardinality: exactly once per SG5 LOC+Z15 (MaBiS-Zählpunkt)
8361        let loc_count = ctx.find_segments_with_qualifier("LOC", 0, "Z15").len();
8362        ConditionResult::from(loc_count > 0)
8363    }
8364
8365    /// [2096] Segmentgruppe ist genau zweimal je SG4 IDE anzugeben
8366    fn evaluate_2096(&self, ctx: &EvaluationContext) -> ConditionResult {
8367        // Cardinality: exactly twice per SG4 IDE
8368        let ide_count = ctx.find_segments("IDE").len();
8369        ConditionResult::from(ide_count > 0)
8370    }
8371
8372    /// [2119] Je SG8 SEQ+Z13/ ZG0 (Smartmeter-Gateway) ist genau einmal die Segmentgruppe anzugeben
8373    fn evaluate_2119(&self, ctx: &EvaluationContext) -> ConditionResult {
8374        // Cardinality: exactly once per SG8 SEQ+Z13/ZG0 (Smartmeter-Gateway)
8375        let found = ctx.find_segments("SEQ").iter().any(|s| {
8376            s.elements.first().and_then(|e| e.first())
8377                .is_some_and(|v| v == "Z13" || v == "ZG0")
8378        });
8379        ConditionResult::from(found)
8380    }
8381
8382    /// [2140] Für die ID der LieferantensummenZR einmal je SG4 IDE+Z01 (Liste)
8383    fn evaluate_2140(&self, ctx: &EvaluationContext) -> ConditionResult {
8384        // Cardinality: LieferantensummenZR once per SG4 IDE+Z01
8385        let ide_count = ctx.find_segments_with_qualifier("IDE", 0, "Z01").len();
8386        ConditionResult::from(ide_count > 0)
8387    }
8388
8389    /// [2182] Segmentgruppe ist genau einmal je SG8 SEQ+Z01/ Z80/ Z81/ Z98 (Daten der Marktlokation/ Erwartete Daten der Marktlokation/ Im System vorhandene Daten der Marktlokation) anzugeben
8390    fn evaluate_2182(&self, ctx: &EvaluationContext) -> ConditionResult {
8391        // Cardinality: exactly once per SG8 SEQ+Z01/Z80/Z81/Z98
8392        let found = ctx.find_segments("SEQ").iter().any(|s| {
8393            s.elements.first().and_then(|e| e.first())
8394                .is_some_and(|v| ["Z01", "Z80", "Z81", "Z98"].contains(&v.as_str()))
8395        });
8396        ConditionResult::from(found)
8397    }
8398
8399    /// [2183] Segmentgruppe ist genau zweimal je SG8 SEQ+Z01/ Z80/ Z81/ Z98 (Daten der Marktlokation/ Erwartete Daten der Marktlokation/ Im System vorhandene Daten der Marktlokation) anzugeben
8400    fn evaluate_2183(&self, ctx: &EvaluationContext) -> ConditionResult {
8401        // Cardinality: exactly twice per SG8 SEQ+Z01/Z80/Z81/Z98
8402        let found = ctx.find_segments("SEQ").iter().any(|s| {
8403            s.elements.first().and_then(|e| e.first())
8404                .is_some_and(|v| ["Z01", "Z80", "Z81", "Z98"].contains(&v.as_str()))
8405        });
8406        ConditionResult::from(found)
8407    }
8408
8409    /// [2207] Für die ID der Lieferanten-ausfall-arbeits-summen-zeitreihe einmal je SG4 IDE+Z01 (Liste)
8410    fn evaluate_2207(&self, ctx: &EvaluationContext) -> ConditionResult {
8411        // Cardinality: Lieferantenausfallarbeitssummenzeitreihe once per SG4 IDE+Z01
8412        let ide_count = ctx.find_segments_with_qualifier("IDE", 0, "Z01").len();
8413        ConditionResult::from(ide_count > 0)
8414    }
8415
8416    /// [2225] Einmal für jede Marktlokation bzw. Tranche für die der LF nicht die gemeldete Ansicht des NB teilt
8417    fn evaluate_2225(&self, _ctx: &EvaluationContext) -> ConditionResult {
8418        // TODO: implement
8419        ConditionResult::Unknown
8420    }
8421
8422    /// [2236] Code einmal je SG4 IDE+24 (Vorgang)
8423    fn evaluate_2236(&self, ctx: &EvaluationContext) -> ConditionResult {
8424        // Cardinality: code once per SG4 IDE+24
8425        let ide_count = ctx.find_segments_with_qualifier("IDE", 0, "24").len();
8426        ConditionResult::from(ide_count > 0)
8427    }
8428
8429    /// [2252] Einmal für jede Marktlokation bzw. Tranche, die in der Lieferantenausfallarbeits-summenzeitreihe berücksichtigt wurde
8430    fn evaluate_2252(&self, _ctx: &EvaluationContext) -> ConditionResult {
8431        // TODO: implement
8432        ConditionResult::Unknown
8433    }
8434
8435    /// [2261] Für jede ID im SG5 LOC+Z21 (Tranche) DE3225, mindestens einmal anzugeben
8436    fn evaluate_2261(&self, ctx: &EvaluationContext) -> ConditionResult {
8437        // Cardinality: at least once per LOC+Z21 (Tranche) ID
8438        let loc_count = ctx.find_segments_with_qualifier("LOC", 0, "Z21").len();
8439        ConditionResult::from(loc_count > 0)
8440    }
8441
8442    /// [2284] Für jede Messlokations-ID im SG5 LOC+Z17 (Messlokation) DE3225 genau einmal anzugeben
8443    fn evaluate_2284(&self, ctx: &EvaluationContext) -> ConditionResult {
8444        // Cardinality: exactly once per LOC+Z17 (Messlokation) ID
8445        let loc_count = ctx.find_segments_with_qualifier("LOC", 0, "Z17").len();
8446        ConditionResult::from(loc_count > 0)
8447    }
8448
8449    /// [2286] Für jede SEQ+ZF3 (Daten der Messlokation) mindestens einmal anzugeben
8450    fn evaluate_2286(&self, ctx: &EvaluationContext) -> ConditionResult {
8451        // Cardinality: at least once per SEQ+ZF3 (Daten der Messlokation)
8452        let found = !ctx.find_segments_with_qualifier("SEQ", 0, "ZF3").is_empty();
8453        ConditionResult::from(found)
8454    }
8455
8456    /// [2287] Für jede SEQ+Z03/ ZF5 (Zähleinrichtungsdaten) mindestens einmal anzugeben
8457    fn evaluate_2287(&self, ctx: &EvaluationContext) -> ConditionResult {
8458        // Cardinality: at least once per SEQ+Z03/ZF5 (Zähleinrichtungsdaten)
8459        let found = ctx.find_segments("SEQ").iter().any(|s| {
8460            s.elements.first().and_then(|e| e.first())
8461                .is_some_and(|v| v == "Z03" || v == "ZF5")
8462        });
8463        ConditionResult::from(found)
8464    }
8465
8466    /// [2288] Einmal für jede Zeitreihe, die in der DZR berücksichtigt wurde
8467    fn evaluate_2288(&self, _ctx: &EvaluationContext) -> ConditionResult {
8468        // TODO: implement
8469        ConditionResult::Unknown
8470    }
8471
8472    /// [2307] Für jede ID im SG5 LOC+Z21 (Tranche) DE3225 genau einmal anzugeben
8473    fn evaluate_2307(&self, ctx: &EvaluationContext) -> ConditionResult {
8474        // Cardinality: exactly once per LOC+Z21 (Tranche) ID
8475        let loc_count = ctx.find_segments_with_qualifier("LOC", 0, "Z21").len();
8476        ConditionResult::from(loc_count > 0)
8477    }
8478
8479    /// [2308] Für jede ID im SG5 LOC+Z16 (Marktlokation) DE3225, mindestens einmal anzugeben
8480    fn evaluate_2308(&self, ctx: &EvaluationContext) -> ConditionResult {
8481        // Cardinality: at least once per LOC+Z16 (Marktlokation) ID
8482        let loc_count = ctx.find_segments_with_qualifier("LOC", 0, "Z16").len();
8483        ConditionResult::from(loc_count > 0)
8484    }
8485
8486    /// [2309] Für jede ID im SG5 LOC+Z17 (Messlokation) DE3225 mindestens einmal anzugeben
8487    fn evaluate_2309(&self, ctx: &EvaluationContext) -> ConditionResult {
8488        // Cardinality: at least once per LOC+Z17 (Messlokation) ID
8489        let loc_count = ctx.find_segments_with_qualifier("LOC", 0, "Z17").len();
8490        ConditionResult::from(loc_count > 0)
8491    }
8492
8493    /// [2310] Für jede ID im SG5 LOC+Z16 (Marktlokation) DE3225 genau einmal anzugeben
8494    fn evaluate_2310(&self, ctx: &EvaluationContext) -> ConditionResult {
8495        // Cardinality: exactly once per LOC+Z16 (Marktlokation) ID
8496        let loc_count = ctx.find_segments_with_qualifier("LOC", 0, "Z16").len();
8497        ConditionResult::from(loc_count > 0)
8498    }
8499
8500    /// [2311] Für jede ID im SG5 LOC+Z21 (Tranche) DE3225, mindestens einmal anzugeben
8501    fn evaluate_2311(&self, ctx: &EvaluationContext) -> ConditionResult {
8502        // Cardinality: at least once per LOC+Z21 (Tranche) ID
8503        let loc_count = ctx.find_segments_with_qualifier("LOC", 0, "Z21").len();
8504        ConditionResult::from(loc_count > 0)
8505    }
8506
8507    /// [2312] Wenn der Objektcode \"9992000001256\" (Netzlokation) im DE1154 des selben RFF+Z33 nicht vorhanden ist, ist das RFF+Z33 in derselben SG8 SEQ+Z58/ ZC9/ ZD0/ ZD6 (Zuordnung Lokation zum Objektcode des...
8508    fn evaluate_2312(&self, ctx: &EvaluationContext) -> ConditionResult {
8509        ctx.lacks_qualifier("RFF", 0, "Z33")
8510
8511    }
8512
8513    /// [2313] Je SG8 SEQ+Z58/ ZC9/ ZD0/ ZD6 (Zuordnung Lokation zum Objektcode des Lokationsbündels) genau einmal anzugeben
8514    fn evaluate_2313(&self, ctx: &EvaluationContext) -> ConditionResult {
8515        // Check: SG8 with SEQ+Z58/ZC9/ZD0/ZD6 exists (cardinality constraint)
8516        let found = ctx.find_segments("SEQ").iter().any(|s| {
8517            s.elements.first().and_then(|e| e.first())
8518                .is_some_and(|v| ["Z58", "ZC9", "ZD0", "ZD6"].contains(&v.as_str()))
8519        });
8520        ConditionResult::from(found)
8521    }
8522
8523    /// [2317] Wenn in derselben SG8 SEQ+Z04/ ZF7 (Wandlerdaten) das SG10 CCI+++Z25 (Wandler) CAV+MIW/MPW/MUW vorhanden, ist das Segment mindestens zweimal anzugeben
8524    fn evaluate_2317(&self, ctx: &EvaluationContext) -> ConditionResult {
8525        let cci_segs = ctx.find_segments("CCI");
8526        let has_code = cci_segs.iter().any(|s| {
8527            s.elements.get(2)
8528                .and_then(|e| e.first())
8529                .is_some_and(|v| v == "Z25")
8530        });
8531        if !has_code {
8532            return ConditionResult::from(false);
8533        }
8534        let cav_segs = ctx.find_segments("CAV");
8535        let found = cav_segs.iter().any(|s| {
8536            s.elements.first()
8537                .and_then(|e| e.first())
8538                .is_some_and(|v| ["MIW", "MPW", "MUW"].contains(&v.as_str()))
8539        });
8540        ConditionResult::from(found)
8541
8542    }
8543
8544    /// [2318] Wenn in derselben SG8 SEQ+Z04/ ZF7 (Wandlerdaten) das SG10 CCI+++Z25 (Wandler) CAV+MBW (Blockstromwandler) vorhanden, ist das Segment genau einmal anzugeben
8545    fn evaluate_2318(&self, ctx: &EvaluationContext) -> ConditionResult {
8546        let cci_segs = ctx.find_segments("CCI");
8547        let has_code = cci_segs.iter().any(|s| {
8548            s.elements.get(2)
8549                .and_then(|e| e.first())
8550                .is_some_and(|v| v == "Z25")
8551        });
8552        if !has_code {
8553            return ConditionResult::from(false);
8554        }
8555        let cav_segs = ctx.find_segments("CAV");
8556        let found = cav_segs.iter().any(|s| {
8557            s.elements.first()
8558                .and_then(|e| e.first())
8559                .is_some_and(|v| ["MBW"].contains(&v.as_str()))
8560        });
8561        ConditionResult::from(found)
8562
8563    }
8564
8565    /// [2344] Einmal für jede Marktlokation bzw. Tranche, die in der DZÜ / BG-CL / LF-SZR berücksichtigt wurde
8566    fn evaluate_2344(&self, _ctx: &EvaluationContext) -> ConditionResult {
8567        // TODO: implement
8568        ConditionResult::Unknown
8569    }
8570
8571    /// [2350] Für jedes SMGW das im SEQ+Z13/ ZG0 (Smartmeter-Gateway) SG10 CCI+++Z75 CAV+Z30 (Gerätenummer) genannt ist, mindestens einmal je SEQ+Z03/ ZF5 (Zähleinrichtungsdaten) das mit SG8 RFF+Z14 (Referenz...
8572    fn evaluate_2350(&self, ctx: &EvaluationContext) -> ConditionResult {
8573        let cci_segs = ctx.find_segments("CCI");
8574        let has_code = cci_segs.iter().any(|s| {
8575            s.elements.get(2)
8576                .and_then(|e| e.first())
8577                .is_some_and(|v| v == "Z75")
8578        });
8579        if !has_code {
8580            return ConditionResult::from(false);
8581        }
8582        let cav_segs = ctx.find_segments("CAV");
8583        let found = cav_segs.iter().any(|s| {
8584            s.elements.first()
8585                .and_then(|e| e.first())
8586                .is_some_and(|v| ["Z30"].contains(&v.as_str()))
8587        });
8588        ConditionResult::from(found)
8589
8590    }
8591
8592    /// [2351] Einmal je ZP einer BK-SZR, in der die Marktlokation in die Berechnung der abgerechneten Summenzeitreihe aufgenommen wurde
8593    fn evaluate_2351(&self, _ctx: &EvaluationContext) -> ConditionResult {
8594        // TODO: implement
8595        ConditionResult::Unknown
8596    }
8597
8598    /// [2352] Einmal je ZP einer BG-SZR, in der die Marktlokation in die Berechnung der abgerechneten Summenzeitreihe aufge-nommen wurde
8599    fn evaluate_2352(&self, _ctx: &EvaluationContext) -> ConditionResult {
8600        // TODO: implement
8601        ConditionResult::Unknown
8602    }
8603
8604    /// [2356] Je SG5 LOC+Z18 (Netzlokation) ist genau einmal die Segmentgruppe anzugeben
8605    fn evaluate_2356(&self, ctx: &EvaluationContext) -> ConditionResult {
8606        // Cardinality: exactly once per SG5 LOC+Z18 (Netzlokation)
8607        let loc_count = ctx.find_segments_with_qualifier("LOC", 0, "Z18").len();
8608        ConditionResult::from(loc_count > 0)
8609    }
8610
8611    /// [2357] Je SG5 LOC+Z20 (Technische Ressource) ist genau einmal die Segmentgruppe anzugeben
8612    fn evaluate_2357(&self, ctx: &EvaluationContext) -> ConditionResult {
8613        // Cardinality: exactly once per SG5 LOC+Z20 (Technische Ressource)
8614        let loc_count = ctx.find_segments_with_qualifier("LOC", 0, "Z20").len();
8615        ConditionResult::from(loc_count > 0)
8616    }
8617
8618    /// [2358] Je SG5 LOC+Z19 (Steuerbare Ressource) ist genau einmal die Segmentgruppe anzugeben
8619    fn evaluate_2358(&self, ctx: &EvaluationContext) -> ConditionResult {
8620        // Cardinality: exactly once per SG5 LOC+Z19 (Steuerbare Ressource)
8621        let loc_count = ctx.find_segments_with_qualifier("LOC", 0, "Z19").len();
8622        ConditionResult::from(loc_count > 0)
8623    }
8624
8625    /// [2359] Für jede ID im SG5 LOC+Z16 / Z17 / Z20 (Marktlokation / Messlokation / Technische Ressource) DE3225 mindestens einmal anzugeben
8626    fn evaluate_2359(&self, ctx: &EvaluationContext) -> ConditionResult {
8627        // Cardinality: at least once per LOC+Z16/Z17/Z20 ID
8628        let found = ctx.find_segments("LOC").iter().any(|s| {
8629            s.elements.first().and_then(|e| e.first())
8630                .is_some_and(|v| ["Z16", "Z17", "Z20"].contains(&v.as_str()))
8631        });
8632        ConditionResult::from(found)
8633    }
8634
8635    /// [2360] Für jede ID im SG5 LOC+Z18 (Netzlokation) DE3225 mindestens einmal anzugeben
8636    fn evaluate_2360(&self, ctx: &EvaluationContext) -> ConditionResult {
8637        // Cardinality: at least once per LOC+Z18 (Netzlokation) ID
8638        let loc_count = ctx.find_segments_with_qualifier("LOC", 0, "Z18").len();
8639        ConditionResult::from(loc_count > 0)
8640    }
8641
8642    /// [2361] Für jede ID im SG5 LOC+Z17 / Z20 / Z22 (Messlokation / Technische Ressource / ruhende Marktlokation) DE3225 mindestens einmal anzugeben
8643    fn evaluate_2361(&self, ctx: &EvaluationContext) -> ConditionResult {
8644        // Cardinality: at least once per LOC+Z17/Z20/Z22 ID
8645        let found = ctx.find_segments("LOC").iter().any(|s| {
8646            s.elements.first().and_then(|e| e.first())
8647                .is_some_and(|v| ["Z17", "Z20", "Z22"].contains(&v.as_str()))
8648        });
8649        ConditionResult::from(found)
8650    }
8651}