1use crate::{error::BuildError, CanonicalizationAlgorithm, FidelityOptions};
8use serde::{Deserialize, Serialize};
9use std::collections::HashMap;
10use std::time::{Duration, Instant};
11
12pub struct BuildVerifier {
14    config: VerificationConfig,
15}
16
17impl BuildVerifier {
18    pub fn new(config: VerificationConfig) -> Self {
20        Self { config }
21    }
22
23    pub fn verify(
25        &self,
26        xml_output: &str,
27        fidelity_options: &FidelityOptions,
28    ) -> Result<VerificationResult, BuildError> {
29        let start_time = Instant::now();
30        let mut issues = Vec::new();
31
32        let mut round_trip_success = true;
34        let mut canonicalization_success = true;
35        let mut schema_validation_success = true;
36        let mut determinism_success = true;
37
38        if self.config.enable_round_trip_verification {
40            match self.verify_round_trip(xml_output, fidelity_options) {
41                Ok(result) => {
42                    if !result.success {
43                        round_trip_success = false;
44                        issues.extend(result.issues);
45                    }
46                }
47                Err(e) => {
48                    round_trip_success = false;
49                    issues.push(VerificationIssue {
50                        severity: VerificationSeverity::Error,
51                        category: "round-trip".to_string(),
52                        message: format!("Round-trip verification failed: {}", e),
53                        path: None,
54                        suggestion: Some("Check XML structure and fidelity options".to_string()),
55                    });
56                }
57            }
58        }
59
60        if self.config.enable_canonicalization_verification {
62            match self.verify_canonicalization(xml_output, fidelity_options) {
63                Ok(result) => {
64                    if !result.success {
65                        canonicalization_success = false;
66                        issues.extend(result.issues);
67                    }
68                }
69                Err(e) => {
70                    canonicalization_success = false;
71                    issues.push(VerificationIssue {
72                        severity: VerificationSeverity::Error,
73                        category: "canonicalization".to_string(),
74                        message: format!("Canonicalization verification failed: {}", e),
75                        path: None,
76                        suggestion: Some("Check canonicalization settings".to_string()),
77                    });
78                }
79            }
80        }
81
82        if self.config.enable_schema_validation {
84            match self.verify_schema(xml_output) {
85                Ok(result) => {
86                    if !result.success {
87                        schema_validation_success = false;
88                        issues.extend(result.issues);
89                    }
90                }
91                Err(e) => {
92                    schema_validation_success = false;
93                    issues.push(VerificationIssue {
94                        severity: VerificationSeverity::Error,
95                        category: "schema".to_string(),
96                        message: format!("Schema validation failed: {}", e),
97                        path: None,
98                        suggestion: Some("Check XML against DDEX schema".to_string()),
99                    });
100                }
101            }
102        }
103
104        if self.config.enable_determinism_verification {
106            match self.verify_determinism(xml_output, fidelity_options) {
107                Ok(result) => {
108                    if !result.success {
109                        determinism_success = false;
110                        issues.extend(result.issues);
111                    }
112                }
113                Err(e) => {
114                    determinism_success = false;
115                    issues.push(VerificationIssue {
116                        severity: VerificationSeverity::Error,
117                        category: "determinism".to_string(),
118                        message: format!("Determinism verification failed: {}", e),
119                        path: None,
120                        suggestion: Some("Check determinism configuration".to_string()),
121                    });
122                }
123            }
124        }
125
126        let verification_time = start_time.elapsed();
127        let overall_success = round_trip_success
128            && canonicalization_success
129            && schema_validation_success
130            && determinism_success;
131
132        Ok(VerificationResult {
133            success: overall_success,
134            round_trip_success,
135            canonicalization_success,
136            schema_validation_success,
137            determinism_success,
138            issues,
139            verification_time,
140        })
141    }
142
143    fn verify_round_trip(
145        &self,
146        _xml_output: &str,
147        _fidelity_options: &FidelityOptions,
148    ) -> Result<RoundTripVerificationResult, BuildError> {
149        let issues = Vec::new();
152
153        Ok(RoundTripVerificationResult {
162            success: true, issues,
164        })
165    }
166
167    fn verify_canonicalization(
169        &self,
170        xml_output: &str,
171        fidelity_options: &FidelityOptions,
172    ) -> Result<CanonicalizationVerificationResult, BuildError> {
173        let mut issues = Vec::new();
174        let mut success = true;
175
176        match &fidelity_options.canonicalization {
177            CanonicalizationAlgorithm::None => {
178                if let Err(e) = quick_xml::Reader::from_str(xml_output).read_event() {
180                    success = false;
181                    issues.push(VerificationIssue {
182                        severity: VerificationSeverity::Error,
183                        category: "xml-wellformed".to_string(),
184                        message: format!("XML is not well-formed: {}", e),
185                        path: None,
186                        suggestion: Some("Check XML syntax".to_string()),
187                    });
188                }
189            }
190            CanonicalizationAlgorithm::C14N
191            | CanonicalizationAlgorithm::C14N11
192            | CanonicalizationAlgorithm::DbC14N => {
193                let mut canonicalized_versions = Vec::new();
195
196                for _ in 0..3 {
197                    match self.canonicalize_xml(xml_output, &fidelity_options.canonicalization) {
198                        Ok(canonical) => canonicalized_versions.push(canonical),
199                        Err(e) => {
200                            success = false;
201                            issues.push(VerificationIssue {
202                                severity: VerificationSeverity::Error,
203                                category: "canonicalization".to_string(),
204                                message: format!("Canonicalization failed: {}", e),
205                                path: None,
206                                suggestion: Some(
207                                    "Check canonicalization algorithm settings".to_string(),
208                                ),
209                            });
210                            break;
211                        }
212                    }
213                }
214
215                if canonicalized_versions.len() >= 2 {
216                    let first = &canonicalized_versions[0];
217                    for (i, version) in canonicalized_versions.iter().enumerate().skip(1) {
218                        if first != version {
219                            success = false;
220                            issues.push(VerificationIssue {
221                                severity: VerificationSeverity::Error,
222                                category: "canonicalization-consistency".to_string(),
223                                message: format!(
224                                    "Canonicalization is not deterministic: iteration {} differs",
225                                    i + 1
226                                ),
227                                path: None,
228                                suggestion: Some(
229                                    "Check for non-deterministic elements in canonicalization"
230                                        .to_string(),
231                                ),
232                            });
233                        }
234                    }
235                }
236            }
237            CanonicalizationAlgorithm::Custom(_rules) => {
238                issues.push(VerificationIssue {
241                    severity: VerificationSeverity::Info,
242                    category: "canonicalization".to_string(),
243                    message: "Custom canonicalization verification not yet implemented".to_string(),
244                    path: None,
245                    suggestion: None,
246                });
247            }
248        }
249
250        Ok(CanonicalizationVerificationResult { success, issues })
251    }
252
253    fn verify_schema(&self, xml_output: &str) -> Result<SchemaVerificationResult, BuildError> {
255        let mut issues = Vec::new();
256        let mut success = true;
257
258        let mut reader = quick_xml::Reader::from_str(xml_output);
260
261        loop {
262            match reader.read_event() {
263                Ok(quick_xml::events::Event::Eof) => break,
264                Ok(_) => continue,
265                Err(e) => {
266                    success = false;
267                    issues.push(VerificationIssue {
268                        severity: VerificationSeverity::Error,
269                        category: "xml-syntax".to_string(),
270                        message: format!("XML syntax error: {}", e),
271                        path: Some(format!("position {}", reader.buffer_position())),
272                        suggestion: Some("Fix XML syntax errors".to_string()),
273                    });
274                    break;
275                }
276            }
277        }
278
279        Ok(SchemaVerificationResult { success, issues })
284    }
285
286    fn verify_determinism(
288        &self,
289        xml_output: &str,
290        fidelity_options: &FidelityOptions,
291    ) -> Result<DeterminismVerificationResult, BuildError> {
292        let mut issues = Vec::new();
293        let mut success = true;
294
295        let non_deterministic_patterns = [
297            (
298                r#"\btimestamp\s*=\s*['"][^'"]*['"]"#,
299                "timestamp attributes",
300            ),
301            (
302                r#"\bcreated\s*=\s*['"][^'"]*['"]"#,
303                "creation time attributes",
304            ),
305            (r#"\buuid\s*=\s*['"][^'"]*['"]"#, "UUID attributes"),
306            (r#"\bid\s*=\s*['"]uuid:[^'"]*['"]"#, "UUID-based IDs"),
307        ];
308
309        for (pattern, description) in non_deterministic_patterns {
310            if let Ok(re) = regex::Regex::new(pattern) {
311                if re.is_match(xml_output) {
312                    issues.push(VerificationIssue {
313                        severity: VerificationSeverity::Warning,
314                        category: "determinism".to_string(),
315                        message: format!(
316                            "Potentially non-deterministic element detected: {}",
317                            description
318                        ),
319                        path: None,
320                        suggestion: Some(
321                            "Use content-based IDs instead of random values".to_string(),
322                        ),
323                    });
324                }
325            }
326        }
327
328        if !fidelity_options.preserve_attribute_order {
330            if let Ok(attribute_order_regex) = regex::Regex::new(r#"<\w+[^>]*>"#) {
332                for attr_match in attribute_order_regex.find_iter(xml_output) {
333                    let element = attr_match.as_str();
334                    if !self.is_attribute_order_deterministic(element) {
335                        success = false;
336                        issues.push(VerificationIssue {
337                            severity: VerificationSeverity::Error,
338                            category: "determinism".to_string(),
339                            message: "Attributes are not in deterministic order".to_string(),
340                            path: Some(element.to_string()),
341                            suggestion: Some("Enable deterministic attribute ordering".to_string()),
342                        });
343                    }
344                }
345            }
346        }
347
348        Ok(DeterminismVerificationResult { success, issues })
349    }
350
351    fn is_attribute_order_deterministic(&self, element_str: &str) -> bool {
353        if let Ok(attr_regex) = regex::Regex::new(r#"(\w+)\s*=\s*['"][^'"]*['"]"#) {
355            let mut attributes: Vec<&str> = attr_regex
356                .captures_iter(element_str)
357                .filter_map(|cap| cap.get(1).map(|m| m.as_str()))
358                .collect();
359
360            let original_order = attributes.clone();
361            attributes.sort();
362
363            original_order == attributes
364        } else {
365            true
367        }
368    }
369
370    fn canonicalize_xml(
372        &self,
373        xml: &str,
374        algorithm: &CanonicalizationAlgorithm,
375    ) -> Result<String, BuildError> {
376        match algorithm {
377            CanonicalizationAlgorithm::None => Ok(xml.to_string()),
378            CanonicalizationAlgorithm::C14N => {
379                Ok(xml.to_string()) }
382            CanonicalizationAlgorithm::C14N11 => {
383                Ok(xml.to_string()) }
386            CanonicalizationAlgorithm::DbC14N => {
387                Ok(xml.to_string()) }
390            CanonicalizationAlgorithm::Custom(_rules) => {
391                Ok(xml.to_string()) }
394        }
395    }
396}
397
398#[derive(Debug, Clone, Serialize, Deserialize)]
400pub struct VerificationConfig {
401    pub enable_round_trip_verification: bool,
403    pub enable_canonicalization_verification: bool,
405    pub enable_schema_validation: bool,
407    pub enable_determinism_verification: bool,
409    pub determinism_test_iterations: usize,
411    pub verification_timeout: Duration,
413}
414
415impl Default for VerificationConfig {
416    fn default() -> Self {
417        Self {
418            enable_round_trip_verification: true,
419            enable_canonicalization_verification: true,
420            enable_schema_validation: false,
421            enable_determinism_verification: true,
422            determinism_test_iterations: 3,
423            verification_timeout: Duration::from_secs(30),
424        }
425    }
426}
427
428#[derive(Debug, Clone, Serialize, Deserialize)]
430pub struct VerificationResult {
431    pub success: bool,
433    pub round_trip_success: bool,
435    pub canonicalization_success: bool,
437    pub schema_validation_success: bool,
439    pub determinism_success: bool,
441    pub issues: Vec<VerificationIssue>,
443    pub verification_time: Duration,
445}
446
447#[derive(Debug, Clone, Serialize, Deserialize)]
449pub struct VerificationIssue {
450    pub severity: VerificationSeverity,
452    pub category: String,
454    pub message: String,
456    pub path: Option<String>,
458    pub suggestion: Option<String>,
460}
461
462#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
464pub enum VerificationSeverity {
465    Error,
467    Warning,
469    Info,
471}
472
473#[derive(Debug, Clone)]
475struct RoundTripVerificationResult {
476    success: bool,
477    issues: Vec<VerificationIssue>,
478}
479
480#[derive(Debug, Clone)]
482struct CanonicalizationVerificationResult {
483    success: bool,
484    issues: Vec<VerificationIssue>,
485}
486
487#[derive(Debug, Clone)]
489struct SchemaVerificationResult {
490    success: bool,
491    issues: Vec<VerificationIssue>,
492}
493
494#[derive(Debug, Clone)]
496struct DeterminismVerificationResult {
497    success: bool,
498    issues: Vec<VerificationIssue>,
499}
500
501#[derive(Debug, Clone, Serialize, Deserialize)]
503pub struct VerificationReport {
504    pub config: VerificationConfig,
506    pub fidelity_options: FidelityOptions,
508    pub result: VerificationResult,
510    pub statistics: VerificationStatistics,
512    pub recommendations: Vec<String>,
514}
515
516#[derive(Debug, Clone, Serialize, Deserialize)]
518pub struct VerificationStatistics {
519    pub elements_verified: usize,
521    pub attributes_verified: usize,
523    pub namespaces_processed: usize,
525    pub comments_verified: usize,
527    pub processing_instructions_verified: usize,
529    pub extensions_verified: HashMap<String, usize>,
531    pub memory_usage: usize,
533}
534
535impl Default for VerificationStatistics {
536    fn default() -> Self {
537        Self {
538            elements_verified: 0,
539            attributes_verified: 0,
540            namespaces_processed: 0,
541            comments_verified: 0,
542            processing_instructions_verified: 0,
543            extensions_verified: HashMap::new(),
544            memory_usage: 0,
545        }
546    }
547}
548
549#[cfg(test)]
550mod tests {
551    use super::*;
552
553    #[test]
554    fn test_verification_config_default() {
555        let config = VerificationConfig::default();
556        assert!(config.enable_round_trip_verification);
557        assert!(config.enable_canonicalization_verification);
558        assert!(!config.enable_schema_validation);
559        assert!(config.enable_determinism_verification);
560        assert_eq!(config.determinism_test_iterations, 3);
561    }
562
563    #[test]
564    fn test_build_verifier_creation() {
565        let config = VerificationConfig::default();
566        let verifier = BuildVerifier::new(config);
567        assert_eq!(verifier.config.determinism_test_iterations, 3);
568    }
569
570    #[test]
571    fn test_attribute_order_determinism() {
572        let verifier = BuildVerifier::new(VerificationConfig::default());
573
574        assert!(verifier.is_attribute_order_deterministic(r#"<element a="1" b="2" c="3">"#));
576
577        assert!(!verifier.is_attribute_order_deterministic(r#"<element c="3" a="1" b="2">"#));
579    }
580
581    #[test]
582    fn test_verification_issue_creation() {
583        let issue = VerificationIssue {
584            severity: VerificationSeverity::Error,
585            category: "test".to_string(),
586            message: "Test issue".to_string(),
587            path: Some("/test/path".to_string()),
588            suggestion: Some("Fix the test".to_string()),
589        };
590
591        assert_eq!(issue.severity, VerificationSeverity::Error);
592        assert_eq!(issue.category, "test");
593        assert_eq!(issue.message, "Test issue");
594    }
595
596    #[test]
597    fn test_verification_statistics() {
598        let stats = VerificationStatistics::default();
599        assert_eq!(stats.elements_verified, 0);
600        assert_eq!(stats.attributes_verified, 0);
601        assert_eq!(stats.memory_usage, 0);
602    }
603}