Skip to main content

datasynth_config/
schema.rs

1//! Configuration schema for synthetic data generation.
2
3use datasynth_core::distributions::{
4    AmountDistributionConfig, DebitCreditDistributionConfig, EvenOddDistributionConfig,
5    LineItemDistributionConfig, SeasonalityConfig,
6};
7use datasynth_core::models::{CoAComplexity, IndustrySector};
8use serde::{Deserialize, Serialize};
9use std::path::PathBuf;
10
11/// Root configuration for the synthetic data generator.
12#[derive(Debug, Clone, Serialize, Deserialize)]
13pub struct GeneratorConfig {
14    /// Global settings
15    pub global: GlobalConfig,
16    /// Company configuration
17    pub companies: Vec<CompanyConfig>,
18    /// Chart of Accounts configuration
19    pub chart_of_accounts: ChartOfAccountsConfig,
20    /// Transaction generation settings
21    #[serde(default)]
22    pub transactions: TransactionConfig,
23    /// Output configuration
24    pub output: OutputConfig,
25    /// Fraud simulation settings
26    #[serde(default)]
27    pub fraud: FraudConfig,
28    /// Data quality variation settings
29    #[serde(default)]
30    pub data_quality: DataQualitySchemaConfig,
31    /// Internal Controls System settings
32    #[serde(default)]
33    pub internal_controls: InternalControlsConfig,
34    /// Business process mix
35    #[serde(default)]
36    pub business_processes: BusinessProcessConfig,
37    /// User persona distribution
38    #[serde(default)]
39    pub user_personas: UserPersonaConfig,
40    /// Template configuration for realistic data
41    #[serde(default)]
42    pub templates: TemplateConfig,
43    /// Approval workflow configuration
44    #[serde(default)]
45    pub approval: ApprovalConfig,
46    /// Department structure configuration
47    #[serde(default)]
48    pub departments: DepartmentConfig,
49    /// Master data generation settings
50    #[serde(default)]
51    pub master_data: MasterDataConfig,
52    /// Document flow generation settings
53    #[serde(default)]
54    pub document_flows: DocumentFlowConfig,
55    /// Intercompany transaction settings
56    #[serde(default)]
57    pub intercompany: IntercompanyConfig,
58    /// Balance and trial balance settings
59    #[serde(default)]
60    pub balance: BalanceConfig,
61    /// OCPM (Object-Centric Process Mining) settings
62    #[serde(default)]
63    pub ocpm: OcpmConfig,
64    /// Audit engagement and workpaper generation settings
65    #[serde(default)]
66    pub audit: AuditGenerationConfig,
67    /// Banking KYC/AML transaction generation settings
68    #[serde(default)]
69    pub banking: datasynth_banking::BankingConfig,
70    /// Scenario configuration for metadata and tagging (Phase 1.3)
71    #[serde(default)]
72    pub scenario: ScenarioConfig,
73    /// Temporal drift configuration for simulating distribution changes over time (Phase 2.2)
74    #[serde(default)]
75    pub temporal: TemporalDriftConfig,
76    /// Graph export configuration for accounting network export
77    #[serde(default)]
78    pub graph_export: GraphExportConfig,
79    /// Streaming output API configuration
80    #[serde(default)]
81    pub streaming: StreamingSchemaConfig,
82    /// Rate limiting configuration
83    #[serde(default)]
84    pub rate_limit: RateLimitSchemaConfig,
85    /// Temporal attribute generation configuration
86    #[serde(default)]
87    pub temporal_attributes: TemporalAttributeSchemaConfig,
88    /// Relationship generation configuration
89    #[serde(default)]
90    pub relationships: RelationshipSchemaConfig,
91    /// Accounting standards framework configuration (IFRS, US GAAP)
92    #[serde(default)]
93    pub accounting_standards: AccountingStandardsConfig,
94    /// Audit standards framework configuration (ISA, PCAOB)
95    #[serde(default)]
96    pub audit_standards: AuditStandardsConfig,
97    /// Advanced distribution configuration (mixture models, correlations, regime changes)
98    #[serde(default)]
99    pub distributions: AdvancedDistributionConfig,
100    /// Temporal patterns configuration (business days, period-end dynamics, processing lags)
101    #[serde(default)]
102    pub temporal_patterns: TemporalPatternsConfig,
103    /// Vendor network configuration (multi-tier supply chain modeling)
104    #[serde(default)]
105    pub vendor_network: VendorNetworkSchemaConfig,
106    /// Customer segmentation configuration (value segments, lifecycle stages)
107    #[serde(default)]
108    pub customer_segmentation: CustomerSegmentationSchemaConfig,
109    /// Relationship strength calculation configuration
110    #[serde(default)]
111    pub relationship_strength: RelationshipStrengthSchemaConfig,
112    /// Cross-process link configuration (P2P ↔ O2C via inventory)
113    #[serde(default)]
114    pub cross_process_links: CrossProcessLinksSchemaConfig,
115    /// Organizational events configuration (acquisitions, divestitures, etc.)
116    #[serde(default)]
117    pub organizational_events: OrganizationalEventsSchemaConfig,
118    /// Behavioral drift configuration (vendor, customer, employee behavior)
119    #[serde(default)]
120    pub behavioral_drift: BehavioralDriftSchemaConfig,
121    /// Market drift configuration (economic cycles, commodities, price shocks)
122    #[serde(default)]
123    pub market_drift: MarketDriftSchemaConfig,
124    /// Drift labeling configuration for ground truth generation
125    #[serde(default)]
126    pub drift_labeling: DriftLabelingSchemaConfig,
127    /// Enhanced anomaly injection configuration (multi-stage schemes, correlated injection, near-miss)
128    #[serde(default)]
129    pub anomaly_injection: EnhancedAnomalyConfig,
130    /// Industry-specific transaction and anomaly generation configuration
131    #[serde(default)]
132    pub industry_specific: IndustrySpecificConfig,
133    /// Fingerprint privacy configuration for extraction/synthesis
134    #[serde(default)]
135    pub fingerprint_privacy: FingerprintPrivacyConfig,
136    /// Quality gate configuration for pass/fail thresholds
137    #[serde(default)]
138    pub quality_gates: QualityGatesSchemaConfig,
139    /// Compliance configuration (EU AI Act, content marking)
140    #[serde(default)]
141    pub compliance: ComplianceSchemaConfig,
142    /// Webhook notification configuration
143    #[serde(default)]
144    pub webhooks: WebhookSchemaConfig,
145    /// LLM enrichment configuration (AI-augmented vendor names, descriptions, explanations)
146    #[serde(default)]
147    pub llm: LlmSchemaConfig,
148    /// Diffusion model configuration (statistical diffusion-based data enhancement)
149    #[serde(default)]
150    pub diffusion: DiffusionSchemaConfig,
151    /// Causal generation configuration (structural causal models, interventions)
152    #[serde(default)]
153    pub causal: CausalSchemaConfig,
154
155    // ===== Enterprise Process Chain Extensions =====
156    /// Source-to-Pay (S2C/S2P) configuration (sourcing, contracts, catalogs, scorecards)
157    #[serde(default)]
158    pub source_to_pay: SourceToPayConfig,
159    /// Financial reporting configuration (financial statements, KPIs, budgets)
160    #[serde(default)]
161    pub financial_reporting: FinancialReportingConfig,
162    /// HR process configuration (payroll, time & attendance, expenses)
163    #[serde(default)]
164    pub hr: HrConfig,
165    /// Manufacturing configuration (production orders, WIP, routing)
166    #[serde(default)]
167    pub manufacturing: ManufacturingProcessConfig,
168    /// Sales quote configuration (quote-to-order pipeline)
169    #[serde(default)]
170    pub sales_quotes: SalesQuoteConfig,
171    /// Tax accounting configuration (VAT/GST, sales tax, withholding, provisions, payroll tax)
172    #[serde(default)]
173    pub tax: TaxConfig,
174    /// Treasury and cash management configuration
175    #[serde(default)]
176    pub treasury: TreasuryConfig,
177    /// Project accounting configuration
178    #[serde(default)]
179    pub project_accounting: ProjectAccountingConfig,
180    /// ESG / Sustainability reporting configuration
181    #[serde(default)]
182    pub esg: EsgConfig,
183    /// Country pack configuration (external packs directory, per-country overrides)
184    #[serde(default)]
185    pub country_packs: Option<CountryPacksSchemaConfig>,
186    /// Counterfactual simulation scenario configuration
187    #[serde(default)]
188    pub scenarios: ScenariosConfig,
189    /// Generation session configuration (period-by-period generation with balance carry-forward)
190    #[serde(default)]
191    pub session: SessionSchemaConfig,
192    /// Compliance regulations framework configuration (standards registry, jurisdictions, temporal versioning, audit templates, graph integration)
193    #[serde(default)]
194    pub compliance_regulations: ComplianceRegulationsConfig,
195    /// v3.3.0: analytics metadata phase — prior-year comparatives,
196    /// industry benchmarks, management reports, drift events. Off by
197    /// default so v3.2.1 archives are byte-identical.
198    #[serde(default)]
199    pub analytics_metadata: AnalyticsMetadataConfig,
200}
201
202/// v3.3.0: analytics-metadata phase configuration.
203///
204/// Gates the `phase_analytics_metadata` pass that runs AFTER all
205/// JE-adding phases (including the fraud-bias sweep at Phase 20b).
206/// When enabled, the orchestrator calls `PriorYearGenerator`,
207/// `IndustryBenchmarkGenerator`, `ManagementReportGenerator`, and
208/// `DriftEventGenerator` in sequence; each sub-flag below controls
209/// whether that specific generator fires.
210#[derive(Debug, Clone, Serialize, Deserialize)]
211pub struct AnalyticsMetadataConfig {
212    /// Master switch for the whole analytics phase.
213    #[serde(default)]
214    pub enabled: bool,
215    /// Emit `PriorYearComparative` records derived from current
216    /// period's account balances.
217    #[serde(default = "default_true")]
218    pub prior_year: bool,
219    /// Emit `IndustryBenchmark` records for the configured industry.
220    #[serde(default = "default_true")]
221    pub industry_benchmark: bool,
222    /// Emit management-report artefacts.
223    #[serde(default = "default_true")]
224    pub management_reports: bool,
225    /// Emit `LabeledDriftEvent` records — post-generation sweep over
226    /// journal entries to label detected drift patterns.
227    #[serde(default = "default_true")]
228    pub drift_events: bool,
229}
230
231impl Default for AnalyticsMetadataConfig {
232    fn default() -> Self {
233        Self {
234            enabled: false,
235            prior_year: true,
236            industry_benchmark: true,
237            management_reports: true,
238            drift_events: true,
239        }
240    }
241}
242
243/// LLM enrichment configuration.
244///
245/// Controls AI-augmented metadata enrichment using LLM providers.
246/// When enabled, vendor names, transaction descriptions, and anomaly explanations
247/// are enriched using the configured provider (mock by default).
248#[derive(Debug, Clone, Serialize, Deserialize)]
249pub struct LlmSchemaConfig {
250    /// Whether LLM enrichment is enabled.
251    #[serde(default)]
252    pub enabled: bool,
253    /// Provider type: "mock", "openai", "anthropic", "custom".
254    #[serde(default = "default_llm_provider")]
255    pub provider: String,
256    /// Model name/ID for the provider.
257    #[serde(default = "default_llm_model_name")]
258    pub model: String,
259    /// Maximum number of vendor names to enrich per run.
260    #[serde(default = "default_llm_batch_size")]
261    pub max_vendor_enrichments: usize,
262
263    /// v4.1.1+: also enrich customer names at generate time.
264    /// Default `false` preserves v4.1.0 behaviour.
265    #[serde(default)]
266    pub enrich_customers: bool,
267
268    /// v4.1.1+: also enrich material descriptions at generate time.
269    /// Default `false`.
270    #[serde(default)]
271    pub enrich_materials: bool,
272
273    /// v4.1.1+: also enrich audit finding titles at generate time
274    /// (the finding narratives remain on their existing template path
275    /// because they're richer and locale-specific). Default `false`.
276    #[serde(default)]
277    pub enrich_findings: bool,
278
279    /// v4.1.1+: upper bound on customer enrichments per run. Matches
280    /// `max_vendor_enrichments` semantics.
281    #[serde(default = "default_llm_batch_size")]
282    pub max_customer_enrichments: usize,
283
284    /// v4.1.1+: upper bound on material enrichments per run.
285    #[serde(default = "default_llm_batch_size")]
286    pub max_material_enrichments: usize,
287
288    /// v4.1.1+: upper bound on finding enrichments per run.
289    #[serde(default = "default_llm_batch_size")]
290    pub max_finding_enrichments: usize,
291}
292
293fn default_llm_provider() -> String {
294    "mock".to_string()
295}
296
297fn default_llm_model_name() -> String {
298    "gpt-4o-mini".to_string()
299}
300
301fn default_llm_batch_size() -> usize {
302    50
303}
304
305impl Default for LlmSchemaConfig {
306    fn default() -> Self {
307        Self {
308            enabled: false,
309            provider: default_llm_provider(),
310            model: default_llm_model_name(),
311            max_vendor_enrichments: default_llm_batch_size(),
312            enrich_customers: false,
313            enrich_materials: false,
314            enrich_findings: false,
315            max_customer_enrichments: default_llm_batch_size(),
316            max_material_enrichments: default_llm_batch_size(),
317            max_finding_enrichments: default_llm_batch_size(),
318        }
319    }
320}
321
322/// Diffusion model configuration.
323///
324/// Controls statistical diffusion-based data enhancement that generates samples
325/// matching target distribution properties (means, standard deviations, correlations).
326#[derive(Debug, Clone, Serialize, Deserialize)]
327pub struct DiffusionSchemaConfig {
328    /// Whether diffusion enhancement is enabled.
329    #[serde(default)]
330    pub enabled: bool,
331    /// Number of diffusion steps (higher = better quality, slower).
332    #[serde(default = "default_diffusion_steps")]
333    pub n_steps: usize,
334    /// Noise schedule type: "linear", "cosine", "sigmoid".
335    #[serde(default = "default_diffusion_schedule")]
336    pub schedule: String,
337    /// Number of sample rows to generate for demonstration.
338    #[serde(default = "default_diffusion_sample_size")]
339    pub sample_size: usize,
340    /// Backend type: "statistical" (default), "neural", "hybrid".
341    #[serde(default = "default_diffusion_backend")]
342    pub backend: String,
343    /// Neural diffusion backend configuration (used when backend is "neural" or "hybrid").
344    #[serde(default)]
345    pub neural: NeuralDiffusionSchemaConfig,
346}
347
348fn default_diffusion_steps() -> usize {
349    100
350}
351
352fn default_diffusion_schedule() -> String {
353    "linear".to_string()
354}
355
356fn default_diffusion_sample_size() -> usize {
357    100
358}
359
360fn default_diffusion_backend() -> String {
361    "statistical".to_string()
362}
363
364impl Default for DiffusionSchemaConfig {
365    fn default() -> Self {
366        Self {
367            enabled: false,
368            n_steps: default_diffusion_steps(),
369            schedule: default_diffusion_schedule(),
370            sample_size: default_diffusion_sample_size(),
371            backend: default_diffusion_backend(),
372            neural: NeuralDiffusionSchemaConfig::default(),
373        }
374    }
375}
376
377/// Neural diffusion backend configuration.
378///
379/// Controls the `candle`-based neural score network that learns joint distributions
380/// from training data for the neural and hybrid diffusion backends.
381#[derive(Debug, Clone, Serialize, Deserialize)]
382pub struct NeuralDiffusionSchemaConfig {
383    /// Hidden layer dimensions for the score network MLP.
384    #[serde(default = "default_neural_hidden_dims")]
385    pub hidden_dims: Vec<usize>,
386    /// Dimensionality of the timestep embedding.
387    #[serde(default = "default_neural_timestep_embed_dim")]
388    pub timestep_embed_dim: usize,
389    /// Learning rate for training.
390    #[serde(default = "default_neural_learning_rate")]
391    pub learning_rate: f64,
392    /// Number of training epochs.
393    #[serde(default = "default_neural_training_epochs")]
394    pub training_epochs: usize,
395    /// Training batch size.
396    #[serde(default = "default_neural_batch_size")]
397    pub batch_size: usize,
398    /// Blend weight for hybrid mode (0.0 = all statistical, 1.0 = all neural).
399    #[serde(default = "default_neural_hybrid_weight")]
400    pub hybrid_weight: f64,
401    /// Hybrid blending strategy: "weighted_average", "column_select", "threshold".
402    #[serde(default = "default_neural_hybrid_strategy")]
403    pub hybrid_strategy: String,
404    /// Columns to apply neural generation to (empty = all numeric columns).
405    #[serde(default)]
406    pub neural_columns: Vec<String>,
407}
408
409fn default_neural_hidden_dims() -> Vec<usize> {
410    vec![256, 256, 128]
411}
412
413fn default_neural_timestep_embed_dim() -> usize {
414    64
415}
416
417fn default_neural_learning_rate() -> f64 {
418    0.001
419}
420
421fn default_neural_training_epochs() -> usize {
422    100
423}
424
425fn default_neural_batch_size() -> usize {
426    64
427}
428
429fn default_neural_hybrid_weight() -> f64 {
430    0.5
431}
432
433fn default_neural_hybrid_strategy() -> String {
434    "weighted_average".to_string()
435}
436
437impl Default for NeuralDiffusionSchemaConfig {
438    fn default() -> Self {
439        Self {
440            hidden_dims: default_neural_hidden_dims(),
441            timestep_embed_dim: default_neural_timestep_embed_dim(),
442            learning_rate: default_neural_learning_rate(),
443            training_epochs: default_neural_training_epochs(),
444            batch_size: default_neural_batch_size(),
445            hybrid_weight: default_neural_hybrid_weight(),
446            hybrid_strategy: default_neural_hybrid_strategy(),
447            neural_columns: Vec::new(),
448        }
449    }
450}
451
452/// Causal generation configuration.
453///
454/// Controls structural causal model (SCM) based data generation that respects
455/// causal relationships between variables, supports do-calculus interventions,
456/// and enables counterfactual scenarios.
457#[derive(Debug, Clone, Serialize, Deserialize)]
458pub struct CausalSchemaConfig {
459    /// Whether causal generation is enabled.
460    #[serde(default)]
461    pub enabled: bool,
462    /// Built-in template to use: "fraud_detection", "revenue_cycle", or "custom".
463    #[serde(default = "default_causal_template")]
464    pub template: String,
465    /// Number of causal samples to generate.
466    #[serde(default = "default_causal_sample_size")]
467    pub sample_size: usize,
468    /// Whether to run causal validation on the output.
469    #[serde(default = "default_true")]
470    pub validate: bool,
471}
472
473fn default_causal_template() -> String {
474    "fraud_detection".to_string()
475}
476
477fn default_causal_sample_size() -> usize {
478    500
479}
480
481impl Default for CausalSchemaConfig {
482    fn default() -> Self {
483        Self {
484            enabled: false,
485            template: default_causal_template(),
486            sample_size: default_causal_sample_size(),
487            validate: true,
488        }
489    }
490}
491
492/// Graph export configuration for accounting network and ML training exports.
493///
494/// This section enables exporting generated data as graphs for:
495/// - Network reconstruction algorithms
496/// - Graph neural network training
497/// - Neo4j graph database import
498#[derive(Debug, Clone, Serialize, Deserialize)]
499pub struct GraphExportConfig {
500    /// Enable graph export.
501    #[serde(default)]
502    pub enabled: bool,
503
504    /// Graph types to generate.
505    #[serde(default = "default_graph_types")]
506    pub graph_types: Vec<GraphTypeConfig>,
507
508    /// Export formats to generate.
509    #[serde(default = "default_graph_formats")]
510    pub formats: Vec<GraphExportFormat>,
511
512    /// Train split ratio for ML datasets.
513    #[serde(default = "default_train_ratio")]
514    pub train_ratio: f64,
515
516    /// Validation split ratio for ML datasets.
517    #[serde(default = "default_val_ratio")]
518    pub validation_ratio: f64,
519
520    /// Random seed for train/val/test splits.
521    #[serde(default)]
522    pub split_seed: Option<u64>,
523
524    /// Output subdirectory for graph exports (relative to output directory).
525    #[serde(default = "default_graph_subdir")]
526    pub output_subdirectory: String,
527
528    /// Multi-layer hypergraph export settings for RustGraph integration.
529    #[serde(default)]
530    pub hypergraph: HypergraphExportSettings,
531
532    /// DGL-specific export settings.
533    #[serde(default)]
534    pub dgl: DglExportConfig,
535}
536
537fn default_graph_types() -> Vec<GraphTypeConfig> {
538    vec![GraphTypeConfig::default()]
539}
540
541fn default_graph_formats() -> Vec<GraphExportFormat> {
542    vec![GraphExportFormat::PytorchGeometric]
543}
544
545fn default_train_ratio() -> f64 {
546    0.7
547}
548
549fn default_val_ratio() -> f64 {
550    0.15
551}
552
553fn default_graph_subdir() -> String {
554    "graphs".to_string()
555}
556
557impl Default for GraphExportConfig {
558    fn default() -> Self {
559        Self {
560            enabled: false,
561            graph_types: default_graph_types(),
562            formats: default_graph_formats(),
563            train_ratio: 0.7,
564            validation_ratio: 0.15,
565            split_seed: None,
566            output_subdirectory: "graphs".to_string(),
567            hypergraph: HypergraphExportSettings::default(),
568            dgl: DglExportConfig::default(),
569        }
570    }
571}
572
573/// DGL-specific export settings.
574#[derive(Debug, Clone, Default, Serialize, Deserialize)]
575pub struct DglExportConfig {
576    /// Export as a heterogeneous graph (distinct node/edge types).
577    ///
578    /// When `true` the DGL exporter produces a `HeteroData` object with typed
579    /// node and edge stores rather than a single homogeneous graph.
580    /// Set to `true` in `graph_export.dgl.heterogeneous: true` in YAML.
581    #[serde(default)]
582    pub heterogeneous: bool,
583}
584
585// Default derived: heterogeneous = false (bool default)
586
587/// Settings for the multi-layer hypergraph export (RustGraph integration).
588///
589/// Produces a 3-layer hypergraph:
590/// - Layer 1: Governance & Controls (COSO, SOX, internal controls, organizational)
591/// - Layer 2: Process Events (P2P/O2C document flows, OCPM events)
592/// - Layer 3: Accounting Network (GL accounts, journal entries as hyperedges)
593#[derive(Debug, Clone, Serialize, Deserialize)]
594pub struct HypergraphExportSettings {
595    /// Enable hypergraph export.
596    #[serde(default)]
597    pub enabled: bool,
598
599    /// Maximum total nodes across all layers (default 50000).
600    #[serde(default = "default_hypergraph_max_nodes")]
601    pub max_nodes: usize,
602
603    /// Aggregation strategy when node budget is exceeded.
604    #[serde(default = "default_aggregation_strategy")]
605    pub aggregation_strategy: String,
606
607    /// Layer 1 (Governance & Controls) settings.
608    #[serde(default)]
609    pub governance_layer: GovernanceLayerSettings,
610
611    /// Layer 2 (Process Events) settings.
612    #[serde(default)]
613    pub process_layer: ProcessLayerSettings,
614
615    /// Layer 3 (Accounting Network) settings.
616    #[serde(default)]
617    pub accounting_layer: AccountingLayerSettings,
618
619    /// Cross-layer edge generation settings.
620    #[serde(default)]
621    pub cross_layer: CrossLayerSettings,
622
623    /// Output subdirectory for hypergraph files (relative to graph output directory).
624    #[serde(default = "default_hypergraph_subdir")]
625    pub output_subdirectory: String,
626
627    /// Output format: "native" (default) for internal field names, "unified" for RustGraph format.
628    #[serde(default = "default_hypergraph_format")]
629    pub output_format: String,
630
631    /// Optional URL for streaming unified JSONL to a RustGraph ingest endpoint.
632    #[serde(default)]
633    pub stream_target: Option<String>,
634
635    /// Batch size for streaming (number of JSONL lines per HTTP POST). Default: 1000.
636    #[serde(default = "default_stream_batch_size")]
637    pub stream_batch_size: usize,
638}
639
640fn default_hypergraph_max_nodes() -> usize {
641    50_000
642}
643
644fn default_aggregation_strategy() -> String {
645    "pool_by_counterparty".to_string()
646}
647
648fn default_hypergraph_subdir() -> String {
649    "hypergraph".to_string()
650}
651
652fn default_hypergraph_format() -> String {
653    "native".to_string()
654}
655
656fn default_stream_batch_size() -> usize {
657    1000
658}
659
660impl Default for HypergraphExportSettings {
661    fn default() -> Self {
662        Self {
663            enabled: false,
664            max_nodes: 50_000,
665            aggregation_strategy: "pool_by_counterparty".to_string(),
666            governance_layer: GovernanceLayerSettings::default(),
667            process_layer: ProcessLayerSettings::default(),
668            accounting_layer: AccountingLayerSettings::default(),
669            cross_layer: CrossLayerSettings::default(),
670            output_subdirectory: "hypergraph".to_string(),
671            output_format: "native".to_string(),
672            stream_target: None,
673            stream_batch_size: 1000,
674        }
675    }
676}
677
678/// Layer 1: Governance & Controls layer settings.
679#[derive(Debug, Clone, Serialize, Deserialize)]
680pub struct GovernanceLayerSettings {
681    /// Include COSO framework nodes (5 components + 17 principles).
682    #[serde(default = "default_true")]
683    pub include_coso: bool,
684    /// Include internal control nodes.
685    #[serde(default = "default_true")]
686    pub include_controls: bool,
687    /// Include SOX assertion nodes.
688    #[serde(default = "default_true")]
689    pub include_sox: bool,
690    /// Include vendor master data nodes.
691    #[serde(default = "default_true")]
692    pub include_vendors: bool,
693    /// Include customer master data nodes.
694    #[serde(default = "default_true")]
695    pub include_customers: bool,
696    /// Include employee/organizational nodes.
697    #[serde(default = "default_true")]
698    pub include_employees: bool,
699}
700
701impl Default for GovernanceLayerSettings {
702    fn default() -> Self {
703        Self {
704            include_coso: true,
705            include_controls: true,
706            include_sox: true,
707            include_vendors: true,
708            include_customers: true,
709            include_employees: true,
710        }
711    }
712}
713
714/// Layer 2: Process Events layer settings.
715#[derive(Debug, Clone, Serialize, Deserialize)]
716pub struct ProcessLayerSettings {
717    /// Include P2P (Procure-to-Pay) document flow nodes.
718    #[serde(default = "default_true")]
719    pub include_p2p: bool,
720    /// Include O2C (Order-to-Cash) document flow nodes.
721    #[serde(default = "default_true")]
722    pub include_o2c: bool,
723    /// Include S2C (Source-to-Contract) document flow nodes.
724    #[serde(default = "default_true")]
725    pub include_s2c: bool,
726    /// Include H2R (Hire-to-Retire) document flow nodes.
727    #[serde(default = "default_true")]
728    pub include_h2r: bool,
729    /// Include MFG (Manufacturing) document flow nodes.
730    #[serde(default = "default_true")]
731    pub include_mfg: bool,
732    /// Include BANK (Banking) document flow nodes.
733    #[serde(default = "default_true")]
734    pub include_bank: bool,
735    /// Include AUDIT document flow nodes.
736    #[serde(default = "default_true")]
737    pub include_audit: bool,
738    /// Include R2R (Record-to-Report) document flow nodes (bank recon + period close).
739    #[serde(default = "default_true")]
740    pub include_r2r: bool,
741    /// Export OCPM events as hyperedges.
742    #[serde(default = "default_true")]
743    pub events_as_hyperedges: bool,
744    /// Threshold: if a counterparty has more documents than this, aggregate into pool nodes.
745    #[serde(default = "default_docs_per_counterparty_threshold")]
746    pub docs_per_counterparty_threshold: usize,
747}
748
749fn default_docs_per_counterparty_threshold() -> usize {
750    20
751}
752
753impl Default for ProcessLayerSettings {
754    fn default() -> Self {
755        Self {
756            include_p2p: true,
757            include_o2c: true,
758            include_s2c: true,
759            include_h2r: true,
760            include_mfg: true,
761            include_bank: true,
762            include_audit: true,
763            include_r2r: true,
764            events_as_hyperedges: true,
765            docs_per_counterparty_threshold: 20,
766        }
767    }
768}
769
770/// Layer 3: Accounting Network layer settings.
771#[derive(Debug, Clone, Serialize, Deserialize)]
772pub struct AccountingLayerSettings {
773    /// Include GL account nodes.
774    #[serde(default = "default_true")]
775    pub include_accounts: bool,
776    /// Export journal entries as hyperedges (debit+credit accounts as participants).
777    #[serde(default = "default_true")]
778    pub je_as_hyperedges: bool,
779}
780
781impl Default for AccountingLayerSettings {
782    fn default() -> Self {
783        Self {
784            include_accounts: true,
785            je_as_hyperedges: true,
786        }
787    }
788}
789
790/// Cross-layer edge generation settings.
791#[derive(Debug, Clone, Serialize, Deserialize)]
792pub struct CrossLayerSettings {
793    /// Generate cross-layer edges (Control→Account, Vendor→PO, etc.).
794    #[serde(default = "default_true")]
795    pub enabled: bool,
796}
797
798impl Default for CrossLayerSettings {
799    fn default() -> Self {
800        Self { enabled: true }
801    }
802}
803
804/// Configuration for a specific graph type to export.
805#[derive(Debug, Clone, Serialize, Deserialize)]
806pub struct GraphTypeConfig {
807    /// Name identifier for this graph configuration.
808    #[serde(default = "default_graph_name")]
809    pub name: String,
810
811    /// Whether to aggregate parallel edges between the same nodes.
812    #[serde(default)]
813    pub aggregate_edges: bool,
814
815    /// Minimum edge weight to include (filters out small transactions).
816    #[serde(default)]
817    pub min_edge_weight: f64,
818
819    /// Whether to include document nodes (creates hub-and-spoke structure).
820    #[serde(default)]
821    pub include_document_nodes: bool,
822}
823
824fn default_graph_name() -> String {
825    "accounting_network".to_string()
826}
827
828impl Default for GraphTypeConfig {
829    fn default() -> Self {
830        Self {
831            name: "accounting_network".to_string(),
832            aggregate_edges: false,
833            min_edge_weight: 0.0,
834            include_document_nodes: false,
835        }
836    }
837}
838
839/// Export format for graph data.
840#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
841#[serde(rename_all = "snake_case")]
842pub enum GraphExportFormat {
843    /// PyTorch Geometric format (.npy files + metadata.json).
844    PytorchGeometric,
845    /// Neo4j format (CSV files + Cypher import scripts).
846    Neo4j,
847    /// Deep Graph Library format.
848    Dgl,
849    /// RustGraph/RustAssureTwin JSON format.
850    RustGraph,
851    /// RustGraph multi-layer hypergraph format (nodes.jsonl + edges.jsonl + hyperedges.jsonl).
852    RustGraphHypergraph,
853}
854
855/// Scenario configuration for metadata, tagging, and ML training setup.
856///
857/// This section enables tracking the purpose and characteristics of a generation run.
858#[derive(Debug, Clone, Default, Serialize, Deserialize)]
859pub struct ScenarioConfig {
860    /// Tags for categorizing and filtering datasets.
861    /// Examples: "fraud_detection", "retail", "month_end_stress", "ml_training"
862    #[serde(default)]
863    pub tags: Vec<String>,
864
865    /// Data quality profile preset.
866    /// - "clean": Minimal data quality issues (0.1% missing, 0.05% typos)
867    /// - "noisy": Moderate issues (5% missing, 2% typos, 1% duplicates)
868    /// - "legacy": Heavy issues simulating legacy system data (10% missing, 5% typos)
869    #[serde(default)]
870    pub profile: Option<String>,
871
872    /// Human-readable description of the scenario purpose.
873    #[serde(default)]
874    pub description: Option<String>,
875
876    /// Whether this run is for ML training (enables balanced labeling).
877    #[serde(default)]
878    pub ml_training: bool,
879
880    /// Target anomaly class balance for ML training.
881    /// If set, anomalies will be injected to achieve this ratio.
882    #[serde(default)]
883    pub target_anomaly_ratio: Option<f64>,
884
885    /// Custom metadata key-value pairs.
886    #[serde(default)]
887    pub metadata: std::collections::HashMap<String, String>,
888}
889
890/// Temporal drift configuration for simulating distribution changes over time.
891///
892/// This enables generation of data that shows realistic temporal evolution,
893/// useful for training drift detection models and testing temporal robustness.
894#[derive(Debug, Clone, Serialize, Deserialize)]
895pub struct TemporalDriftConfig {
896    /// Enable temporal drift simulation.
897    #[serde(default)]
898    pub enabled: bool,
899
900    /// Amount mean drift per period (e.g., 0.02 = 2% mean shift per month).
901    /// Simulates gradual inflation or business growth.
902    #[serde(default = "default_amount_drift")]
903    pub amount_mean_drift: f64,
904
905    /// Amount variance drift per period (e.g., 0.01 = 1% variance increase per month).
906    /// Simulates increasing volatility over time.
907    #[serde(default)]
908    pub amount_variance_drift: f64,
909
910    /// Anomaly rate drift per period (e.g., 0.001 = 0.1% increase per month).
911    /// Simulates increasing fraud attempts or degrading controls.
912    #[serde(default)]
913    pub anomaly_rate_drift: f64,
914
915    /// Concept drift rate - how quickly feature distributions change (0.0-1.0).
916    /// Higher values cause more rapid distribution shifts.
917    #[serde(default = "default_concept_drift")]
918    pub concept_drift_rate: f64,
919
920    /// Sudden drift events - probability of a sudden distribution shift in any period.
921    #[serde(default)]
922    pub sudden_drift_probability: f64,
923
924    /// Magnitude of sudden drift events when they occur (multiplier).
925    #[serde(default = "default_sudden_drift_magnitude")]
926    pub sudden_drift_magnitude: f64,
927
928    /// Seasonal drift - enable cyclic patterns that repeat annually.
929    #[serde(default)]
930    pub seasonal_drift: bool,
931
932    /// Drift start period (0 = from beginning). Use to simulate stable baseline before drift.
933    #[serde(default)]
934    pub drift_start_period: u32,
935
936    /// Drift type: "gradual", "sudden", "recurring", "mixed"
937    #[serde(default = "default_drift_type")]
938    pub drift_type: DriftType,
939}
940
941fn default_amount_drift() -> f64 {
942    0.02
943}
944
945fn default_concept_drift() -> f64 {
946    0.01
947}
948
949fn default_sudden_drift_magnitude() -> f64 {
950    2.0
951}
952
953fn default_drift_type() -> DriftType {
954    DriftType::Gradual
955}
956
957impl Default for TemporalDriftConfig {
958    fn default() -> Self {
959        Self {
960            enabled: false,
961            amount_mean_drift: 0.02,
962            amount_variance_drift: 0.0,
963            anomaly_rate_drift: 0.0,
964            concept_drift_rate: 0.01,
965            sudden_drift_probability: 0.0,
966            sudden_drift_magnitude: 2.0,
967            seasonal_drift: false,
968            drift_start_period: 0,
969            drift_type: DriftType::Gradual,
970        }
971    }
972}
973
974impl TemporalDriftConfig {
975    /// Convert to core DriftConfig for use in generators.
976    pub fn to_core_config(&self) -> datasynth_core::distributions::DriftConfig {
977        datasynth_core::distributions::DriftConfig {
978            enabled: self.enabled,
979            amount_mean_drift: self.amount_mean_drift,
980            amount_variance_drift: self.amount_variance_drift,
981            anomaly_rate_drift: self.anomaly_rate_drift,
982            concept_drift_rate: self.concept_drift_rate,
983            sudden_drift_probability: self.sudden_drift_probability,
984            sudden_drift_magnitude: self.sudden_drift_magnitude,
985            seasonal_drift: self.seasonal_drift,
986            drift_start_period: self.drift_start_period,
987            drift_type: match self.drift_type {
988                DriftType::Gradual => datasynth_core::distributions::DriftType::Gradual,
989                DriftType::Sudden => datasynth_core::distributions::DriftType::Sudden,
990                DriftType::Recurring => datasynth_core::distributions::DriftType::Recurring,
991                DriftType::Mixed => datasynth_core::distributions::DriftType::Mixed,
992            },
993            regime_changes: Vec::new(),
994            economic_cycle: Default::default(),
995            parameter_drifts: Vec::new(),
996        }
997    }
998}
999
1000/// Types of temporal drift patterns.
1001#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
1002#[serde(rename_all = "snake_case")]
1003pub enum DriftType {
1004    /// Gradual, continuous drift over time (like inflation).
1005    #[default]
1006    Gradual,
1007    /// Sudden, point-in-time shifts (like policy changes).
1008    Sudden,
1009    /// Recurring patterns that cycle (like seasonal variations).
1010    Recurring,
1011    /// Combination of gradual background drift with occasional sudden shifts.
1012    Mixed,
1013}
1014
1015// ============================================================================
1016// Streaming Output API Configuration (Phase 2)
1017// ============================================================================
1018
1019/// Configuration for streaming output API.
1020#[derive(Debug, Clone, Serialize, Deserialize)]
1021pub struct StreamingSchemaConfig {
1022    /// Enable streaming output.
1023    #[serde(default)]
1024    pub enabled: bool,
1025    /// Target events per second (0 = unlimited, default 0).
1026    #[serde(default)]
1027    pub events_per_second: f64,
1028    /// Token bucket burst size (default 100).
1029    #[serde(default = "default_burst_size")]
1030    pub burst_size: u32,
1031    /// Buffer size for streaming (number of items).
1032    #[serde(default = "default_buffer_size")]
1033    pub buffer_size: usize,
1034    /// Enable progress reporting.
1035    #[serde(default = "default_true")]
1036    pub enable_progress: bool,
1037    /// Progress reporting interval (number of items).
1038    #[serde(default = "default_progress_interval")]
1039    pub progress_interval: u64,
1040    /// Backpressure strategy.
1041    #[serde(default)]
1042    pub backpressure: BackpressureSchemaStrategy,
1043}
1044
1045fn default_buffer_size() -> usize {
1046    1000
1047}
1048
1049fn default_progress_interval() -> u64 {
1050    100
1051}
1052
1053impl Default for StreamingSchemaConfig {
1054    fn default() -> Self {
1055        Self {
1056            enabled: false,
1057            events_per_second: 0.0,
1058            burst_size: 100,
1059            buffer_size: 1000,
1060            enable_progress: true,
1061            progress_interval: 100,
1062            backpressure: BackpressureSchemaStrategy::Block,
1063        }
1064    }
1065}
1066
1067/// Backpressure strategy for streaming output.
1068#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
1069#[serde(rename_all = "snake_case")]
1070pub enum BackpressureSchemaStrategy {
1071    /// Block until space is available in the buffer.
1072    #[default]
1073    Block,
1074    /// Drop oldest items when buffer is full.
1075    DropOldest,
1076    /// Drop newest items when buffer is full.
1077    DropNewest,
1078    /// Buffer overflow items up to a limit, then block.
1079    Buffer,
1080}
1081
1082// ============================================================================
1083// Rate Limiting Configuration (Phase 5)
1084// ============================================================================
1085
1086/// Configuration for rate limiting.
1087#[derive(Debug, Clone, Serialize, Deserialize)]
1088pub struct RateLimitSchemaConfig {
1089    /// Enable rate limiting.
1090    #[serde(default)]
1091    pub enabled: bool,
1092    /// Entities per second limit.
1093    #[serde(default = "default_entities_per_second")]
1094    pub entities_per_second: f64,
1095    /// Burst size (number of tokens in bucket).
1096    #[serde(default = "default_burst_size")]
1097    pub burst_size: u32,
1098    /// Backpressure strategy for rate limiting.
1099    #[serde(default)]
1100    pub backpressure: RateLimitBackpressureSchema,
1101}
1102
1103fn default_entities_per_second() -> f64 {
1104    1000.0
1105}
1106
1107fn default_burst_size() -> u32 {
1108    100
1109}
1110
1111impl Default for RateLimitSchemaConfig {
1112    fn default() -> Self {
1113        Self {
1114            enabled: false,
1115            entities_per_second: 1000.0,
1116            burst_size: 100,
1117            backpressure: RateLimitBackpressureSchema::Block,
1118        }
1119    }
1120}
1121
1122/// Backpressure strategy for rate limiting.
1123#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
1124#[serde(rename_all = "snake_case")]
1125pub enum RateLimitBackpressureSchema {
1126    /// Block until rate allows.
1127    #[default]
1128    Block,
1129    /// Drop items that exceed rate.
1130    Drop,
1131    /// Buffer items and process when rate allows.
1132    Buffer,
1133}
1134
1135// ============================================================================
1136// Temporal Attribute Generation Configuration (Phase 3)
1137// ============================================================================
1138
1139/// Configuration for temporal attribute generation.
1140#[derive(Debug, Clone, Serialize, Deserialize)]
1141pub struct TemporalAttributeSchemaConfig {
1142    /// Enable temporal attribute generation.
1143    #[serde(default)]
1144    pub enabled: bool,
1145    /// Valid time configuration.
1146    #[serde(default)]
1147    pub valid_time: ValidTimeSchemaConfig,
1148    /// Transaction time configuration.
1149    #[serde(default)]
1150    pub transaction_time: TransactionTimeSchemaConfig,
1151    /// Generate version chains for entities.
1152    #[serde(default)]
1153    pub generate_version_chains: bool,
1154    /// Average number of versions per entity.
1155    #[serde(default = "default_avg_versions")]
1156    pub avg_versions_per_entity: f64,
1157}
1158
1159fn default_avg_versions() -> f64 {
1160    1.5
1161}
1162
1163impl Default for TemporalAttributeSchemaConfig {
1164    fn default() -> Self {
1165        Self {
1166            enabled: false,
1167            valid_time: ValidTimeSchemaConfig::default(),
1168            transaction_time: TransactionTimeSchemaConfig::default(),
1169            generate_version_chains: false,
1170            avg_versions_per_entity: 1.5,
1171        }
1172    }
1173}
1174
1175/// Configuration for valid time (business time) generation.
1176#[derive(Debug, Clone, Serialize, Deserialize)]
1177pub struct ValidTimeSchemaConfig {
1178    /// Probability that valid_to is set (entity has ended validity).
1179    #[serde(default = "default_closed_probability")]
1180    pub closed_probability: f64,
1181    /// Average validity duration in days.
1182    #[serde(default = "default_avg_validity_days")]
1183    pub avg_validity_days: u32,
1184    /// Standard deviation of validity duration in days.
1185    #[serde(default = "default_validity_stddev")]
1186    pub validity_stddev_days: u32,
1187}
1188
1189fn default_closed_probability() -> f64 {
1190    0.1
1191}
1192
1193fn default_avg_validity_days() -> u32 {
1194    365
1195}
1196
1197fn default_validity_stddev() -> u32 {
1198    90
1199}
1200
1201impl Default for ValidTimeSchemaConfig {
1202    fn default() -> Self {
1203        Self {
1204            closed_probability: 0.1,
1205            avg_validity_days: 365,
1206            validity_stddev_days: 90,
1207        }
1208    }
1209}
1210
1211/// Configuration for transaction time (system time) generation.
1212#[derive(Debug, Clone, Serialize, Deserialize)]
1213pub struct TransactionTimeSchemaConfig {
1214    /// Average recording delay in seconds (0 = immediate).
1215    #[serde(default)]
1216    pub avg_recording_delay_seconds: u32,
1217    /// Allow backdating (recording time before valid time).
1218    #[serde(default)]
1219    pub allow_backdating: bool,
1220    /// Probability of backdating if allowed.
1221    #[serde(default = "default_backdating_probability")]
1222    pub backdating_probability: f64,
1223    /// Maximum backdate days.
1224    #[serde(default = "default_max_backdate_days")]
1225    pub max_backdate_days: u32,
1226}
1227
1228fn default_backdating_probability() -> f64 {
1229    0.01
1230}
1231
1232fn default_max_backdate_days() -> u32 {
1233    30
1234}
1235
1236impl Default for TransactionTimeSchemaConfig {
1237    fn default() -> Self {
1238        Self {
1239            avg_recording_delay_seconds: 0,
1240            allow_backdating: false,
1241            backdating_probability: 0.01,
1242            max_backdate_days: 30,
1243        }
1244    }
1245}
1246
1247// ============================================================================
1248// Relationship Generation Configuration (Phase 4)
1249// ============================================================================
1250
1251/// Configuration for relationship generation.
1252#[derive(Debug, Clone, Serialize, Deserialize)]
1253pub struct RelationshipSchemaConfig {
1254    /// Relationship type definitions.
1255    #[serde(default)]
1256    pub relationship_types: Vec<RelationshipTypeSchemaConfig>,
1257    /// Allow orphan entities (entities with no relationships).
1258    #[serde(default = "default_true")]
1259    pub allow_orphans: bool,
1260    /// Probability of creating an orphan entity.
1261    #[serde(default = "default_orphan_probability")]
1262    pub orphan_probability: f64,
1263    /// Allow circular relationships.
1264    #[serde(default)]
1265    pub allow_circular: bool,
1266    /// Maximum depth for circular relationship detection.
1267    #[serde(default = "default_max_circular_depth")]
1268    pub max_circular_depth: u32,
1269}
1270
1271fn default_orphan_probability() -> f64 {
1272    0.01
1273}
1274
1275fn default_max_circular_depth() -> u32 {
1276    3
1277}
1278
1279impl Default for RelationshipSchemaConfig {
1280    fn default() -> Self {
1281        Self {
1282            relationship_types: Vec::new(),
1283            allow_orphans: true,
1284            orphan_probability: 0.01,
1285            allow_circular: false,
1286            max_circular_depth: 3,
1287        }
1288    }
1289}
1290
1291/// Configuration for a specific relationship type.
1292#[derive(Debug, Clone, Serialize, Deserialize)]
1293pub struct RelationshipTypeSchemaConfig {
1294    /// Name of the relationship type (e.g., "debits", "credits", "created").
1295    pub name: String,
1296    /// Source entity type (e.g., "journal_entry").
1297    pub source_type: String,
1298    /// Target entity type (e.g., "account").
1299    pub target_type: String,
1300    /// Cardinality rule for this relationship.
1301    #[serde(default)]
1302    pub cardinality: CardinalitySchemaRule,
1303    /// Weight for this relationship in random selection.
1304    #[serde(default = "default_relationship_weight")]
1305    pub weight: f64,
1306    /// Whether this relationship is required.
1307    #[serde(default)]
1308    pub required: bool,
1309    /// Whether this relationship is directed.
1310    #[serde(default = "default_true")]
1311    pub directed: bool,
1312}
1313
1314fn default_relationship_weight() -> f64 {
1315    1.0
1316}
1317
1318impl Default for RelationshipTypeSchemaConfig {
1319    fn default() -> Self {
1320        Self {
1321            name: String::new(),
1322            source_type: String::new(),
1323            target_type: String::new(),
1324            cardinality: CardinalitySchemaRule::default(),
1325            weight: 1.0,
1326            required: false,
1327            directed: true,
1328        }
1329    }
1330}
1331
1332/// Cardinality rule for relationships in schema config.
1333#[derive(Debug, Clone, Serialize, Deserialize)]
1334#[serde(rename_all = "snake_case")]
1335pub enum CardinalitySchemaRule {
1336    /// One source to one target.
1337    OneToOne,
1338    /// One source to many targets.
1339    OneToMany {
1340        /// Minimum number of targets.
1341        min: u32,
1342        /// Maximum number of targets.
1343        max: u32,
1344    },
1345    /// Many sources to one target.
1346    ManyToOne {
1347        /// Minimum number of sources.
1348        min: u32,
1349        /// Maximum number of sources.
1350        max: u32,
1351    },
1352    /// Many sources to many targets.
1353    ManyToMany {
1354        /// Minimum targets per source.
1355        min_per_source: u32,
1356        /// Maximum targets per source.
1357        max_per_source: u32,
1358    },
1359}
1360
1361impl Default for CardinalitySchemaRule {
1362    fn default() -> Self {
1363        Self::OneToMany { min: 1, max: 5 }
1364    }
1365}
1366
1367/// Global configuration settings.
1368#[derive(Debug, Clone, Serialize, Deserialize)]
1369pub struct GlobalConfig {
1370    /// Random seed for reproducibility
1371    pub seed: Option<u64>,
1372    /// Industry sector
1373    pub industry: IndustrySector,
1374    /// Simulation start date (YYYY-MM-DD)
1375    pub start_date: String,
1376    /// Simulation period in months
1377    pub period_months: u32,
1378    /// Base currency for group reporting
1379    #[serde(default = "default_currency")]
1380    pub group_currency: String,
1381    /// Presentation currency for consolidated financial statements (ISO 4217).
1382    /// If not set, defaults to `group_currency`.
1383    #[serde(default)]
1384    pub presentation_currency: Option<String>,
1385    /// Enable parallel generation
1386    #[serde(default = "default_true")]
1387    pub parallel: bool,
1388    /// Number of worker threads (0 = auto-detect)
1389    #[serde(default)]
1390    pub worker_threads: usize,
1391    /// Memory limit in MB (0 = unlimited)
1392    #[serde(default)]
1393    pub memory_limit_mb: usize,
1394    /// Fiscal year length in months (defaults to 12 if not set).
1395    /// Used by session-based generation to split the total period into fiscal years.
1396    #[serde(default)]
1397    pub fiscal_year_months: Option<u32>,
1398}
1399
1400fn default_currency() -> String {
1401    "USD".to_string()
1402}
1403fn default_true() -> bool {
1404    true
1405}
1406
1407/// Configuration for generation session behavior.
1408///
1409/// When enabled, the generation pipeline splits the total period into fiscal years
1410/// and generates data period-by-period, carrying forward balance state.
1411#[derive(Debug, Clone, Serialize, Deserialize)]
1412pub struct SessionSchemaConfig {
1413    /// Whether session-based (period-by-period) generation is enabled.
1414    #[serde(default)]
1415    pub enabled: bool,
1416    /// Optional path for saving/loading session checkpoint files.
1417    #[serde(default)]
1418    pub checkpoint_path: Option<String>,
1419    /// Whether to write output files per fiscal period (e.g., `period_01/`).
1420    #[serde(default = "default_true")]
1421    pub per_period_output: bool,
1422    /// Whether to also produce a single consolidated output across all periods.
1423    #[serde(default = "default_true")]
1424    pub consolidated_output: bool,
1425}
1426
1427impl Default for SessionSchemaConfig {
1428    fn default() -> Self {
1429        Self {
1430            enabled: false,
1431            checkpoint_path: None,
1432            per_period_output: true,
1433            consolidated_output: true,
1434        }
1435    }
1436}
1437
1438/// Company code configuration.
1439#[derive(Debug, Clone, Serialize, Deserialize)]
1440pub struct CompanyConfig {
1441    /// Company code identifier
1442    pub code: String,
1443    /// Company name
1444    pub name: String,
1445    /// Local currency (ISO 4217)
1446    pub currency: String,
1447    /// Functional currency for IAS 21 translation (ISO 4217).
1448    /// If not set, defaults to the `currency` field (i.e. local == functional).
1449    #[serde(default)]
1450    pub functional_currency: Option<String>,
1451    /// Country code (ISO 3166-1 alpha-2)
1452    pub country: String,
1453    /// Fiscal year variant
1454    #[serde(default = "default_fiscal_variant")]
1455    pub fiscal_year_variant: String,
1456    /// Transaction volume per year
1457    pub annual_transaction_volume: TransactionVolume,
1458    /// Company-specific transaction weight
1459    #[serde(default = "default_weight")]
1460    pub volume_weight: f64,
1461}
1462
1463fn default_fiscal_variant() -> String {
1464    "K4".to_string()
1465}
1466fn default_weight() -> f64 {
1467    1.0
1468}
1469
1470/// Transaction volume presets.
1471#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
1472#[serde(rename_all = "snake_case")]
1473pub enum TransactionVolume {
1474    /// 10,000 transactions per year
1475    TenK,
1476    /// 100,000 transactions per year
1477    HundredK,
1478    /// 1,000,000 transactions per year
1479    OneM,
1480    /// 10,000,000 transactions per year
1481    TenM,
1482    /// 100,000,000 transactions per year
1483    HundredM,
1484    /// Custom count
1485    Custom(u64),
1486}
1487
1488impl TransactionVolume {
1489    /// Get the transaction count.
1490    pub fn count(&self) -> u64 {
1491        match self {
1492            Self::TenK => 10_000,
1493            Self::HundredK => 100_000,
1494            Self::OneM => 1_000_000,
1495            Self::TenM => 10_000_000,
1496            Self::HundredM => 100_000_000,
1497            Self::Custom(n) => *n,
1498        }
1499    }
1500}
1501
1502/// Chart of Accounts configuration.
1503#[derive(Debug, Clone, Serialize, Deserialize)]
1504pub struct ChartOfAccountsConfig {
1505    /// CoA complexity level
1506    pub complexity: CoAComplexity,
1507    /// Use industry-specific accounts
1508    #[serde(default = "default_true")]
1509    pub industry_specific: bool,
1510    /// Custom account definitions file
1511    pub custom_accounts: Option<PathBuf>,
1512    /// Minimum hierarchy depth
1513    #[serde(default = "default_min_depth")]
1514    pub min_hierarchy_depth: u8,
1515    /// Maximum hierarchy depth
1516    #[serde(default = "default_max_depth")]
1517    pub max_hierarchy_depth: u8,
1518}
1519
1520fn default_min_depth() -> u8 {
1521    2
1522}
1523fn default_max_depth() -> u8 {
1524    5
1525}
1526
1527impl Default for ChartOfAccountsConfig {
1528    fn default() -> Self {
1529        Self {
1530            complexity: CoAComplexity::Small,
1531            industry_specific: true,
1532            custom_accounts: None,
1533            min_hierarchy_depth: default_min_depth(),
1534            max_hierarchy_depth: default_max_depth(),
1535        }
1536    }
1537}
1538
1539/// Transaction generation configuration.
1540#[derive(Debug, Clone, Serialize, Deserialize, Default)]
1541pub struct TransactionConfig {
1542    /// Line item distribution
1543    #[serde(default)]
1544    pub line_item_distribution: LineItemDistributionConfig,
1545    /// Debit/credit balance distribution
1546    #[serde(default)]
1547    pub debit_credit_distribution: DebitCreditDistributionConfig,
1548    /// Even/odd line count distribution
1549    #[serde(default)]
1550    pub even_odd_distribution: EvenOddDistributionConfig,
1551    /// Transaction source distribution
1552    #[serde(default)]
1553    pub source_distribution: SourceDistribution,
1554    /// Seasonality configuration
1555    #[serde(default)]
1556    pub seasonality: SeasonalityConfig,
1557    /// Amount distribution
1558    #[serde(default)]
1559    pub amounts: AmountDistributionConfig,
1560    /// Benford's Law compliance configuration
1561    #[serde(default)]
1562    pub benford: BenfordConfig,
1563}
1564
1565/// Benford's Law compliance configuration.
1566#[derive(Debug, Clone, Serialize, Deserialize)]
1567pub struct BenfordConfig {
1568    /// Enable Benford's Law compliance for amount generation
1569    #[serde(default = "default_true")]
1570    pub enabled: bool,
1571    /// Tolerance for deviation from ideal Benford distribution (0.0-1.0)
1572    #[serde(default = "default_benford_tolerance")]
1573    pub tolerance: f64,
1574    /// Transaction sources exempt from Benford's Law (fixed amounts)
1575    #[serde(default)]
1576    pub exempt_sources: Vec<BenfordExemption>,
1577}
1578
1579fn default_benford_tolerance() -> f64 {
1580    0.05
1581}
1582
1583impl Default for BenfordConfig {
1584    fn default() -> Self {
1585        Self {
1586            enabled: true,
1587            tolerance: default_benford_tolerance(),
1588            exempt_sources: vec![BenfordExemption::Recurring, BenfordExemption::Payroll],
1589        }
1590    }
1591}
1592
1593/// Types of transactions exempt from Benford's Law.
1594#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
1595#[serde(rename_all = "snake_case")]
1596pub enum BenfordExemption {
1597    /// Recurring fixed amounts (rent, subscriptions)
1598    Recurring,
1599    /// Payroll (standardized salaries)
1600    Payroll,
1601    /// Fixed fees and charges
1602    FixedFees,
1603    /// Round number purchases (often legitimate)
1604    RoundAmounts,
1605}
1606
1607/// Distribution of transaction sources.
1608#[derive(Debug, Clone, Serialize, Deserialize)]
1609pub struct SourceDistribution {
1610    /// Manual entries percentage
1611    pub manual: f64,
1612    /// Automated system entries
1613    pub automated: f64,
1614    /// Recurring entries
1615    pub recurring: f64,
1616    /// Adjustment entries
1617    pub adjustment: f64,
1618}
1619
1620impl Default for SourceDistribution {
1621    fn default() -> Self {
1622        Self {
1623            manual: 0.20,
1624            automated: 0.70,
1625            recurring: 0.07,
1626            adjustment: 0.03,
1627        }
1628    }
1629}
1630
1631/// Output configuration.
1632#[derive(Debug, Clone, Serialize, Deserialize)]
1633pub struct OutputConfig {
1634    /// Output mode
1635    #[serde(default)]
1636    pub mode: OutputMode,
1637    /// Output directory
1638    pub output_directory: PathBuf,
1639    /// File formats to generate
1640    #[serde(default = "default_formats")]
1641    pub formats: Vec<FileFormat>,
1642    /// Compression settings
1643    #[serde(default)]
1644    pub compression: CompressionConfig,
1645    /// Batch size for writes
1646    #[serde(default = "default_batch_size")]
1647    pub batch_size: usize,
1648    /// Include ACDOCA format
1649    #[serde(default = "default_true")]
1650    pub include_acdoca: bool,
1651    /// Include BSEG format
1652    #[serde(default)]
1653    pub include_bseg: bool,
1654    /// Partition by fiscal period
1655    #[serde(default = "default_true")]
1656    pub partition_by_period: bool,
1657    /// Partition by company code
1658    #[serde(default)]
1659    pub partition_by_company: bool,
1660    /// Numeric serialization mode for JSON output.
1661    /// "string" (default): decimals as `"1729237.30"` — lossless precision.
1662    /// "native": decimals as `1729237.30` — friendlier for pandas/analytics.
1663    #[serde(default)]
1664    pub numeric_mode: NumericMode,
1665    /// JSON export layout for journal entries and document flows.
1666    /// "nested" (default): `{"header": {...}, "lines": [...]}` — natural ERP structure.
1667    /// "flat": header fields repeated on every line — friendlier for analytics/ML.
1668    ///
1669    /// Accepts both `export_layout` (canonical / YAML) and `exportLayout`
1670    /// (camelCase / SDK JSON) so SDKs that follow camelCase conventions
1671    /// hit the flat path rather than silently getting the Nested default.
1672    /// Before v3.1.1 the missing camelCase alias meant SDK requests with
1673    /// `exportLayout: "flat"` were silently ignored, which SDK operators
1674    /// reported as "flat hangs generation" (the job completed with Nested
1675    /// layout, but manifests didn't match the expected flat shape).
1676    #[serde(default, alias = "exportLayout")]
1677    pub export_layout: ExportLayout,
1678}
1679
1680fn default_formats() -> Vec<FileFormat> {
1681    vec![FileFormat::Parquet]
1682}
1683fn default_batch_size() -> usize {
1684    100_000
1685}
1686
1687impl Default for OutputConfig {
1688    fn default() -> Self {
1689        Self {
1690            mode: OutputMode::FlatFile,
1691            output_directory: PathBuf::from("./output"),
1692            formats: default_formats(),
1693            compression: CompressionConfig::default(),
1694            batch_size: default_batch_size(),
1695            include_acdoca: true,
1696            include_bseg: false,
1697            partition_by_period: true,
1698            partition_by_company: false,
1699            numeric_mode: NumericMode::default(),
1700            export_layout: ExportLayout::default(),
1701        }
1702    }
1703}
1704
1705/// Numeric serialization mode for JSON decimal fields.
1706#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)]
1707#[serde(rename_all = "snake_case")]
1708pub enum NumericMode {
1709    /// Decimals as JSON strings (e.g. `"1729237.30"`). Preserves full precision.
1710    #[default]
1711    String,
1712    /// Decimals as JSON numbers (e.g. `1729237.30`). Friendlier for pandas/analytics.
1713    Native,
1714}
1715
1716/// JSON export layout for nested structures (journal entries, document flows).
1717#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)]
1718#[serde(rename_all = "snake_case")]
1719pub enum ExportLayout {
1720    /// Nested structure: `{"header": {...}, "lines": [...]}`. Natural ERP format.
1721    #[default]
1722    Nested,
1723    /// Flat structure: header fields repeated on every line. Analytics-friendly.
1724    Flat,
1725}
1726
1727/// Output mode.
1728#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize)]
1729#[serde(rename_all = "snake_case")]
1730pub enum OutputMode {
1731    /// Stream records as generated
1732    Streaming,
1733    /// Write to flat files
1734    #[default]
1735    FlatFile,
1736    /// Both streaming and flat file
1737    Both,
1738}
1739
1740/// Supported file formats.
1741#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
1742#[serde(rename_all = "snake_case")]
1743pub enum FileFormat {
1744    Csv,
1745    Parquet,
1746    Json,
1747    JsonLines,
1748}
1749
1750/// Compression configuration.
1751#[derive(Debug, Clone, Serialize, Deserialize)]
1752pub struct CompressionConfig {
1753    /// Enable compression
1754    #[serde(default = "default_true")]
1755    pub enabled: bool,
1756    /// Compression algorithm
1757    #[serde(default)]
1758    pub algorithm: CompressionAlgorithm,
1759    /// Compression level (1-9)
1760    #[serde(default = "default_compression_level")]
1761    pub level: u8,
1762}
1763
1764fn default_compression_level() -> u8 {
1765    3
1766}
1767
1768impl Default for CompressionConfig {
1769    fn default() -> Self {
1770        Self {
1771            enabled: true,
1772            algorithm: CompressionAlgorithm::default(),
1773            level: default_compression_level(),
1774        }
1775    }
1776}
1777
1778/// Compression algorithms.
1779#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize)]
1780#[serde(rename_all = "snake_case")]
1781pub enum CompressionAlgorithm {
1782    Gzip,
1783    #[default]
1784    Zstd,
1785    Lz4,
1786    Snappy,
1787}
1788
1789/// Fraud simulation configuration.
1790///
1791/// ## Document-level vs. line-level fraud
1792///
1793/// `fraud_rate` applies to individual journal-entry lines (line-level).
1794/// `document_fraud_rate` (optional) applies to source documents
1795/// (purchase orders, vendor invoices, customer invoices, payments), and when
1796/// `propagate_to_lines` is true, every JE derived from a fraudulent document
1797/// also gets `is_fraud = true`. This lets users express either:
1798///
1799///  * pure line-level fraud (`document_fraud_rate = None`): legacy behaviour;
1800///  * pure document-level fraud (`fraud_rate ≈ 0` and `document_fraud_rate` set):
1801///    fraud rings expressed at document granularity — realistic for PO/invoice
1802///    fraud schemes where one fraudulent document spawns multiple derived JEs;
1803///  * hybrid (both set): document-level scheme fraud plus unrelated line-level
1804///    slip-ups.
1805///
1806/// `propagate_to_document` does the inverse: when a JE is tagged as fraud by
1807/// the anomaly injector, its source document is also marked fraudulent.
1808#[derive(Debug, Clone, Serialize, Deserialize)]
1809pub struct FraudConfig {
1810    /// Enable fraud scenario generation
1811    #[serde(default)]
1812    pub enabled: bool,
1813    /// Line-level fraud rate: fraction of individual JE lines flagged as fraud (0.0 to 1.0).
1814    ///
1815    /// # Effective line-level prevalence
1816    ///
1817    /// If `document_fraud_rate = Some(d)` and `propagate_to_lines = true`,
1818    /// the observed line-level fraud prevalence is roughly:
1819    ///
1820    ///     P(line is_fraud) ≈ fraud_rate + d × avg_lines_per_fraud_doc / total_lines
1821    ///
1822    /// For a typical retail job (avg 3 lines per document, ~30 % of lines
1823    /// come from doc-flow-derived JEs) the combined rate lands near:
1824    ///
1825    ///     fraud_rate + 0.3 × d
1826    ///
1827    /// so setting `fraud_rate=0.02, document_fraud_rate=0.05, propagate_to_lines=true`
1828    /// produces ~3.5 % line-level fraud, not 2 %. To target a specific
1829    /// line-level prevalence X, choose `fraud_rate = X - 0.3 × d`.
1830    #[serde(default = "default_fraud_rate", alias = "fraudRate")]
1831    pub fraud_rate: f64,
1832    /// Document-level fraud rate: fraction of source documents (PO, vendor
1833    /// invoice, customer invoice, payment) flagged as fraud. `None` disables
1834    /// document-level injection; `Some(r)` marks ~r × document-count as fraud
1835    /// independently of the line-level rate.
1836    #[serde(default, alias = "documentFraudRate")]
1837    pub document_fraud_rate: Option<f64>,
1838    /// When true, flagging a document as fraudulent cascades `is_fraud = true`
1839    /// and `fraud_type` to every journal entry derived from that document,
1840    /// and records `fraud_source_document_id` on the JE header.
1841    /// Default: `true`.
1842    #[serde(default = "default_true", alias = "propagateToLines")]
1843    pub propagate_to_lines: bool,
1844    /// When true, tagging a JE as fraud via line-level anomaly injection also
1845    /// marks the JE's source document as fraudulent (if it can be resolved).
1846    /// Default: `true`.
1847    #[serde(default = "default_true", alias = "propagateToDocument")]
1848    pub propagate_to_document: bool,
1849    /// Fraud type distribution
1850    #[serde(default)]
1851    pub fraud_type_distribution: FraudTypeDistribution,
1852    /// Enable fraud clustering
1853    #[serde(default)]
1854    pub clustering_enabled: bool,
1855    /// Clustering factor
1856    #[serde(default = "default_clustering_factor")]
1857    pub clustering_factor: f64,
1858    /// Approval thresholds for threshold-adjacent fraud pattern
1859    #[serde(default = "default_approval_thresholds")]
1860    pub approval_thresholds: Vec<f64>,
1861}
1862
1863fn default_approval_thresholds() -> Vec<f64> {
1864    vec![1000.0, 5000.0, 10000.0, 25000.0, 50000.0, 100000.0]
1865}
1866
1867fn default_fraud_rate() -> f64 {
1868    0.005
1869}
1870fn default_clustering_factor() -> f64 {
1871    3.0
1872}
1873
1874impl Default for FraudConfig {
1875    fn default() -> Self {
1876        Self {
1877            enabled: false,
1878            fraud_rate: default_fraud_rate(),
1879            document_fraud_rate: None,
1880            propagate_to_lines: true,
1881            propagate_to_document: true,
1882            fraud_type_distribution: FraudTypeDistribution::default(),
1883            clustering_enabled: false,
1884            clustering_factor: default_clustering_factor(),
1885            approval_thresholds: default_approval_thresholds(),
1886        }
1887    }
1888}
1889
1890/// Distribution of fraud types.
1891#[derive(Debug, Clone, Serialize, Deserialize)]
1892pub struct FraudTypeDistribution {
1893    pub suspense_account_abuse: f64,
1894    pub fictitious_transaction: f64,
1895    pub revenue_manipulation: f64,
1896    pub expense_capitalization: f64,
1897    pub split_transaction: f64,
1898    pub timing_anomaly: f64,
1899    pub unauthorized_access: f64,
1900    pub duplicate_payment: f64,
1901}
1902
1903impl Default for FraudTypeDistribution {
1904    fn default() -> Self {
1905        Self {
1906            suspense_account_abuse: 0.25,
1907            fictitious_transaction: 0.15,
1908            revenue_manipulation: 0.10,
1909            expense_capitalization: 0.10,
1910            split_transaction: 0.15,
1911            timing_anomaly: 0.10,
1912            unauthorized_access: 0.10,
1913            duplicate_payment: 0.05,
1914        }
1915    }
1916}
1917
1918/// Internal Controls System (ICS) configuration.
1919#[derive(Debug, Clone, Serialize, Deserialize)]
1920pub struct InternalControlsConfig {
1921    /// Enable internal controls system
1922    #[serde(default)]
1923    pub enabled: bool,
1924    /// Rate at which controls result in exceptions (0.0 - 1.0)
1925    #[serde(default = "default_exception_rate")]
1926    pub exception_rate: f64,
1927    /// Rate at which SoD violations occur (0.0 - 1.0)
1928    #[serde(default = "default_sod_violation_rate")]
1929    pub sod_violation_rate: f64,
1930    /// Export control master data to separate files
1931    #[serde(default = "default_true")]
1932    pub export_control_master_data: bool,
1933    /// SOX materiality threshold for marking transactions as SOX-relevant
1934    #[serde(default = "default_sox_materiality_threshold")]
1935    pub sox_materiality_threshold: f64,
1936    /// Enable COSO 2013 framework integration
1937    #[serde(default = "default_true")]
1938    pub coso_enabled: bool,
1939    /// Include entity-level controls in generation
1940    #[serde(default)]
1941    pub include_entity_level_controls: bool,
1942    /// Target maturity level for controls
1943    /// Valid values: "ad_hoc", "repeatable", "defined", "managed", "optimized", "mixed"
1944    #[serde(default = "default_target_maturity_level")]
1945    pub target_maturity_level: String,
1946}
1947
1948fn default_exception_rate() -> f64 {
1949    0.02
1950}
1951
1952fn default_sod_violation_rate() -> f64 {
1953    0.01
1954}
1955
1956fn default_sox_materiality_threshold() -> f64 {
1957    10000.0
1958}
1959
1960fn default_target_maturity_level() -> String {
1961    "mixed".to_string()
1962}
1963
1964impl Default for InternalControlsConfig {
1965    fn default() -> Self {
1966        Self {
1967            enabled: false,
1968            exception_rate: default_exception_rate(),
1969            sod_violation_rate: default_sod_violation_rate(),
1970            export_control_master_data: true,
1971            sox_materiality_threshold: default_sox_materiality_threshold(),
1972            coso_enabled: true,
1973            include_entity_level_controls: false,
1974            target_maturity_level: default_target_maturity_level(),
1975        }
1976    }
1977}
1978
1979/// Business process configuration.
1980#[derive(Debug, Clone, Serialize, Deserialize)]
1981pub struct BusinessProcessConfig {
1982    /// Order-to-Cash weight
1983    #[serde(default = "default_o2c")]
1984    pub o2c_weight: f64,
1985    /// Procure-to-Pay weight
1986    #[serde(default = "default_p2p")]
1987    pub p2p_weight: f64,
1988    /// Record-to-Report weight
1989    #[serde(default = "default_r2r")]
1990    pub r2r_weight: f64,
1991    /// Hire-to-Retire weight
1992    #[serde(default = "default_h2r")]
1993    pub h2r_weight: f64,
1994    /// Acquire-to-Retire weight
1995    #[serde(default = "default_a2r")]
1996    pub a2r_weight: f64,
1997}
1998
1999fn default_o2c() -> f64 {
2000    0.35
2001}
2002fn default_p2p() -> f64 {
2003    0.30
2004}
2005fn default_r2r() -> f64 {
2006    0.20
2007}
2008fn default_h2r() -> f64 {
2009    0.10
2010}
2011fn default_a2r() -> f64 {
2012    0.05
2013}
2014
2015impl Default for BusinessProcessConfig {
2016    fn default() -> Self {
2017        Self {
2018            o2c_weight: default_o2c(),
2019            p2p_weight: default_p2p(),
2020            r2r_weight: default_r2r(),
2021            h2r_weight: default_h2r(),
2022            a2r_weight: default_a2r(),
2023        }
2024    }
2025}
2026
2027/// User persona configuration.
2028#[derive(Debug, Clone, Serialize, Deserialize, Default)]
2029pub struct UserPersonaConfig {
2030    /// Distribution of user personas
2031    #[serde(default)]
2032    pub persona_distribution: PersonaDistribution,
2033    /// Users per persona type
2034    #[serde(default)]
2035    pub users_per_persona: UsersPerPersona,
2036}
2037
2038/// Distribution of user personas for transaction generation.
2039#[derive(Debug, Clone, Serialize, Deserialize)]
2040pub struct PersonaDistribution {
2041    pub junior_accountant: f64,
2042    pub senior_accountant: f64,
2043    pub controller: f64,
2044    pub manager: f64,
2045    pub automated_system: f64,
2046}
2047
2048impl Default for PersonaDistribution {
2049    fn default() -> Self {
2050        Self {
2051            junior_accountant: 0.15,
2052            senior_accountant: 0.15,
2053            controller: 0.05,
2054            manager: 0.05,
2055            automated_system: 0.60,
2056        }
2057    }
2058}
2059
2060/// Number of users per persona type.
2061#[derive(Debug, Clone, Serialize, Deserialize)]
2062pub struct UsersPerPersona {
2063    pub junior_accountant: usize,
2064    pub senior_accountant: usize,
2065    pub controller: usize,
2066    pub manager: usize,
2067    pub automated_system: usize,
2068}
2069
2070impl Default for UsersPerPersona {
2071    fn default() -> Self {
2072        Self {
2073            junior_accountant: 10,
2074            senior_accountant: 5,
2075            controller: 2,
2076            manager: 3,
2077            automated_system: 20,
2078        }
2079    }
2080}
2081
2082/// Template configuration for realistic data generation.
2083///
2084/// # User-supplied template packs (v3.2.0+)
2085///
2086/// Set `path` to a directory (or single YAML/JSON file) to override or
2087/// extend the embedded default pools for vendor names, customer names,
2088/// material/asset descriptions, audit findings, bank names, and
2089/// department names. When `path` is `None` (the default), generators
2090/// use the compiled-in pools and output is byte-identical to v3.1.2.
2091///
2092/// See `crates/datasynth-core/src/templates/loader.rs::TemplateData`
2093/// for the full YAML schema. Use `datasynth-data templates export` to
2094/// dump the defaults as a starter pack.
2095#[derive(Debug, Clone, Serialize, Deserialize, Default)]
2096pub struct TemplateConfig {
2097    /// Name generation settings
2098    #[serde(default)]
2099    pub names: NameTemplateConfig,
2100    /// Description generation settings
2101    #[serde(default)]
2102    pub descriptions: DescriptionTemplateConfig,
2103    /// Reference number settings
2104    #[serde(default)]
2105    pub references: ReferenceTemplateConfig,
2106    /// Optional path to a user-supplied template file or directory.
2107    /// When set, entries from the file(s) augment or replace the
2108    /// embedded defaults according to `merge_strategy`.
2109    ///
2110    /// `None` (default) = use embedded pools only (byte-identical to v3.1.2).
2111    #[serde(default, alias = "templatesPath")]
2112    pub path: Option<std::path::PathBuf>,
2113    /// How file-based entries combine with embedded defaults.
2114    ///
2115    /// - `extend` (default): append file entries to embedded pools,
2116    ///   de-duplicating. Safe for incremental overlays.
2117    /// - `replace`: discard embedded pools entirely and use only file
2118    ///   entries. Requires a fully-populated template file.
2119    /// - `merge_prefer_file`: replace individual categories when present
2120    ///   in the file; keep embedded for absent categories.
2121    #[serde(default, alias = "mergeStrategy")]
2122    pub merge_strategy: TemplateMergeStrategy,
2123}
2124
2125/// Strategy for combining user-supplied template files with embedded defaults.
2126#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
2127#[serde(rename_all = "snake_case")]
2128pub enum TemplateMergeStrategy {
2129    /// Append file entries to embedded pools (default).
2130    #[default]
2131    Extend,
2132    /// Replace embedded pools entirely with file entries.
2133    Replace,
2134    /// Replace individual categories when present in file; keep embedded for absent ones.
2135    MergePreferFile,
2136}
2137
2138/// Name template configuration.
2139#[derive(Debug, Clone, Serialize, Deserialize)]
2140pub struct NameTemplateConfig {
2141    /// Distribution of name cultures
2142    #[serde(default)]
2143    pub culture_distribution: CultureDistribution,
2144    /// Email domain for generated users
2145    #[serde(default = "default_email_domain")]
2146    pub email_domain: String,
2147    /// Generate realistic display names
2148    #[serde(default = "default_true")]
2149    pub generate_realistic_names: bool,
2150}
2151
2152fn default_email_domain() -> String {
2153    "company.com".to_string()
2154}
2155
2156impl Default for NameTemplateConfig {
2157    fn default() -> Self {
2158        Self {
2159            culture_distribution: CultureDistribution::default(),
2160            email_domain: default_email_domain(),
2161            generate_realistic_names: true,
2162        }
2163    }
2164}
2165
2166/// Distribution of name cultures for generation.
2167#[derive(Debug, Clone, Serialize, Deserialize)]
2168pub struct CultureDistribution {
2169    pub western_us: f64,
2170    pub hispanic: f64,
2171    pub german: f64,
2172    pub french: f64,
2173    pub chinese: f64,
2174    pub japanese: f64,
2175    pub indian: f64,
2176}
2177
2178impl Default for CultureDistribution {
2179    fn default() -> Self {
2180        Self {
2181            western_us: 0.40,
2182            hispanic: 0.20,
2183            german: 0.10,
2184            french: 0.05,
2185            chinese: 0.10,
2186            japanese: 0.05,
2187            indian: 0.10,
2188        }
2189    }
2190}
2191
2192/// Description template configuration.
2193#[derive(Debug, Clone, Serialize, Deserialize)]
2194pub struct DescriptionTemplateConfig {
2195    /// Generate header text for journal entries
2196    #[serde(default = "default_true")]
2197    pub generate_header_text: bool,
2198    /// Generate line text for journal entry lines
2199    #[serde(default = "default_true")]
2200    pub generate_line_text: bool,
2201}
2202
2203impl Default for DescriptionTemplateConfig {
2204    fn default() -> Self {
2205        Self {
2206            generate_header_text: true,
2207            generate_line_text: true,
2208        }
2209    }
2210}
2211
2212/// Reference number template configuration.
2213#[derive(Debug, Clone, Serialize, Deserialize)]
2214pub struct ReferenceTemplateConfig {
2215    /// Generate reference numbers
2216    #[serde(default = "default_true")]
2217    pub generate_references: bool,
2218    /// Invoice prefix
2219    #[serde(default = "default_invoice_prefix")]
2220    pub invoice_prefix: String,
2221    /// Purchase order prefix
2222    #[serde(default = "default_po_prefix")]
2223    pub po_prefix: String,
2224    /// Sales order prefix
2225    #[serde(default = "default_so_prefix")]
2226    pub so_prefix: String,
2227}
2228
2229fn default_invoice_prefix() -> String {
2230    "INV".to_string()
2231}
2232fn default_po_prefix() -> String {
2233    "PO".to_string()
2234}
2235fn default_so_prefix() -> String {
2236    "SO".to_string()
2237}
2238
2239impl Default for ReferenceTemplateConfig {
2240    fn default() -> Self {
2241        Self {
2242            generate_references: true,
2243            invoice_prefix: default_invoice_prefix(),
2244            po_prefix: default_po_prefix(),
2245            so_prefix: default_so_prefix(),
2246        }
2247    }
2248}
2249
2250/// Approval workflow configuration.
2251#[derive(Debug, Clone, Serialize, Deserialize)]
2252pub struct ApprovalConfig {
2253    /// Enable approval workflow generation
2254    #[serde(default)]
2255    pub enabled: bool,
2256    /// Threshold below which transactions are auto-approved
2257    #[serde(default = "default_auto_approve_threshold")]
2258    pub auto_approve_threshold: f64,
2259    /// Rate at which approvals are rejected (0.0 to 1.0)
2260    #[serde(default = "default_rejection_rate")]
2261    pub rejection_rate: f64,
2262    /// Rate at which approvals require revision (0.0 to 1.0)
2263    #[serde(default = "default_revision_rate")]
2264    pub revision_rate: f64,
2265    /// Average delay in hours for approval processing
2266    #[serde(default = "default_approval_delay_hours")]
2267    pub average_approval_delay_hours: f64,
2268    /// Approval chain thresholds
2269    #[serde(default)]
2270    pub thresholds: Vec<ApprovalThresholdConfig>,
2271}
2272
2273fn default_auto_approve_threshold() -> f64 {
2274    1000.0
2275}
2276fn default_rejection_rate() -> f64 {
2277    0.02
2278}
2279fn default_revision_rate() -> f64 {
2280    0.05
2281}
2282fn default_approval_delay_hours() -> f64 {
2283    4.0
2284}
2285
2286impl Default for ApprovalConfig {
2287    fn default() -> Self {
2288        Self {
2289            enabled: false,
2290            auto_approve_threshold: default_auto_approve_threshold(),
2291            rejection_rate: default_rejection_rate(),
2292            revision_rate: default_revision_rate(),
2293            average_approval_delay_hours: default_approval_delay_hours(),
2294            thresholds: vec![
2295                ApprovalThresholdConfig {
2296                    amount: 1000.0,
2297                    level: 1,
2298                    roles: vec!["senior_accountant".to_string()],
2299                },
2300                ApprovalThresholdConfig {
2301                    amount: 10000.0,
2302                    level: 2,
2303                    roles: vec!["senior_accountant".to_string(), "controller".to_string()],
2304                },
2305                ApprovalThresholdConfig {
2306                    amount: 100000.0,
2307                    level: 3,
2308                    roles: vec![
2309                        "senior_accountant".to_string(),
2310                        "controller".to_string(),
2311                        "manager".to_string(),
2312                    ],
2313                },
2314                ApprovalThresholdConfig {
2315                    amount: 500000.0,
2316                    level: 4,
2317                    roles: vec![
2318                        "senior_accountant".to_string(),
2319                        "controller".to_string(),
2320                        "manager".to_string(),
2321                        "executive".to_string(),
2322                    ],
2323                },
2324            ],
2325        }
2326    }
2327}
2328
2329/// Configuration for a single approval threshold.
2330#[derive(Debug, Clone, Serialize, Deserialize)]
2331pub struct ApprovalThresholdConfig {
2332    /// Amount threshold
2333    pub amount: f64,
2334    /// Approval level required
2335    pub level: u8,
2336    /// Roles that can approve at this level
2337    pub roles: Vec<String>,
2338}
2339
2340/// Department configuration.
2341#[derive(Debug, Clone, Serialize, Deserialize)]
2342pub struct DepartmentConfig {
2343    /// Enable department assignment
2344    #[serde(default)]
2345    pub enabled: bool,
2346    /// Multiplier for department headcounts
2347    #[serde(default = "default_headcount_multiplier")]
2348    pub headcount_multiplier: f64,
2349    /// Custom department definitions (optional)
2350    #[serde(default)]
2351    pub custom_departments: Vec<CustomDepartmentConfig>,
2352}
2353
2354fn default_headcount_multiplier() -> f64 {
2355    1.0
2356}
2357
2358impl Default for DepartmentConfig {
2359    fn default() -> Self {
2360        Self {
2361            enabled: false,
2362            headcount_multiplier: default_headcount_multiplier(),
2363            custom_departments: Vec::new(),
2364        }
2365    }
2366}
2367
2368/// Custom department definition.
2369#[derive(Debug, Clone, Serialize, Deserialize)]
2370pub struct CustomDepartmentConfig {
2371    /// Department code
2372    pub code: String,
2373    /// Department name
2374    pub name: String,
2375    /// Associated cost center
2376    #[serde(default)]
2377    pub cost_center: Option<String>,
2378    /// Primary business processes
2379    #[serde(default)]
2380    pub primary_processes: Vec<String>,
2381    /// Parent department code
2382    #[serde(default)]
2383    pub parent_code: Option<String>,
2384}
2385
2386// ============================================================================
2387// Master Data Configuration
2388// ============================================================================
2389
2390/// Master data generation configuration.
2391#[derive(Debug, Clone, Default, Serialize, Deserialize)]
2392pub struct MasterDataConfig {
2393    /// Vendor master data settings
2394    #[serde(default)]
2395    pub vendors: VendorMasterConfig,
2396    /// Customer master data settings
2397    #[serde(default)]
2398    pub customers: CustomerMasterConfig,
2399    /// Material master data settings
2400    #[serde(default)]
2401    pub materials: MaterialMasterConfig,
2402    /// Fixed asset master data settings
2403    #[serde(default)]
2404    pub fixed_assets: FixedAssetMasterConfig,
2405    /// Employee master data settings
2406    #[serde(default)]
2407    pub employees: EmployeeMasterConfig,
2408    /// Cost center master data settings
2409    #[serde(default)]
2410    pub cost_centers: CostCenterMasterConfig,
2411}
2412
2413/// Vendor master data configuration.
2414#[derive(Debug, Clone, Serialize, Deserialize)]
2415pub struct VendorMasterConfig {
2416    /// Number of vendors to generate
2417    #[serde(default = "default_vendor_count")]
2418    pub count: usize,
2419    /// Percentage of vendors that are intercompany (0.0 to 1.0)
2420    #[serde(default = "default_intercompany_percent")]
2421    pub intercompany_percent: f64,
2422    /// Payment terms distribution
2423    #[serde(default)]
2424    pub payment_terms_distribution: PaymentTermsDistribution,
2425    /// Vendor behavior distribution
2426    #[serde(default)]
2427    pub behavior_distribution: VendorBehaviorDistribution,
2428    /// Generate bank account details
2429    #[serde(default = "default_true")]
2430    pub generate_bank_accounts: bool,
2431    /// Generate tax IDs
2432    #[serde(default = "default_true")]
2433    pub generate_tax_ids: bool,
2434}
2435
2436fn default_vendor_count() -> usize {
2437    500
2438}
2439
2440fn default_intercompany_percent() -> f64 {
2441    0.05
2442}
2443
2444impl Default for VendorMasterConfig {
2445    fn default() -> Self {
2446        Self {
2447            count: default_vendor_count(),
2448            intercompany_percent: default_intercompany_percent(),
2449            payment_terms_distribution: PaymentTermsDistribution::default(),
2450            behavior_distribution: VendorBehaviorDistribution::default(),
2451            generate_bank_accounts: true,
2452            generate_tax_ids: true,
2453        }
2454    }
2455}
2456
2457/// Payment terms distribution for vendors.
2458#[derive(Debug, Clone, Serialize, Deserialize)]
2459pub struct PaymentTermsDistribution {
2460    /// Net 30 days
2461    pub net_30: f64,
2462    /// Net 60 days
2463    pub net_60: f64,
2464    /// Net 90 days
2465    pub net_90: f64,
2466    /// 2% 10 Net 30 (early payment discount)
2467    pub two_ten_net_30: f64,
2468    /// Due on receipt
2469    pub due_on_receipt: f64,
2470    /// End of month
2471    pub end_of_month: f64,
2472}
2473
2474impl Default for PaymentTermsDistribution {
2475    fn default() -> Self {
2476        Self {
2477            net_30: 0.40,
2478            net_60: 0.20,
2479            net_90: 0.10,
2480            two_ten_net_30: 0.15,
2481            due_on_receipt: 0.05,
2482            end_of_month: 0.10,
2483        }
2484    }
2485}
2486
2487/// Vendor behavior distribution.
2488#[derive(Debug, Clone, Serialize, Deserialize)]
2489pub struct VendorBehaviorDistribution {
2490    /// Reliable vendors (consistent delivery, quality)
2491    pub reliable: f64,
2492    /// Sometimes late vendors
2493    pub sometimes_late: f64,
2494    /// Inconsistent quality vendors
2495    pub inconsistent_quality: f64,
2496    /// Premium vendors (high quality, premium pricing)
2497    pub premium: f64,
2498    /// Budget vendors (lower quality, lower pricing)
2499    pub budget: f64,
2500}
2501
2502impl Default for VendorBehaviorDistribution {
2503    fn default() -> Self {
2504        Self {
2505            reliable: 0.50,
2506            sometimes_late: 0.20,
2507            inconsistent_quality: 0.10,
2508            premium: 0.10,
2509            budget: 0.10,
2510        }
2511    }
2512}
2513
2514/// Customer master data configuration.
2515#[derive(Debug, Clone, Serialize, Deserialize)]
2516pub struct CustomerMasterConfig {
2517    /// Number of customers to generate
2518    #[serde(default = "default_customer_count")]
2519    pub count: usize,
2520    /// Percentage of customers that are intercompany (0.0 to 1.0)
2521    #[serde(default = "default_intercompany_percent")]
2522    pub intercompany_percent: f64,
2523    /// Credit rating distribution
2524    #[serde(default)]
2525    pub credit_rating_distribution: CreditRatingDistribution,
2526    /// Payment behavior distribution
2527    #[serde(default)]
2528    pub payment_behavior_distribution: PaymentBehaviorDistribution,
2529    /// Generate credit limits based on rating
2530    #[serde(default = "default_true")]
2531    pub generate_credit_limits: bool,
2532}
2533
2534fn default_customer_count() -> usize {
2535    2000
2536}
2537
2538impl Default for CustomerMasterConfig {
2539    fn default() -> Self {
2540        Self {
2541            count: default_customer_count(),
2542            intercompany_percent: default_intercompany_percent(),
2543            credit_rating_distribution: CreditRatingDistribution::default(),
2544            payment_behavior_distribution: PaymentBehaviorDistribution::default(),
2545            generate_credit_limits: true,
2546        }
2547    }
2548}
2549
2550/// Credit rating distribution for customers.
2551#[derive(Debug, Clone, Serialize, Deserialize)]
2552pub struct CreditRatingDistribution {
2553    /// AAA rating
2554    pub aaa: f64,
2555    /// AA rating
2556    pub aa: f64,
2557    /// A rating
2558    pub a: f64,
2559    /// BBB rating
2560    pub bbb: f64,
2561    /// BB rating
2562    pub bb: f64,
2563    /// B rating
2564    pub b: f64,
2565    /// Below B rating
2566    pub below_b: f64,
2567}
2568
2569impl Default for CreditRatingDistribution {
2570    fn default() -> Self {
2571        Self {
2572            aaa: 0.05,
2573            aa: 0.10,
2574            a: 0.20,
2575            bbb: 0.30,
2576            bb: 0.20,
2577            b: 0.10,
2578            below_b: 0.05,
2579        }
2580    }
2581}
2582
2583/// Payment behavior distribution for customers.
2584#[derive(Debug, Clone, Serialize, Deserialize)]
2585pub struct PaymentBehaviorDistribution {
2586    /// Always pays early
2587    pub early_payer: f64,
2588    /// Pays on time
2589    pub on_time: f64,
2590    /// Occasionally late
2591    pub occasional_late: f64,
2592    /// Frequently late
2593    pub frequent_late: f64,
2594    /// Takes early payment discounts
2595    pub discount_taker: f64,
2596}
2597
2598impl Default for PaymentBehaviorDistribution {
2599    fn default() -> Self {
2600        Self {
2601            early_payer: 0.10,
2602            on_time: 0.50,
2603            occasional_late: 0.25,
2604            frequent_late: 0.10,
2605            discount_taker: 0.05,
2606        }
2607    }
2608}
2609
2610/// Material master data configuration.
2611#[derive(Debug, Clone, Serialize, Deserialize)]
2612pub struct MaterialMasterConfig {
2613    /// Number of materials to generate
2614    #[serde(default = "default_material_count")]
2615    pub count: usize,
2616    /// Material type distribution
2617    #[serde(default)]
2618    pub type_distribution: MaterialTypeDistribution,
2619    /// Valuation method distribution
2620    #[serde(default)]
2621    pub valuation_distribution: ValuationMethodDistribution,
2622    /// Percentage of materials with BOM (bill of materials)
2623    #[serde(default = "default_bom_percent")]
2624    pub bom_percent: f64,
2625    /// Maximum BOM depth
2626    #[serde(default = "default_max_bom_depth")]
2627    pub max_bom_depth: u8,
2628}
2629
2630fn default_material_count() -> usize {
2631    5000
2632}
2633
2634fn default_bom_percent() -> f64 {
2635    0.20
2636}
2637
2638fn default_max_bom_depth() -> u8 {
2639    3
2640}
2641
2642impl Default for MaterialMasterConfig {
2643    fn default() -> Self {
2644        Self {
2645            count: default_material_count(),
2646            type_distribution: MaterialTypeDistribution::default(),
2647            valuation_distribution: ValuationMethodDistribution::default(),
2648            bom_percent: default_bom_percent(),
2649            max_bom_depth: default_max_bom_depth(),
2650        }
2651    }
2652}
2653
2654/// Material type distribution.
2655#[derive(Debug, Clone, Serialize, Deserialize)]
2656pub struct MaterialTypeDistribution {
2657    /// Raw materials
2658    pub raw_material: f64,
2659    /// Semi-finished goods
2660    pub semi_finished: f64,
2661    /// Finished goods
2662    pub finished_good: f64,
2663    /// Trading goods (purchased for resale)
2664    pub trading_good: f64,
2665    /// Operating supplies
2666    pub operating_supply: f64,
2667    /// Services
2668    pub service: f64,
2669}
2670
2671impl Default for MaterialTypeDistribution {
2672    fn default() -> Self {
2673        Self {
2674            raw_material: 0.30,
2675            semi_finished: 0.15,
2676            finished_good: 0.25,
2677            trading_good: 0.15,
2678            operating_supply: 0.10,
2679            service: 0.05,
2680        }
2681    }
2682}
2683
2684/// Valuation method distribution for materials.
2685#[derive(Debug, Clone, Serialize, Deserialize)]
2686pub struct ValuationMethodDistribution {
2687    /// Standard cost
2688    pub standard_cost: f64,
2689    /// Moving average
2690    pub moving_average: f64,
2691    /// FIFO (First In, First Out)
2692    pub fifo: f64,
2693    /// LIFO (Last In, First Out)
2694    pub lifo: f64,
2695}
2696
2697impl Default for ValuationMethodDistribution {
2698    fn default() -> Self {
2699        Self {
2700            standard_cost: 0.50,
2701            moving_average: 0.30,
2702            fifo: 0.15,
2703            lifo: 0.05,
2704        }
2705    }
2706}
2707
2708/// Fixed asset master data configuration.
2709#[derive(Debug, Clone, Serialize, Deserialize)]
2710pub struct FixedAssetMasterConfig {
2711    /// Number of fixed assets to generate
2712    #[serde(default = "default_asset_count")]
2713    pub count: usize,
2714    /// Asset class distribution
2715    #[serde(default)]
2716    pub class_distribution: AssetClassDistribution,
2717    /// Depreciation method distribution
2718    #[serde(default)]
2719    pub depreciation_distribution: DepreciationMethodDistribution,
2720    /// Percentage of assets that are fully depreciated
2721    #[serde(default = "default_fully_depreciated_percent")]
2722    pub fully_depreciated_percent: f64,
2723    /// Generate acquisition history
2724    #[serde(default = "default_true")]
2725    pub generate_acquisition_history: bool,
2726}
2727
2728fn default_asset_count() -> usize {
2729    800
2730}
2731
2732fn default_fully_depreciated_percent() -> f64 {
2733    0.15
2734}
2735
2736impl Default for FixedAssetMasterConfig {
2737    fn default() -> Self {
2738        Self {
2739            count: default_asset_count(),
2740            class_distribution: AssetClassDistribution::default(),
2741            depreciation_distribution: DepreciationMethodDistribution::default(),
2742            fully_depreciated_percent: default_fully_depreciated_percent(),
2743            generate_acquisition_history: true,
2744        }
2745    }
2746}
2747
2748/// Asset class distribution.
2749#[derive(Debug, Clone, Serialize, Deserialize)]
2750pub struct AssetClassDistribution {
2751    /// Buildings and structures
2752    pub buildings: f64,
2753    /// Machinery and equipment
2754    pub machinery: f64,
2755    /// Vehicles
2756    pub vehicles: f64,
2757    /// IT equipment
2758    pub it_equipment: f64,
2759    /// Furniture and fixtures
2760    pub furniture: f64,
2761    /// Land (non-depreciable)
2762    pub land: f64,
2763    /// Leasehold improvements
2764    pub leasehold: f64,
2765}
2766
2767impl Default for AssetClassDistribution {
2768    fn default() -> Self {
2769        Self {
2770            buildings: 0.15,
2771            machinery: 0.30,
2772            vehicles: 0.15,
2773            it_equipment: 0.20,
2774            furniture: 0.10,
2775            land: 0.05,
2776            leasehold: 0.05,
2777        }
2778    }
2779}
2780
2781/// Depreciation method distribution.
2782#[derive(Debug, Clone, Serialize, Deserialize)]
2783pub struct DepreciationMethodDistribution {
2784    /// Straight line
2785    pub straight_line: f64,
2786    /// Declining balance
2787    pub declining_balance: f64,
2788    /// Double declining balance
2789    pub double_declining: f64,
2790    /// Sum of years' digits
2791    pub sum_of_years: f64,
2792    /// Units of production
2793    pub units_of_production: f64,
2794}
2795
2796impl Default for DepreciationMethodDistribution {
2797    fn default() -> Self {
2798        Self {
2799            straight_line: 0.60,
2800            declining_balance: 0.20,
2801            double_declining: 0.10,
2802            sum_of_years: 0.05,
2803            units_of_production: 0.05,
2804        }
2805    }
2806}
2807
2808/// Employee master data configuration.
2809#[derive(Debug, Clone, Serialize, Deserialize)]
2810pub struct EmployeeMasterConfig {
2811    /// Number of employees to generate
2812    #[serde(default = "default_employee_count")]
2813    pub count: usize,
2814    /// Generate organizational hierarchy
2815    #[serde(default = "default_true")]
2816    pub generate_hierarchy: bool,
2817    /// Maximum hierarchy depth
2818    #[serde(default = "default_hierarchy_depth")]
2819    pub max_hierarchy_depth: u8,
2820    /// Average span of control (direct reports per manager)
2821    #[serde(default = "default_span_of_control")]
2822    pub average_span_of_control: f64,
2823    /// Approval limit distribution by job level
2824    #[serde(default)]
2825    pub approval_limits: ApprovalLimitDistribution,
2826    /// Department distribution
2827    #[serde(default)]
2828    pub department_distribution: EmployeeDepartmentDistribution,
2829}
2830
2831fn default_employee_count() -> usize {
2832    1500
2833}
2834
2835fn default_hierarchy_depth() -> u8 {
2836    6
2837}
2838
2839fn default_span_of_control() -> f64 {
2840    5.0
2841}
2842
2843impl Default for EmployeeMasterConfig {
2844    fn default() -> Self {
2845        Self {
2846            count: default_employee_count(),
2847            generate_hierarchy: true,
2848            max_hierarchy_depth: default_hierarchy_depth(),
2849            average_span_of_control: default_span_of_control(),
2850            approval_limits: ApprovalLimitDistribution::default(),
2851            department_distribution: EmployeeDepartmentDistribution::default(),
2852        }
2853    }
2854}
2855
2856/// Approval limit distribution by job level.
2857#[derive(Debug, Clone, Serialize, Deserialize)]
2858pub struct ApprovalLimitDistribution {
2859    /// Staff level approval limit
2860    #[serde(default = "default_staff_limit")]
2861    pub staff: f64,
2862    /// Senior staff approval limit
2863    #[serde(default = "default_senior_limit")]
2864    pub senior: f64,
2865    /// Manager approval limit
2866    #[serde(default = "default_manager_limit")]
2867    pub manager: f64,
2868    /// Director approval limit
2869    #[serde(default = "default_director_limit")]
2870    pub director: f64,
2871    /// VP approval limit
2872    #[serde(default = "default_vp_limit")]
2873    pub vp: f64,
2874    /// Executive approval limit
2875    #[serde(default = "default_executive_limit")]
2876    pub executive: f64,
2877}
2878
2879fn default_staff_limit() -> f64 {
2880    1000.0
2881}
2882fn default_senior_limit() -> f64 {
2883    5000.0
2884}
2885fn default_manager_limit() -> f64 {
2886    25000.0
2887}
2888fn default_director_limit() -> f64 {
2889    100000.0
2890}
2891fn default_vp_limit() -> f64 {
2892    500000.0
2893}
2894fn default_executive_limit() -> f64 {
2895    f64::INFINITY
2896}
2897
2898impl Default for ApprovalLimitDistribution {
2899    fn default() -> Self {
2900        Self {
2901            staff: default_staff_limit(),
2902            senior: default_senior_limit(),
2903            manager: default_manager_limit(),
2904            director: default_director_limit(),
2905            vp: default_vp_limit(),
2906            executive: default_executive_limit(),
2907        }
2908    }
2909}
2910
2911/// Employee distribution across departments.
2912#[derive(Debug, Clone, Serialize, Deserialize)]
2913pub struct EmployeeDepartmentDistribution {
2914    /// Finance and Accounting
2915    pub finance: f64,
2916    /// Procurement
2917    pub procurement: f64,
2918    /// Sales
2919    pub sales: f64,
2920    /// Warehouse and Logistics
2921    pub warehouse: f64,
2922    /// IT
2923    pub it: f64,
2924    /// Human Resources
2925    pub hr: f64,
2926    /// Operations
2927    pub operations: f64,
2928    /// Executive
2929    pub executive: f64,
2930}
2931
2932impl Default for EmployeeDepartmentDistribution {
2933    fn default() -> Self {
2934        Self {
2935            finance: 0.12,
2936            procurement: 0.10,
2937            sales: 0.25,
2938            warehouse: 0.15,
2939            it: 0.10,
2940            hr: 0.05,
2941            operations: 0.20,
2942            executive: 0.03,
2943        }
2944    }
2945}
2946
2947/// Cost center master data configuration.
2948#[derive(Debug, Clone, Serialize, Deserialize)]
2949pub struct CostCenterMasterConfig {
2950    /// Number of cost centers to generate
2951    #[serde(default = "default_cost_center_count")]
2952    pub count: usize,
2953    /// Generate cost center hierarchy
2954    #[serde(default = "default_true")]
2955    pub generate_hierarchy: bool,
2956    /// Maximum hierarchy depth
2957    #[serde(default = "default_cc_hierarchy_depth")]
2958    pub max_hierarchy_depth: u8,
2959}
2960
2961fn default_cost_center_count() -> usize {
2962    50
2963}
2964
2965fn default_cc_hierarchy_depth() -> u8 {
2966    3
2967}
2968
2969impl Default for CostCenterMasterConfig {
2970    fn default() -> Self {
2971        Self {
2972            count: default_cost_center_count(),
2973            generate_hierarchy: true,
2974            max_hierarchy_depth: default_cc_hierarchy_depth(),
2975        }
2976    }
2977}
2978
2979// ============================================================================
2980// Document Flow Configuration
2981// ============================================================================
2982
2983/// Document flow generation configuration.
2984#[derive(Debug, Clone, Serialize, Deserialize)]
2985pub struct DocumentFlowConfig {
2986    /// P2P (Procure-to-Pay) flow configuration
2987    #[serde(default)]
2988    pub p2p: P2PFlowConfig,
2989    /// O2C (Order-to-Cash) flow configuration
2990    #[serde(default)]
2991    pub o2c: O2CFlowConfig,
2992    /// Generate document reference chains
2993    #[serde(default = "default_true")]
2994    pub generate_document_references: bool,
2995    /// Export document flow graph
2996    #[serde(default)]
2997    pub export_flow_graph: bool,
2998}
2999
3000impl Default for DocumentFlowConfig {
3001    fn default() -> Self {
3002        Self {
3003            p2p: P2PFlowConfig::default(),
3004            o2c: O2CFlowConfig::default(),
3005            generate_document_references: true,
3006            export_flow_graph: false,
3007        }
3008    }
3009}
3010
3011/// P2P (Procure-to-Pay) flow configuration.
3012#[derive(Debug, Clone, Serialize, Deserialize)]
3013pub struct P2PFlowConfig {
3014    /// Enable P2P document flow generation
3015    #[serde(default = "default_true")]
3016    pub enabled: bool,
3017    /// Three-way match success rate (PO-GR-Invoice)
3018    #[serde(default = "default_three_way_match_rate")]
3019    pub three_way_match_rate: f64,
3020    /// Rate of partial deliveries
3021    #[serde(default = "default_partial_delivery_rate")]
3022    pub partial_delivery_rate: f64,
3023    /// Rate of price variances between PO and Invoice
3024    #[serde(default = "default_price_variance_rate")]
3025    pub price_variance_rate: f64,
3026    /// Maximum price variance percentage
3027    #[serde(default = "default_max_price_variance")]
3028    pub max_price_variance_percent: f64,
3029    /// Rate of quantity variances between PO/GR and Invoice
3030    #[serde(default = "default_quantity_variance_rate")]
3031    pub quantity_variance_rate: f64,
3032    /// Average days from PO to goods receipt
3033    #[serde(default = "default_po_to_gr_days")]
3034    pub average_po_to_gr_days: u32,
3035    /// Average days from GR to invoice
3036    #[serde(default = "default_gr_to_invoice_days")]
3037    pub average_gr_to_invoice_days: u32,
3038    /// Average days from invoice to payment
3039    #[serde(default = "default_invoice_to_payment_days")]
3040    pub average_invoice_to_payment_days: u32,
3041    /// PO line count distribution
3042    #[serde(default)]
3043    pub line_count_distribution: DocumentLineCountDistribution,
3044    /// Payment behavior configuration
3045    #[serde(default)]
3046    pub payment_behavior: P2PPaymentBehaviorConfig,
3047    /// Rate of over-deliveries (quantity received exceeds PO quantity)
3048    #[serde(default)]
3049    pub over_delivery_rate: Option<f64>,
3050    /// Rate of early payment discounts being taken
3051    #[serde(default)]
3052    pub early_payment_discount_rate: Option<f64>,
3053}
3054
3055fn default_three_way_match_rate() -> f64 {
3056    0.95
3057}
3058
3059fn default_partial_delivery_rate() -> f64 {
3060    0.15
3061}
3062
3063fn default_price_variance_rate() -> f64 {
3064    0.08
3065}
3066
3067fn default_max_price_variance() -> f64 {
3068    0.05
3069}
3070
3071fn default_quantity_variance_rate() -> f64 {
3072    0.05
3073}
3074
3075fn default_po_to_gr_days() -> u32 {
3076    14
3077}
3078
3079fn default_gr_to_invoice_days() -> u32 {
3080    5
3081}
3082
3083fn default_invoice_to_payment_days() -> u32 {
3084    30
3085}
3086
3087impl Default for P2PFlowConfig {
3088    fn default() -> Self {
3089        Self {
3090            enabled: true,
3091            three_way_match_rate: default_three_way_match_rate(),
3092            partial_delivery_rate: default_partial_delivery_rate(),
3093            price_variance_rate: default_price_variance_rate(),
3094            max_price_variance_percent: default_max_price_variance(),
3095            quantity_variance_rate: default_quantity_variance_rate(),
3096            average_po_to_gr_days: default_po_to_gr_days(),
3097            average_gr_to_invoice_days: default_gr_to_invoice_days(),
3098            average_invoice_to_payment_days: default_invoice_to_payment_days(),
3099            line_count_distribution: DocumentLineCountDistribution::default(),
3100            payment_behavior: P2PPaymentBehaviorConfig::default(),
3101            over_delivery_rate: None,
3102            early_payment_discount_rate: None,
3103        }
3104    }
3105}
3106
3107// ============================================================================
3108// P2P Payment Behavior Configuration
3109// ============================================================================
3110
3111/// P2P payment behavior configuration.
3112#[derive(Debug, Clone, Serialize, Deserialize)]
3113pub struct P2PPaymentBehaviorConfig {
3114    /// Rate of late payments (beyond due date)
3115    #[serde(default = "default_p2p_late_payment_rate")]
3116    pub late_payment_rate: f64,
3117    /// Distribution of late payment days
3118    #[serde(default)]
3119    pub late_payment_days_distribution: LatePaymentDaysDistribution,
3120    /// Rate of partial payments
3121    #[serde(default = "default_p2p_partial_payment_rate")]
3122    pub partial_payment_rate: f64,
3123    /// Rate of payment corrections (NSF, chargebacks, reversals)
3124    #[serde(default = "default_p2p_payment_correction_rate")]
3125    pub payment_correction_rate: f64,
3126    /// Average days until partial payment remainder is paid
3127    #[serde(default = "default_p2p_avg_days_until_remainder")]
3128    pub avg_days_until_remainder: u32,
3129}
3130
3131fn default_p2p_late_payment_rate() -> f64 {
3132    0.15
3133}
3134
3135fn default_p2p_partial_payment_rate() -> f64 {
3136    0.05
3137}
3138
3139fn default_p2p_payment_correction_rate() -> f64 {
3140    0.02
3141}
3142
3143fn default_p2p_avg_days_until_remainder() -> u32 {
3144    30
3145}
3146
3147impl Default for P2PPaymentBehaviorConfig {
3148    fn default() -> Self {
3149        Self {
3150            late_payment_rate: default_p2p_late_payment_rate(),
3151            late_payment_days_distribution: LatePaymentDaysDistribution::default(),
3152            partial_payment_rate: default_p2p_partial_payment_rate(),
3153            payment_correction_rate: default_p2p_payment_correction_rate(),
3154            avg_days_until_remainder: default_p2p_avg_days_until_remainder(),
3155        }
3156    }
3157}
3158
3159/// Distribution of late payment days for P2P.
3160#[derive(Debug, Clone, Serialize, Deserialize)]
3161pub struct LatePaymentDaysDistribution {
3162    /// 1-7 days late (slightly late)
3163    #[serde(default = "default_slightly_late")]
3164    pub slightly_late_1_to_7: f64,
3165    /// 8-14 days late
3166    #[serde(default = "default_late_8_14")]
3167    pub late_8_to_14: f64,
3168    /// 15-30 days late (very late)
3169    #[serde(default = "default_very_late")]
3170    pub very_late_15_to_30: f64,
3171    /// 31-60 days late (severely late)
3172    #[serde(default = "default_severely_late")]
3173    pub severely_late_31_to_60: f64,
3174    /// Over 60 days late (extremely late)
3175    #[serde(default = "default_extremely_late")]
3176    pub extremely_late_over_60: f64,
3177}
3178
3179fn default_slightly_late() -> f64 {
3180    0.50
3181}
3182
3183fn default_late_8_14() -> f64 {
3184    0.25
3185}
3186
3187fn default_very_late() -> f64 {
3188    0.15
3189}
3190
3191fn default_severely_late() -> f64 {
3192    0.07
3193}
3194
3195fn default_extremely_late() -> f64 {
3196    0.03
3197}
3198
3199impl Default for LatePaymentDaysDistribution {
3200    fn default() -> Self {
3201        Self {
3202            slightly_late_1_to_7: default_slightly_late(),
3203            late_8_to_14: default_late_8_14(),
3204            very_late_15_to_30: default_very_late(),
3205            severely_late_31_to_60: default_severely_late(),
3206            extremely_late_over_60: default_extremely_late(),
3207        }
3208    }
3209}
3210
3211/// O2C (Order-to-Cash) flow configuration.
3212#[derive(Debug, Clone, Serialize, Deserialize)]
3213pub struct O2CFlowConfig {
3214    /// Enable O2C document flow generation
3215    #[serde(default = "default_true")]
3216    pub enabled: bool,
3217    /// Credit check failure rate
3218    #[serde(default = "default_credit_check_failure_rate")]
3219    pub credit_check_failure_rate: f64,
3220    /// Rate of partial shipments
3221    #[serde(default = "default_partial_shipment_rate")]
3222    pub partial_shipment_rate: f64,
3223    /// Rate of returns
3224    #[serde(default = "default_return_rate")]
3225    pub return_rate: f64,
3226    /// Bad debt write-off rate
3227    #[serde(default = "default_bad_debt_rate")]
3228    pub bad_debt_rate: f64,
3229    /// Average days from SO to delivery
3230    #[serde(default = "default_so_to_delivery_days")]
3231    pub average_so_to_delivery_days: u32,
3232    /// Average days from delivery to invoice
3233    #[serde(default = "default_delivery_to_invoice_days")]
3234    pub average_delivery_to_invoice_days: u32,
3235    /// Average days from invoice to receipt
3236    #[serde(default = "default_invoice_to_receipt_days")]
3237    pub average_invoice_to_receipt_days: u32,
3238    /// SO line count distribution
3239    #[serde(default)]
3240    pub line_count_distribution: DocumentLineCountDistribution,
3241    /// Cash discount configuration
3242    #[serde(default)]
3243    pub cash_discount: CashDiscountConfig,
3244    /// Payment behavior configuration
3245    #[serde(default)]
3246    pub payment_behavior: O2CPaymentBehaviorConfig,
3247    /// Rate of late payments
3248    #[serde(default)]
3249    pub late_payment_rate: Option<f64>,
3250}
3251
3252fn default_credit_check_failure_rate() -> f64 {
3253    0.02
3254}
3255
3256fn default_partial_shipment_rate() -> f64 {
3257    0.10
3258}
3259
3260fn default_return_rate() -> f64 {
3261    0.03
3262}
3263
3264fn default_bad_debt_rate() -> f64 {
3265    0.01
3266}
3267
3268fn default_so_to_delivery_days() -> u32 {
3269    7
3270}
3271
3272fn default_delivery_to_invoice_days() -> u32 {
3273    1
3274}
3275
3276fn default_invoice_to_receipt_days() -> u32 {
3277    45
3278}
3279
3280impl Default for O2CFlowConfig {
3281    fn default() -> Self {
3282        Self {
3283            enabled: true,
3284            credit_check_failure_rate: default_credit_check_failure_rate(),
3285            partial_shipment_rate: default_partial_shipment_rate(),
3286            return_rate: default_return_rate(),
3287            bad_debt_rate: default_bad_debt_rate(),
3288            average_so_to_delivery_days: default_so_to_delivery_days(),
3289            average_delivery_to_invoice_days: default_delivery_to_invoice_days(),
3290            average_invoice_to_receipt_days: default_invoice_to_receipt_days(),
3291            line_count_distribution: DocumentLineCountDistribution::default(),
3292            cash_discount: CashDiscountConfig::default(),
3293            payment_behavior: O2CPaymentBehaviorConfig::default(),
3294            late_payment_rate: None,
3295        }
3296    }
3297}
3298
3299// ============================================================================
3300// O2C Payment Behavior Configuration
3301// ============================================================================
3302
3303/// O2C payment behavior configuration.
3304#[derive(Debug, Clone, Serialize, Deserialize, Default)]
3305pub struct O2CPaymentBehaviorConfig {
3306    /// Dunning (Mahnung) configuration
3307    #[serde(default)]
3308    pub dunning: DunningConfig,
3309    /// Partial payment configuration
3310    #[serde(default)]
3311    pub partial_payments: PartialPaymentConfig,
3312    /// Short payment configuration (unauthorized deductions)
3313    #[serde(default)]
3314    pub short_payments: ShortPaymentConfig,
3315    /// On-account payment configuration (unapplied payments)
3316    #[serde(default)]
3317    pub on_account_payments: OnAccountPaymentConfig,
3318    /// Payment correction configuration (NSF, chargebacks)
3319    #[serde(default)]
3320    pub payment_corrections: PaymentCorrectionConfig,
3321}
3322
3323/// Dunning (Mahnungen) configuration for AR collections.
3324#[derive(Debug, Clone, Serialize, Deserialize)]
3325pub struct DunningConfig {
3326    /// Enable dunning process
3327    #[serde(default)]
3328    pub enabled: bool,
3329    /// Days overdue for level 1 dunning (1st reminder)
3330    #[serde(default = "default_dunning_level_1_days")]
3331    pub level_1_days_overdue: u32,
3332    /// Days overdue for level 2 dunning (2nd reminder)
3333    #[serde(default = "default_dunning_level_2_days")]
3334    pub level_2_days_overdue: u32,
3335    /// Days overdue for level 3 dunning (final notice)
3336    #[serde(default = "default_dunning_level_3_days")]
3337    pub level_3_days_overdue: u32,
3338    /// Days overdue for collection handover
3339    #[serde(default = "default_collection_days")]
3340    pub collection_days_overdue: u32,
3341    /// Payment rates after each dunning level
3342    #[serde(default)]
3343    pub payment_after_dunning_rates: DunningPaymentRates,
3344    /// Rate of invoices blocked from dunning (disputes)
3345    #[serde(default = "default_dunning_block_rate")]
3346    pub dunning_block_rate: f64,
3347    /// Interest rate per year for overdue amounts
3348    #[serde(default = "default_dunning_interest_rate")]
3349    pub interest_rate_per_year: f64,
3350    /// Fixed dunning charge per letter
3351    #[serde(default = "default_dunning_charge")]
3352    pub dunning_charge: f64,
3353}
3354
3355fn default_dunning_level_1_days() -> u32 {
3356    14
3357}
3358
3359fn default_dunning_level_2_days() -> u32 {
3360    28
3361}
3362
3363fn default_dunning_level_3_days() -> u32 {
3364    42
3365}
3366
3367fn default_collection_days() -> u32 {
3368    60
3369}
3370
3371fn default_dunning_block_rate() -> f64 {
3372    0.05
3373}
3374
3375fn default_dunning_interest_rate() -> f64 {
3376    0.09
3377}
3378
3379fn default_dunning_charge() -> f64 {
3380    25.0
3381}
3382
3383impl Default for DunningConfig {
3384    fn default() -> Self {
3385        Self {
3386            enabled: false,
3387            level_1_days_overdue: default_dunning_level_1_days(),
3388            level_2_days_overdue: default_dunning_level_2_days(),
3389            level_3_days_overdue: default_dunning_level_3_days(),
3390            collection_days_overdue: default_collection_days(),
3391            payment_after_dunning_rates: DunningPaymentRates::default(),
3392            dunning_block_rate: default_dunning_block_rate(),
3393            interest_rate_per_year: default_dunning_interest_rate(),
3394            dunning_charge: default_dunning_charge(),
3395        }
3396    }
3397}
3398
3399/// Payment rates after each dunning level.
3400#[derive(Debug, Clone, Serialize, Deserialize)]
3401pub struct DunningPaymentRates {
3402    /// Rate that pays after level 1 reminder
3403    #[serde(default = "default_after_level_1")]
3404    pub after_level_1: f64,
3405    /// Rate that pays after level 2 reminder
3406    #[serde(default = "default_after_level_2")]
3407    pub after_level_2: f64,
3408    /// Rate that pays after level 3 final notice
3409    #[serde(default = "default_after_level_3")]
3410    pub after_level_3: f64,
3411    /// Rate that pays during collection
3412    #[serde(default = "default_during_collection")]
3413    pub during_collection: f64,
3414    /// Rate that never pays (becomes bad debt)
3415    #[serde(default = "default_never_pay")]
3416    pub never_pay: f64,
3417}
3418
3419fn default_after_level_1() -> f64 {
3420    0.40
3421}
3422
3423fn default_after_level_2() -> f64 {
3424    0.30
3425}
3426
3427fn default_after_level_3() -> f64 {
3428    0.15
3429}
3430
3431fn default_during_collection() -> f64 {
3432    0.05
3433}
3434
3435fn default_never_pay() -> f64 {
3436    0.10
3437}
3438
3439impl Default for DunningPaymentRates {
3440    fn default() -> Self {
3441        Self {
3442            after_level_1: default_after_level_1(),
3443            after_level_2: default_after_level_2(),
3444            after_level_3: default_after_level_3(),
3445            during_collection: default_during_collection(),
3446            never_pay: default_never_pay(),
3447        }
3448    }
3449}
3450
3451/// Partial payment configuration.
3452#[derive(Debug, Clone, Serialize, Deserialize)]
3453pub struct PartialPaymentConfig {
3454    /// Rate of invoices paid partially
3455    #[serde(default = "default_partial_payment_rate")]
3456    pub rate: f64,
3457    /// Distribution of partial payment percentages
3458    #[serde(default)]
3459    pub percentage_distribution: PartialPaymentPercentageDistribution,
3460    /// Average days until remainder is paid
3461    #[serde(default = "default_avg_days_until_remainder")]
3462    pub avg_days_until_remainder: u32,
3463}
3464
3465fn default_partial_payment_rate() -> f64 {
3466    0.08
3467}
3468
3469fn default_avg_days_until_remainder() -> u32 {
3470    30
3471}
3472
3473impl Default for PartialPaymentConfig {
3474    fn default() -> Self {
3475        Self {
3476            rate: default_partial_payment_rate(),
3477            percentage_distribution: PartialPaymentPercentageDistribution::default(),
3478            avg_days_until_remainder: default_avg_days_until_remainder(),
3479        }
3480    }
3481}
3482
3483/// Distribution of partial payment percentages.
3484#[derive(Debug, Clone, Serialize, Deserialize)]
3485pub struct PartialPaymentPercentageDistribution {
3486    /// Pay 25% of invoice
3487    #[serde(default = "default_partial_25")]
3488    pub pay_25_percent: f64,
3489    /// Pay 50% of invoice
3490    #[serde(default = "default_partial_50")]
3491    pub pay_50_percent: f64,
3492    /// Pay 75% of invoice
3493    #[serde(default = "default_partial_75")]
3494    pub pay_75_percent: f64,
3495    /// Pay random percentage
3496    #[serde(default = "default_partial_random")]
3497    pub pay_random_percent: f64,
3498}
3499
3500fn default_partial_25() -> f64 {
3501    0.15
3502}
3503
3504fn default_partial_50() -> f64 {
3505    0.50
3506}
3507
3508fn default_partial_75() -> f64 {
3509    0.25
3510}
3511
3512fn default_partial_random() -> f64 {
3513    0.10
3514}
3515
3516impl Default for PartialPaymentPercentageDistribution {
3517    fn default() -> Self {
3518        Self {
3519            pay_25_percent: default_partial_25(),
3520            pay_50_percent: default_partial_50(),
3521            pay_75_percent: default_partial_75(),
3522            pay_random_percent: default_partial_random(),
3523        }
3524    }
3525}
3526
3527/// Short payment configuration (unauthorized deductions).
3528#[derive(Debug, Clone, Serialize, Deserialize)]
3529pub struct ShortPaymentConfig {
3530    /// Rate of payments that are short
3531    #[serde(default = "default_short_payment_rate")]
3532    pub rate: f64,
3533    /// Distribution of short payment reasons
3534    #[serde(default)]
3535    pub reason_distribution: ShortPaymentReasonDistribution,
3536    /// Maximum percentage that can be short
3537    #[serde(default = "default_max_short_percent")]
3538    pub max_short_percent: f64,
3539}
3540
3541fn default_short_payment_rate() -> f64 {
3542    0.03
3543}
3544
3545fn default_max_short_percent() -> f64 {
3546    0.10
3547}
3548
3549impl Default for ShortPaymentConfig {
3550    fn default() -> Self {
3551        Self {
3552            rate: default_short_payment_rate(),
3553            reason_distribution: ShortPaymentReasonDistribution::default(),
3554            max_short_percent: default_max_short_percent(),
3555        }
3556    }
3557}
3558
3559/// Distribution of short payment reasons.
3560#[derive(Debug, Clone, Serialize, Deserialize)]
3561pub struct ShortPaymentReasonDistribution {
3562    /// Pricing dispute
3563    #[serde(default = "default_pricing_dispute")]
3564    pub pricing_dispute: f64,
3565    /// Quality issue
3566    #[serde(default = "default_quality_issue")]
3567    pub quality_issue: f64,
3568    /// Quantity discrepancy
3569    #[serde(default = "default_quantity_discrepancy")]
3570    pub quantity_discrepancy: f64,
3571    /// Unauthorized deduction
3572    #[serde(default = "default_unauthorized_deduction")]
3573    pub unauthorized_deduction: f64,
3574    /// Early payment discount taken incorrectly
3575    #[serde(default = "default_incorrect_discount")]
3576    pub incorrect_discount: f64,
3577}
3578
3579fn default_pricing_dispute() -> f64 {
3580    0.30
3581}
3582
3583fn default_quality_issue() -> f64 {
3584    0.20
3585}
3586
3587fn default_quantity_discrepancy() -> f64 {
3588    0.20
3589}
3590
3591fn default_unauthorized_deduction() -> f64 {
3592    0.15
3593}
3594
3595fn default_incorrect_discount() -> f64 {
3596    0.15
3597}
3598
3599impl Default for ShortPaymentReasonDistribution {
3600    fn default() -> Self {
3601        Self {
3602            pricing_dispute: default_pricing_dispute(),
3603            quality_issue: default_quality_issue(),
3604            quantity_discrepancy: default_quantity_discrepancy(),
3605            unauthorized_deduction: default_unauthorized_deduction(),
3606            incorrect_discount: default_incorrect_discount(),
3607        }
3608    }
3609}
3610
3611/// On-account payment configuration (unapplied payments).
3612#[derive(Debug, Clone, Serialize, Deserialize)]
3613pub struct OnAccountPaymentConfig {
3614    /// Rate of payments that are on-account (unapplied)
3615    #[serde(default = "default_on_account_rate")]
3616    pub rate: f64,
3617    /// Average days until on-account payments are applied
3618    #[serde(default = "default_avg_days_until_applied")]
3619    pub avg_days_until_applied: u32,
3620}
3621
3622fn default_on_account_rate() -> f64 {
3623    0.02
3624}
3625
3626fn default_avg_days_until_applied() -> u32 {
3627    14
3628}
3629
3630impl Default for OnAccountPaymentConfig {
3631    fn default() -> Self {
3632        Self {
3633            rate: default_on_account_rate(),
3634            avg_days_until_applied: default_avg_days_until_applied(),
3635        }
3636    }
3637}
3638
3639/// Payment correction configuration.
3640#[derive(Debug, Clone, Serialize, Deserialize)]
3641pub struct PaymentCorrectionConfig {
3642    /// Rate of payments requiring correction
3643    #[serde(default = "default_payment_correction_rate")]
3644    pub rate: f64,
3645    /// Distribution of correction types
3646    #[serde(default)]
3647    pub type_distribution: PaymentCorrectionTypeDistribution,
3648}
3649
3650fn default_payment_correction_rate() -> f64 {
3651    0.02
3652}
3653
3654impl Default for PaymentCorrectionConfig {
3655    fn default() -> Self {
3656        Self {
3657            rate: default_payment_correction_rate(),
3658            type_distribution: PaymentCorrectionTypeDistribution::default(),
3659        }
3660    }
3661}
3662
3663/// Distribution of payment correction types.
3664#[derive(Debug, Clone, Serialize, Deserialize)]
3665pub struct PaymentCorrectionTypeDistribution {
3666    /// NSF (Non-sufficient funds) / bounced check
3667    #[serde(default = "default_nsf_rate")]
3668    pub nsf: f64,
3669    /// Chargeback
3670    #[serde(default = "default_chargeback_rate")]
3671    pub chargeback: f64,
3672    /// Wrong amount applied
3673    #[serde(default = "default_wrong_amount_rate")]
3674    pub wrong_amount: f64,
3675    /// Wrong customer applied
3676    #[serde(default = "default_wrong_customer_rate")]
3677    pub wrong_customer: f64,
3678    /// Duplicate payment
3679    #[serde(default = "default_duplicate_payment_rate")]
3680    pub duplicate_payment: f64,
3681}
3682
3683fn default_nsf_rate() -> f64 {
3684    0.30
3685}
3686
3687fn default_chargeback_rate() -> f64 {
3688    0.20
3689}
3690
3691fn default_wrong_amount_rate() -> f64 {
3692    0.20
3693}
3694
3695fn default_wrong_customer_rate() -> f64 {
3696    0.15
3697}
3698
3699fn default_duplicate_payment_rate() -> f64 {
3700    0.15
3701}
3702
3703impl Default for PaymentCorrectionTypeDistribution {
3704    fn default() -> Self {
3705        Self {
3706            nsf: default_nsf_rate(),
3707            chargeback: default_chargeback_rate(),
3708            wrong_amount: default_wrong_amount_rate(),
3709            wrong_customer: default_wrong_customer_rate(),
3710            duplicate_payment: default_duplicate_payment_rate(),
3711        }
3712    }
3713}
3714
3715/// Document line count distribution.
3716#[derive(Debug, Clone, Serialize, Deserialize)]
3717pub struct DocumentLineCountDistribution {
3718    /// Minimum number of lines
3719    #[serde(default = "default_min_lines")]
3720    pub min_lines: u32,
3721    /// Maximum number of lines
3722    #[serde(default = "default_max_lines")]
3723    pub max_lines: u32,
3724    /// Most common line count (mode)
3725    #[serde(default = "default_mode_lines")]
3726    pub mode_lines: u32,
3727}
3728
3729fn default_min_lines() -> u32 {
3730    1
3731}
3732
3733fn default_max_lines() -> u32 {
3734    20
3735}
3736
3737fn default_mode_lines() -> u32 {
3738    3
3739}
3740
3741impl Default for DocumentLineCountDistribution {
3742    fn default() -> Self {
3743        Self {
3744            min_lines: default_min_lines(),
3745            max_lines: default_max_lines(),
3746            mode_lines: default_mode_lines(),
3747        }
3748    }
3749}
3750
3751/// Cash discount configuration.
3752#[derive(Debug, Clone, Serialize, Deserialize)]
3753pub struct CashDiscountConfig {
3754    /// Percentage of invoices eligible for cash discount
3755    #[serde(default = "default_discount_eligible_rate")]
3756    pub eligible_rate: f64,
3757    /// Rate at which customers take the discount
3758    #[serde(default = "default_discount_taken_rate")]
3759    pub taken_rate: f64,
3760    /// Standard discount percentage
3761    #[serde(default = "default_discount_percent")]
3762    pub discount_percent: f64,
3763    /// Days within which discount must be taken
3764    #[serde(default = "default_discount_days")]
3765    pub discount_days: u32,
3766}
3767
3768fn default_discount_eligible_rate() -> f64 {
3769    0.30
3770}
3771
3772fn default_discount_taken_rate() -> f64 {
3773    0.60
3774}
3775
3776fn default_discount_percent() -> f64 {
3777    0.02
3778}
3779
3780fn default_discount_days() -> u32 {
3781    10
3782}
3783
3784impl Default for CashDiscountConfig {
3785    fn default() -> Self {
3786        Self {
3787            eligible_rate: default_discount_eligible_rate(),
3788            taken_rate: default_discount_taken_rate(),
3789            discount_percent: default_discount_percent(),
3790            discount_days: default_discount_days(),
3791        }
3792    }
3793}
3794
3795// ============================================================================
3796// Intercompany Configuration
3797// ============================================================================
3798
3799/// Intercompany transaction configuration.
3800#[derive(Debug, Clone, Serialize, Deserialize)]
3801pub struct IntercompanyConfig {
3802    /// Enable intercompany transaction generation
3803    #[serde(default)]
3804    pub enabled: bool,
3805    /// Rate of transactions that are intercompany
3806    #[serde(default = "default_ic_transaction_rate")]
3807    pub ic_transaction_rate: f64,
3808    /// Transfer pricing method
3809    #[serde(default)]
3810    pub transfer_pricing_method: TransferPricingMethod,
3811    /// Transfer pricing markup percentage (for cost-plus)
3812    #[serde(default = "default_markup_percent")]
3813    pub markup_percent: f64,
3814    /// Generate matched IC pairs (offsetting entries)
3815    #[serde(default = "default_true")]
3816    pub generate_matched_pairs: bool,
3817    /// IC transaction type distribution
3818    #[serde(default)]
3819    pub transaction_type_distribution: ICTransactionTypeDistribution,
3820    /// Generate elimination entries for consolidation
3821    #[serde(default)]
3822    pub generate_eliminations: bool,
3823}
3824
3825fn default_ic_transaction_rate() -> f64 {
3826    0.15
3827}
3828
3829fn default_markup_percent() -> f64 {
3830    0.05
3831}
3832
3833impl Default for IntercompanyConfig {
3834    fn default() -> Self {
3835        Self {
3836            enabled: false,
3837            ic_transaction_rate: default_ic_transaction_rate(),
3838            transfer_pricing_method: TransferPricingMethod::default(),
3839            markup_percent: default_markup_percent(),
3840            generate_matched_pairs: true,
3841            transaction_type_distribution: ICTransactionTypeDistribution::default(),
3842            generate_eliminations: false,
3843        }
3844    }
3845}
3846
3847/// Transfer pricing method.
3848#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize)]
3849#[serde(rename_all = "snake_case")]
3850pub enum TransferPricingMethod {
3851    /// Cost plus a markup
3852    #[default]
3853    CostPlus,
3854    /// Comparable uncontrolled price
3855    ComparableUncontrolled,
3856    /// Resale price method
3857    ResalePrice,
3858    /// Transactional net margin method
3859    TransactionalNetMargin,
3860    /// Profit split method
3861    ProfitSplit,
3862}
3863
3864/// IC transaction type distribution.
3865#[derive(Debug, Clone, Serialize, Deserialize)]
3866pub struct ICTransactionTypeDistribution {
3867    /// Goods sales between entities
3868    pub goods_sale: f64,
3869    /// Services provided
3870    pub service_provided: f64,
3871    /// Intercompany loans
3872    pub loan: f64,
3873    /// Dividends
3874    pub dividend: f64,
3875    /// Management fees
3876    pub management_fee: f64,
3877    /// Royalties
3878    pub royalty: f64,
3879    /// Cost sharing
3880    pub cost_sharing: f64,
3881}
3882
3883impl Default for ICTransactionTypeDistribution {
3884    fn default() -> Self {
3885        Self {
3886            goods_sale: 0.35,
3887            service_provided: 0.20,
3888            loan: 0.10,
3889            dividend: 0.05,
3890            management_fee: 0.15,
3891            royalty: 0.10,
3892            cost_sharing: 0.05,
3893        }
3894    }
3895}
3896
3897// ============================================================================
3898// Balance Configuration
3899// ============================================================================
3900
3901/// Balance and trial balance configuration.
3902#[derive(Debug, Clone, Serialize, Deserialize)]
3903pub struct BalanceConfig {
3904    /// Generate opening balances
3905    #[serde(default)]
3906    pub generate_opening_balances: bool,
3907    /// Generate trial balances
3908    #[serde(default = "default_true")]
3909    pub generate_trial_balances: bool,
3910    /// Target gross margin (for revenue/COGS coherence)
3911    #[serde(default = "default_gross_margin")]
3912    pub target_gross_margin: f64,
3913    /// Target DSO (Days Sales Outstanding)
3914    #[serde(default = "default_dso")]
3915    pub target_dso_days: u32,
3916    /// Target DPO (Days Payable Outstanding)
3917    #[serde(default = "default_dpo")]
3918    pub target_dpo_days: u32,
3919    /// Target current ratio
3920    #[serde(default = "default_current_ratio")]
3921    pub target_current_ratio: f64,
3922    /// Target debt-to-equity ratio
3923    #[serde(default = "default_debt_equity")]
3924    pub target_debt_to_equity: f64,
3925    /// Validate balance sheet equation (A = L + E)
3926    #[serde(default = "default_true")]
3927    pub validate_balance_equation: bool,
3928    /// Reconcile subledgers to GL control accounts
3929    #[serde(default = "default_true")]
3930    pub reconcile_subledgers: bool,
3931}
3932
3933fn default_gross_margin() -> f64 {
3934    0.35
3935}
3936
3937fn default_dso() -> u32 {
3938    45
3939}
3940
3941fn default_dpo() -> u32 {
3942    30
3943}
3944
3945fn default_current_ratio() -> f64 {
3946    1.5
3947}
3948
3949fn default_debt_equity() -> f64 {
3950    0.5
3951}
3952
3953impl Default for BalanceConfig {
3954    fn default() -> Self {
3955        Self {
3956            generate_opening_balances: false,
3957            generate_trial_balances: true,
3958            target_gross_margin: default_gross_margin(),
3959            target_dso_days: default_dso(),
3960            target_dpo_days: default_dpo(),
3961            target_current_ratio: default_current_ratio(),
3962            target_debt_to_equity: default_debt_equity(),
3963            validate_balance_equation: true,
3964            reconcile_subledgers: true,
3965        }
3966    }
3967}
3968
3969// ==========================================================================
3970// OCPM (Object-Centric Process Mining) Configuration
3971// ==========================================================================
3972
3973/// OCPM (Object-Centric Process Mining) configuration.
3974///
3975/// Controls generation of OCEL 2.0 compatible event logs with
3976/// many-to-many event-to-object relationships.
3977#[derive(Debug, Clone, Serialize, Deserialize)]
3978pub struct OcpmConfig {
3979    /// Enable OCPM event log generation
3980    #[serde(default)]
3981    pub enabled: bool,
3982
3983    /// Generate lifecycle events (Start/Complete pairs vs atomic events)
3984    #[serde(default = "default_true")]
3985    pub generate_lifecycle_events: bool,
3986
3987    /// Include object-to-object relationships in output
3988    #[serde(default = "default_true")]
3989    pub include_object_relationships: bool,
3990
3991    /// Compute and export process variants
3992    #[serde(default = "default_true")]
3993    pub compute_variants: bool,
3994
3995    /// Maximum variants to track (0 = unlimited)
3996    #[serde(default)]
3997    pub max_variants: usize,
3998
3999    /// P2P process configuration
4000    #[serde(default)]
4001    pub p2p_process: OcpmProcessConfig,
4002
4003    /// O2C process configuration
4004    #[serde(default)]
4005    pub o2c_process: OcpmProcessConfig,
4006
4007    /// Output format configuration
4008    #[serde(default)]
4009    pub output: OcpmOutputConfig,
4010}
4011
4012impl Default for OcpmConfig {
4013    fn default() -> Self {
4014        Self {
4015            enabled: false,
4016            generate_lifecycle_events: true,
4017            include_object_relationships: true,
4018            compute_variants: true,
4019            max_variants: 0,
4020            p2p_process: OcpmProcessConfig::default(),
4021            o2c_process: OcpmProcessConfig::default(),
4022            output: OcpmOutputConfig::default(),
4023        }
4024    }
4025}
4026
4027/// Process-specific OCPM configuration.
4028#[derive(Debug, Clone, Serialize, Deserialize)]
4029pub struct OcpmProcessConfig {
4030    /// Rework probability (0.0-1.0)
4031    #[serde(default = "default_rework_probability")]
4032    pub rework_probability: f64,
4033
4034    /// Skip step probability (0.0-1.0)
4035    #[serde(default = "default_skip_probability")]
4036    pub skip_step_probability: f64,
4037
4038    /// Out-of-order step probability (0.0-1.0)
4039    #[serde(default = "default_out_of_order_probability")]
4040    pub out_of_order_probability: f64,
4041}
4042
4043// Defaults deliberately produce variant counts and Inductive-Miner fitness
4044// in the range seen in real ERP data (dozens of variants, ~0.7–0.9 fitness).
4045// Lowering them all to 0 yields a single-variant happy-path log.
4046fn default_rework_probability() -> f64 {
4047    0.15
4048}
4049
4050fn default_skip_probability() -> f64 {
4051    0.10
4052}
4053
4054fn default_out_of_order_probability() -> f64 {
4055    0.08
4056}
4057
4058impl Default for OcpmProcessConfig {
4059    fn default() -> Self {
4060        Self {
4061            rework_probability: default_rework_probability(),
4062            skip_step_probability: default_skip_probability(),
4063            out_of_order_probability: default_out_of_order_probability(),
4064        }
4065    }
4066}
4067
4068/// OCPM output format configuration.
4069#[derive(Debug, Clone, Serialize, Deserialize)]
4070pub struct OcpmOutputConfig {
4071    /// Export OCEL 2.0 JSON format
4072    #[serde(default = "default_true")]
4073    pub ocel_json: bool,
4074
4075    /// Export OCEL 2.0 XML format
4076    #[serde(default)]
4077    pub ocel_xml: bool,
4078
4079    /// Export XES 2.0 XML format (IEEE standard for process mining tools)
4080    #[serde(default)]
4081    pub xes: bool,
4082
4083    /// Include lifecycle transitions in XES output (start/complete pairs)
4084    #[serde(default = "default_true")]
4085    pub xes_include_lifecycle: bool,
4086
4087    /// Include resource attributes in XES output
4088    #[serde(default = "default_true")]
4089    pub xes_include_resources: bool,
4090
4091    /// Export flattened CSV for each object type
4092    #[serde(default = "default_true")]
4093    pub flattened_csv: bool,
4094
4095    /// Export event-object relationship table
4096    #[serde(default = "default_true")]
4097    pub event_object_csv: bool,
4098
4099    /// Export object-object relationship table
4100    #[serde(default = "default_true")]
4101    pub object_relationship_csv: bool,
4102
4103    /// Export process variants summary
4104    #[serde(default = "default_true")]
4105    pub variants_csv: bool,
4106
4107    /// Export reference process models (canonical P2P, O2C, R2R)
4108    #[serde(default)]
4109    pub export_reference_models: bool,
4110}
4111
4112impl Default for OcpmOutputConfig {
4113    fn default() -> Self {
4114        Self {
4115            ocel_json: true,
4116            ocel_xml: false,
4117            xes: false,
4118            xes_include_lifecycle: true,
4119            xes_include_resources: true,
4120            flattened_csv: true,
4121            event_object_csv: true,
4122            object_relationship_csv: true,
4123            variants_csv: true,
4124            export_reference_models: false,
4125        }
4126    }
4127}
4128
4129/// Audit engagement and workpaper generation configuration.
4130#[derive(Debug, Clone, Serialize, Deserialize)]
4131pub struct AuditGenerationConfig {
4132    /// Enable audit engagement generation
4133    #[serde(default)]
4134    pub enabled: bool,
4135
4136    /// Gate for workpaper generation (v3.3.2+).
4137    /// When `false`, workpapers and dependent evidence are skipped
4138    /// while engagements / risk assessments / findings still generate.
4139    #[serde(default = "default_true")]
4140    pub generate_workpapers: bool,
4141
4142    /// Engagement type distribution (v3.3.2+). Drives per-engagement
4143    /// type draw via `AuditEngagementGenerator::draw_engagement_type`.
4144    #[serde(default)]
4145    pub engagement_types: AuditEngagementTypesConfig,
4146
4147    /// Workpaper configuration (v3.3.2+). `average_per_phase` maps onto
4148    /// `WorkpaperGenerator.workpapers_per_section` as a ±50% band
4149    /// around the average. Sampling / ISA / cross-reference flags are
4150    /// surfaced for downstream formatting overlays.
4151    #[serde(default)]
4152    pub workpapers: WorkpaperConfig,
4153
4154    /// Audit team configuration (v3.3.2+). `min_team_size` /
4155    /// `max_team_size` map directly onto
4156    /// `AuditEngagementGenerator.team_size_range`.
4157    /// `specialist_probability` is reserved for v3.4 (explicit
4158    /// specialist-role support).
4159    #[serde(default)]
4160    pub team: AuditTeamConfig,
4161
4162    /// Review workflow configuration (v3.3.2+).
4163    /// `average_review_delay_days` drives both
4164    /// `first_review_delay_range` and `second_review_delay_range` as
4165    /// a ±1-day band around the average. `rework_probability` and
4166    /// `require_partner_signoff` are reserved for v3.4 workflow
4167    /// modeling.
4168    #[serde(default)]
4169    pub review: ReviewWorkflowConfig,
4170
4171    /// FSM-driven audit generation configuration.
4172    #[serde(default)]
4173    pub fsm: Option<AuditFsmConfig>,
4174
4175    /// v3.3.0: IT general controls (access logs, change management
4176    /// records) emitted alongside audit engagements. Requires both
4177    /// `audit.enabled = true` and `audit.it_controls.enabled = true`
4178    /// to take effect — the latter defaults to `false` so current
4179    /// archives are byte-identical to v3.2.1.
4180    #[serde(default)]
4181    pub it_controls: ItControlsConfig,
4182}
4183
4184/// IT general controls config (v3.3.0+).
4185#[derive(Debug, Clone, Serialize, Deserialize)]
4186pub struct ItControlsConfig {
4187    /// Master switch — when `false`, no access logs or change records
4188    /// are generated.
4189    #[serde(default)]
4190    pub enabled: bool,
4191    /// Number of access-log entries per engagement (approximate — the
4192    /// generator may round or scale based on company size).
4193    #[serde(default = "default_access_log_count")]
4194    pub access_logs_per_engagement: usize,
4195    /// Number of change-management records per engagement.
4196    #[serde(default = "default_change_record_count")]
4197    pub change_records_per_engagement: usize,
4198}
4199
4200fn default_access_log_count() -> usize {
4201    500
4202}
4203fn default_change_record_count() -> usize {
4204    50
4205}
4206
4207impl Default for ItControlsConfig {
4208    fn default() -> Self {
4209        Self {
4210            enabled: false,
4211            access_logs_per_engagement: default_access_log_count(),
4212            change_records_per_engagement: default_change_record_count(),
4213        }
4214    }
4215}
4216
4217impl Default for AuditGenerationConfig {
4218    fn default() -> Self {
4219        Self {
4220            enabled: false,
4221            generate_workpapers: true,
4222            engagement_types: AuditEngagementTypesConfig::default(),
4223            workpapers: WorkpaperConfig::default(),
4224            team: AuditTeamConfig::default(),
4225            review: ReviewWorkflowConfig::default(),
4226            fsm: None,
4227            it_controls: ItControlsConfig::default(),
4228        }
4229    }
4230}
4231
4232/// FSM-driven audit generation configuration.
4233#[derive(Debug, Clone, Serialize, Deserialize)]
4234pub struct AuditFsmConfig {
4235    /// Enable FSM-driven audit generation.
4236    #[serde(default)]
4237    pub enabled: bool,
4238
4239    /// Blueprint source: "builtin:fsa", "builtin:ia", or a file path.
4240    #[serde(default = "default_audit_fsm_blueprint")]
4241    pub blueprint: String,
4242
4243    /// Overlay source: "builtin:default", "builtin:thorough", "builtin:rushed", or a file path.
4244    #[serde(default = "default_audit_fsm_overlay")]
4245    pub overlay: String,
4246
4247    /// Depth level override.
4248    #[serde(default)]
4249    pub depth: Option<String>,
4250
4251    /// Discriminator filter.
4252    #[serde(default)]
4253    pub discriminators: std::collections::HashMap<String, Vec<String>>,
4254
4255    /// Event trail output config.
4256    #[serde(default)]
4257    pub event_trail: AuditEventTrailConfig,
4258
4259    /// RNG seed override.
4260    #[serde(default)]
4261    pub seed: Option<u64>,
4262}
4263
4264impl Default for AuditFsmConfig {
4265    fn default() -> Self {
4266        Self {
4267            enabled: false,
4268            blueprint: default_audit_fsm_blueprint(),
4269            overlay: default_audit_fsm_overlay(),
4270            depth: None,
4271            discriminators: std::collections::HashMap::new(),
4272            event_trail: AuditEventTrailConfig::default(),
4273            seed: None,
4274        }
4275    }
4276}
4277
4278fn default_audit_fsm_blueprint() -> String {
4279    "builtin:fsa".to_string()
4280}
4281
4282fn default_audit_fsm_overlay() -> String {
4283    "builtin:default".to_string()
4284}
4285
4286/// Event trail output configuration for FSM-driven audit generation.
4287#[derive(Debug, Clone, Serialize, Deserialize)]
4288pub struct AuditEventTrailConfig {
4289    /// Emit a flat event log.
4290    #[serde(default = "default_true")]
4291    pub flat_log: bool,
4292    /// Project events to OCEL 2.0 format.
4293    #[serde(default)]
4294    pub ocel_projection: bool,
4295}
4296
4297impl Default for AuditEventTrailConfig {
4298    fn default() -> Self {
4299        Self {
4300            flat_log: true,
4301            ocel_projection: false,
4302        }
4303    }
4304}
4305
4306/// Engagement type distribution configuration.
4307#[derive(Debug, Clone, Serialize, Deserialize)]
4308pub struct AuditEngagementTypesConfig {
4309    /// Financial statement audit probability
4310    #[serde(default = "default_financial_audit_prob")]
4311    pub financial_statement: f64,
4312    /// SOX/ICFR audit probability
4313    #[serde(default = "default_sox_audit_prob")]
4314    pub sox_icfr: f64,
4315    /// Integrated audit probability
4316    #[serde(default = "default_integrated_audit_prob")]
4317    pub integrated: f64,
4318    /// Review engagement probability
4319    #[serde(default = "default_review_prob")]
4320    pub review: f64,
4321    /// Agreed-upon procedures probability
4322    #[serde(default = "default_aup_prob")]
4323    pub agreed_upon_procedures: f64,
4324}
4325
4326fn default_financial_audit_prob() -> f64 {
4327    0.40
4328}
4329fn default_sox_audit_prob() -> f64 {
4330    0.20
4331}
4332fn default_integrated_audit_prob() -> f64 {
4333    0.25
4334}
4335fn default_review_prob() -> f64 {
4336    0.10
4337}
4338fn default_aup_prob() -> f64 {
4339    0.05
4340}
4341
4342impl Default for AuditEngagementTypesConfig {
4343    fn default() -> Self {
4344        Self {
4345            financial_statement: default_financial_audit_prob(),
4346            sox_icfr: default_sox_audit_prob(),
4347            integrated: default_integrated_audit_prob(),
4348            review: default_review_prob(),
4349            agreed_upon_procedures: default_aup_prob(),
4350        }
4351    }
4352}
4353
4354/// Workpaper generation configuration.
4355#[derive(Debug, Clone, Serialize, Deserialize)]
4356pub struct WorkpaperConfig {
4357    /// Average workpapers per engagement phase
4358    #[serde(default = "default_workpapers_per_phase")]
4359    pub average_per_phase: usize,
4360
4361    /// Include ISA compliance references
4362    #[serde(default = "default_true")]
4363    pub include_isa_references: bool,
4364
4365    /// Generate sample details
4366    #[serde(default = "default_true")]
4367    pub include_sample_details: bool,
4368
4369    /// Include cross-references between workpapers
4370    #[serde(default = "default_true")]
4371    pub include_cross_references: bool,
4372
4373    /// Sampling configuration
4374    #[serde(default)]
4375    pub sampling: SamplingConfig,
4376}
4377
4378fn default_workpapers_per_phase() -> usize {
4379    5
4380}
4381
4382impl Default for WorkpaperConfig {
4383    fn default() -> Self {
4384        Self {
4385            average_per_phase: default_workpapers_per_phase(),
4386            include_isa_references: true,
4387            include_sample_details: true,
4388            include_cross_references: true,
4389            sampling: SamplingConfig::default(),
4390        }
4391    }
4392}
4393
4394/// Sampling method configuration.
4395#[derive(Debug, Clone, Serialize, Deserialize)]
4396pub struct SamplingConfig {
4397    /// Statistical sampling rate (0.0-1.0)
4398    #[serde(default = "default_statistical_rate")]
4399    pub statistical_rate: f64,
4400    /// Judgmental sampling rate (0.0-1.0)
4401    #[serde(default = "default_judgmental_rate")]
4402    pub judgmental_rate: f64,
4403    /// Haphazard sampling rate (0.0-1.0)
4404    #[serde(default = "default_haphazard_rate")]
4405    pub haphazard_rate: f64,
4406    /// 100% examination rate (0.0-1.0)
4407    #[serde(default = "default_complete_examination_rate")]
4408    pub complete_examination_rate: f64,
4409}
4410
4411fn default_statistical_rate() -> f64 {
4412    0.40
4413}
4414fn default_judgmental_rate() -> f64 {
4415    0.30
4416}
4417fn default_haphazard_rate() -> f64 {
4418    0.20
4419}
4420fn default_complete_examination_rate() -> f64 {
4421    0.10
4422}
4423
4424impl Default for SamplingConfig {
4425    fn default() -> Self {
4426        Self {
4427            statistical_rate: default_statistical_rate(),
4428            judgmental_rate: default_judgmental_rate(),
4429            haphazard_rate: default_haphazard_rate(),
4430            complete_examination_rate: default_complete_examination_rate(),
4431        }
4432    }
4433}
4434
4435/// Audit team configuration.
4436#[derive(Debug, Clone, Serialize, Deserialize)]
4437pub struct AuditTeamConfig {
4438    /// Minimum team size
4439    #[serde(default = "default_min_team_size")]
4440    pub min_team_size: usize,
4441    /// Maximum team size
4442    #[serde(default = "default_max_team_size")]
4443    pub max_team_size: usize,
4444    /// Probability of having a specialist on the team
4445    #[serde(default = "default_specialist_probability")]
4446    pub specialist_probability: f64,
4447}
4448
4449fn default_min_team_size() -> usize {
4450    3
4451}
4452fn default_max_team_size() -> usize {
4453    8
4454}
4455fn default_specialist_probability() -> f64 {
4456    0.30
4457}
4458
4459impl Default for AuditTeamConfig {
4460    fn default() -> Self {
4461        Self {
4462            min_team_size: default_min_team_size(),
4463            max_team_size: default_max_team_size(),
4464            specialist_probability: default_specialist_probability(),
4465        }
4466    }
4467}
4468
4469/// Review workflow configuration.
4470#[derive(Debug, Clone, Serialize, Deserialize)]
4471pub struct ReviewWorkflowConfig {
4472    /// Average days between preparer completion and first review
4473    #[serde(default = "default_review_delay_days")]
4474    pub average_review_delay_days: u32,
4475    /// Probability of review notes requiring rework
4476    #[serde(default = "default_rework_probability_review")]
4477    pub rework_probability: f64,
4478    /// Require partner sign-off for all workpapers
4479    #[serde(default = "default_true")]
4480    pub require_partner_signoff: bool,
4481}
4482
4483fn default_review_delay_days() -> u32 {
4484    2
4485}
4486fn default_rework_probability_review() -> f64 {
4487    0.15
4488}
4489
4490impl Default for ReviewWorkflowConfig {
4491    fn default() -> Self {
4492        Self {
4493            average_review_delay_days: default_review_delay_days(),
4494            rework_probability: default_rework_probability_review(),
4495            require_partner_signoff: true,
4496        }
4497    }
4498}
4499
4500// =============================================================================
4501// Data Quality Configuration
4502// =============================================================================
4503
4504/// Data quality variation settings for realistic flakiness injection.
4505#[derive(Debug, Clone, Serialize, Deserialize)]
4506pub struct DataQualitySchemaConfig {
4507    /// Enable data quality variations
4508    #[serde(default)]
4509    pub enabled: bool,
4510    /// Preset to use (overrides individual settings if set)
4511    #[serde(default)]
4512    pub preset: DataQualityPreset,
4513    /// Missing value injection settings
4514    #[serde(default)]
4515    pub missing_values: MissingValuesSchemaConfig,
4516    /// Typo injection settings
4517    #[serde(default)]
4518    pub typos: TypoSchemaConfig,
4519    /// Format variation settings
4520    #[serde(default)]
4521    pub format_variations: FormatVariationSchemaConfig,
4522    /// Duplicate injection settings
4523    #[serde(default)]
4524    pub duplicates: DuplicateSchemaConfig,
4525    /// Encoding issue settings
4526    #[serde(default)]
4527    pub encoding_issues: EncodingIssueSchemaConfig,
4528    /// Generate quality issue labels for ML training
4529    #[serde(default)]
4530    pub generate_labels: bool,
4531    /// Per-sink quality profiles (different settings for CSV vs JSON etc.)
4532    #[serde(default)]
4533    pub sink_profiles: SinkQualityProfiles,
4534}
4535
4536impl Default for DataQualitySchemaConfig {
4537    fn default() -> Self {
4538        Self {
4539            enabled: false,
4540            preset: DataQualityPreset::None,
4541            missing_values: MissingValuesSchemaConfig::default(),
4542            typos: TypoSchemaConfig::default(),
4543            format_variations: FormatVariationSchemaConfig::default(),
4544            duplicates: DuplicateSchemaConfig::default(),
4545            encoding_issues: EncodingIssueSchemaConfig::default(),
4546            generate_labels: true,
4547            sink_profiles: SinkQualityProfiles::default(),
4548        }
4549    }
4550}
4551
4552impl DataQualitySchemaConfig {
4553    /// Creates a config for a specific preset profile.
4554    pub fn with_preset(preset: DataQualityPreset) -> Self {
4555        let mut config = Self {
4556            preset,
4557            ..Default::default()
4558        };
4559        config.apply_preset();
4560        config
4561    }
4562
4563    /// Applies the preset settings to the individual configuration fields.
4564    /// Call this after deserializing if preset is not Custom or None.
4565    pub fn apply_preset(&mut self) {
4566        if !self.preset.overrides_settings() {
4567            return;
4568        }
4569
4570        self.enabled = true;
4571
4572        // Missing values
4573        self.missing_values.enabled = self.preset.missing_rate() > 0.0;
4574        self.missing_values.rate = self.preset.missing_rate();
4575
4576        // Typos
4577        self.typos.enabled = self.preset.typo_rate() > 0.0;
4578        self.typos.char_error_rate = self.preset.typo_rate();
4579
4580        // Duplicates
4581        self.duplicates.enabled = self.preset.duplicate_rate() > 0.0;
4582        self.duplicates.exact_duplicate_ratio = self.preset.duplicate_rate() * 0.4;
4583        self.duplicates.near_duplicate_ratio = self.preset.duplicate_rate() * 0.4;
4584        self.duplicates.fuzzy_duplicate_ratio = self.preset.duplicate_rate() * 0.2;
4585
4586        // Format variations
4587        self.format_variations.enabled = self.preset.format_variations_enabled();
4588
4589        // Encoding issues
4590        self.encoding_issues.enabled = self.preset.encoding_issues_enabled();
4591        self.encoding_issues.rate = self.preset.encoding_issue_rate();
4592
4593        // OCR errors for typos in legacy preset
4594        if self.preset.ocr_errors_enabled() {
4595            self.typos.type_weights.ocr_errors = 0.3;
4596        }
4597    }
4598
4599    /// Returns the effective missing value rate (considering preset).
4600    pub fn effective_missing_rate(&self) -> f64 {
4601        if self.preset.overrides_settings() {
4602            self.preset.missing_rate()
4603        } else {
4604            self.missing_values.rate
4605        }
4606    }
4607
4608    /// Returns the effective typo rate (considering preset).
4609    pub fn effective_typo_rate(&self) -> f64 {
4610        if self.preset.overrides_settings() {
4611            self.preset.typo_rate()
4612        } else {
4613            self.typos.char_error_rate
4614        }
4615    }
4616
4617    /// Returns the effective duplicate rate (considering preset).
4618    pub fn effective_duplicate_rate(&self) -> f64 {
4619        if self.preset.overrides_settings() {
4620            self.preset.duplicate_rate()
4621        } else {
4622            self.duplicates.exact_duplicate_ratio
4623                + self.duplicates.near_duplicate_ratio
4624                + self.duplicates.fuzzy_duplicate_ratio
4625        }
4626    }
4627
4628    /// Creates a clean profile config.
4629    pub fn clean() -> Self {
4630        Self::with_preset(DataQualityPreset::Clean)
4631    }
4632
4633    /// Creates a noisy profile config.
4634    pub fn noisy() -> Self {
4635        Self::with_preset(DataQualityPreset::Noisy)
4636    }
4637
4638    /// Creates a legacy profile config.
4639    pub fn legacy() -> Self {
4640        Self::with_preset(DataQualityPreset::Legacy)
4641    }
4642}
4643
4644/// Preset configurations for common data quality scenarios.
4645#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
4646#[serde(rename_all = "snake_case")]
4647pub enum DataQualityPreset {
4648    /// No data quality variations (clean data)
4649    #[default]
4650    None,
4651    /// Minimal variations (very clean data with rare issues)
4652    Minimal,
4653    /// Normal variations (realistic enterprise data quality)
4654    Normal,
4655    /// High variations (messy data for stress testing)
4656    High,
4657    /// Custom (use individual settings)
4658    Custom,
4659
4660    // ========================================
4661    // ML-Oriented Profiles (Phase 2.1)
4662    // ========================================
4663    /// Clean profile for ML training - minimal data quality issues
4664    /// Missing: 0.1%, Typos: 0.05%, Duplicates: 0%, Format: None
4665    Clean,
4666    /// Noisy profile simulating typical production data issues
4667    /// Missing: 5%, Typos: 2%, Duplicates: 1%, Format: Medium
4668    Noisy,
4669    /// Legacy profile simulating migrated/OCR'd historical data
4670    /// Missing: 10%, Typos: 5%, Duplicates: 3%, Format: Heavy + OCR
4671    Legacy,
4672}
4673
4674impl DataQualityPreset {
4675    /// Returns the missing value rate for this preset.
4676    pub fn missing_rate(&self) -> f64 {
4677        match self {
4678            DataQualityPreset::None => 0.0,
4679            DataQualityPreset::Minimal => 0.005,
4680            DataQualityPreset::Normal => 0.02,
4681            DataQualityPreset::High => 0.08,
4682            DataQualityPreset::Custom => 0.01, // Use config value
4683            DataQualityPreset::Clean => 0.001,
4684            DataQualityPreset::Noisy => 0.05,
4685            DataQualityPreset::Legacy => 0.10,
4686        }
4687    }
4688
4689    /// Returns the typo rate for this preset.
4690    pub fn typo_rate(&self) -> f64 {
4691        match self {
4692            DataQualityPreset::None => 0.0,
4693            DataQualityPreset::Minimal => 0.0005,
4694            DataQualityPreset::Normal => 0.002,
4695            DataQualityPreset::High => 0.01,
4696            DataQualityPreset::Custom => 0.001, // Use config value
4697            DataQualityPreset::Clean => 0.0005,
4698            DataQualityPreset::Noisy => 0.02,
4699            DataQualityPreset::Legacy => 0.05,
4700        }
4701    }
4702
4703    /// Returns the duplicate rate for this preset.
4704    pub fn duplicate_rate(&self) -> f64 {
4705        match self {
4706            DataQualityPreset::None => 0.0,
4707            DataQualityPreset::Minimal => 0.001,
4708            DataQualityPreset::Normal => 0.005,
4709            DataQualityPreset::High => 0.02,
4710            DataQualityPreset::Custom => 0.0, // Use config value
4711            DataQualityPreset::Clean => 0.0,
4712            DataQualityPreset::Noisy => 0.01,
4713            DataQualityPreset::Legacy => 0.03,
4714        }
4715    }
4716
4717    /// Returns whether format variations are enabled for this preset.
4718    pub fn format_variations_enabled(&self) -> bool {
4719        match self {
4720            DataQualityPreset::None | DataQualityPreset::Clean => false,
4721            DataQualityPreset::Minimal => true,
4722            DataQualityPreset::Normal => true,
4723            DataQualityPreset::High => true,
4724            DataQualityPreset::Custom => true,
4725            DataQualityPreset::Noisy => true,
4726            DataQualityPreset::Legacy => true,
4727        }
4728    }
4729
4730    /// Returns whether OCR-style errors are enabled for this preset.
4731    pub fn ocr_errors_enabled(&self) -> bool {
4732        matches!(self, DataQualityPreset::Legacy | DataQualityPreset::High)
4733    }
4734
4735    /// Returns whether encoding issues are enabled for this preset.
4736    pub fn encoding_issues_enabled(&self) -> bool {
4737        matches!(
4738            self,
4739            DataQualityPreset::Legacy | DataQualityPreset::High | DataQualityPreset::Noisy
4740        )
4741    }
4742
4743    /// Returns the encoding issue rate for this preset.
4744    pub fn encoding_issue_rate(&self) -> f64 {
4745        match self {
4746            DataQualityPreset::None | DataQualityPreset::Clean | DataQualityPreset::Minimal => 0.0,
4747            DataQualityPreset::Normal => 0.002,
4748            DataQualityPreset::High => 0.01,
4749            DataQualityPreset::Custom => 0.0,
4750            DataQualityPreset::Noisy => 0.005,
4751            DataQualityPreset::Legacy => 0.02,
4752        }
4753    }
4754
4755    /// Returns true if this preset overrides individual settings.
4756    pub fn overrides_settings(&self) -> bool {
4757        !matches!(self, DataQualityPreset::Custom | DataQualityPreset::None)
4758    }
4759
4760    /// Returns a human-readable description of this preset.
4761    pub fn description(&self) -> &'static str {
4762        match self {
4763            DataQualityPreset::None => "No data quality issues (pristine data)",
4764            DataQualityPreset::Minimal => "Very rare data quality issues",
4765            DataQualityPreset::Normal => "Realistic enterprise data quality",
4766            DataQualityPreset::High => "Messy data for stress testing",
4767            DataQualityPreset::Custom => "Custom settings from configuration",
4768            DataQualityPreset::Clean => "ML-ready clean data with minimal issues",
4769            DataQualityPreset::Noisy => "Typical production data with moderate issues",
4770            DataQualityPreset::Legacy => "Legacy/migrated data with heavy issues and OCR errors",
4771        }
4772    }
4773}
4774
4775/// Missing value injection configuration.
4776#[derive(Debug, Clone, Serialize, Deserialize)]
4777pub struct MissingValuesSchemaConfig {
4778    /// Enable missing value injection
4779    #[serde(default)]
4780    pub enabled: bool,
4781    /// Global missing rate (0.0 to 1.0)
4782    #[serde(default = "default_missing_rate")]
4783    pub rate: f64,
4784    /// Missing value strategy
4785    #[serde(default)]
4786    pub strategy: MissingValueStrategy,
4787    /// Field-specific rates (field name -> rate)
4788    #[serde(default)]
4789    pub field_rates: std::collections::HashMap<String, f64>,
4790    /// Fields that should never have missing values
4791    #[serde(default)]
4792    pub protected_fields: Vec<String>,
4793}
4794
4795fn default_missing_rate() -> f64 {
4796    0.01
4797}
4798
4799impl Default for MissingValuesSchemaConfig {
4800    fn default() -> Self {
4801        Self {
4802            enabled: false,
4803            rate: default_missing_rate(),
4804            strategy: MissingValueStrategy::Mcar,
4805            field_rates: std::collections::HashMap::new(),
4806            protected_fields: vec![
4807                "document_id".to_string(),
4808                "company_code".to_string(),
4809                "posting_date".to_string(),
4810            ],
4811        }
4812    }
4813}
4814
4815/// Missing value strategy types.
4816#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
4817#[serde(rename_all = "snake_case")]
4818pub enum MissingValueStrategy {
4819    /// Missing Completely At Random - equal probability for all values
4820    #[default]
4821    Mcar,
4822    /// Missing At Random - depends on other observed values
4823    Mar,
4824    /// Missing Not At Random - depends on the value itself
4825    Mnar,
4826    /// Systematic - entire field groups missing together
4827    Systematic,
4828}
4829
4830/// Typo injection configuration.
4831#[derive(Debug, Clone, Serialize, Deserialize)]
4832pub struct TypoSchemaConfig {
4833    /// Enable typo injection
4834    #[serde(default)]
4835    pub enabled: bool,
4836    /// Character error rate (per character, not per field)
4837    #[serde(default = "default_typo_rate")]
4838    pub char_error_rate: f64,
4839    /// Typo type weights
4840    #[serde(default)]
4841    pub type_weights: TypoTypeWeights,
4842    /// Fields that should never have typos
4843    #[serde(default)]
4844    pub protected_fields: Vec<String>,
4845}
4846
4847fn default_typo_rate() -> f64 {
4848    0.001
4849}
4850
4851impl Default for TypoSchemaConfig {
4852    fn default() -> Self {
4853        Self {
4854            enabled: false,
4855            char_error_rate: default_typo_rate(),
4856            type_weights: TypoTypeWeights::default(),
4857            protected_fields: vec![
4858                "document_id".to_string(),
4859                "gl_account".to_string(),
4860                "company_code".to_string(),
4861            ],
4862        }
4863    }
4864}
4865
4866/// Weights for different typo types.
4867#[derive(Debug, Clone, Serialize, Deserialize)]
4868pub struct TypoTypeWeights {
4869    /// Keyboard-adjacent substitution (e.g., 'a' -> 's')
4870    #[serde(default = "default_substitution_weight")]
4871    pub substitution: f64,
4872    /// Adjacent character transposition (e.g., 'ab' -> 'ba')
4873    #[serde(default = "default_transposition_weight")]
4874    pub transposition: f64,
4875    /// Character insertion
4876    #[serde(default = "default_insertion_weight")]
4877    pub insertion: f64,
4878    /// Character deletion
4879    #[serde(default = "default_deletion_weight")]
4880    pub deletion: f64,
4881    /// OCR-style errors (e.g., '0' -> 'O')
4882    #[serde(default = "default_ocr_weight")]
4883    pub ocr_errors: f64,
4884    /// Homophone substitution (e.g., 'their' -> 'there')
4885    #[serde(default = "default_homophone_weight")]
4886    pub homophones: f64,
4887}
4888
4889fn default_substitution_weight() -> f64 {
4890    0.35
4891}
4892fn default_transposition_weight() -> f64 {
4893    0.25
4894}
4895fn default_insertion_weight() -> f64 {
4896    0.10
4897}
4898fn default_deletion_weight() -> f64 {
4899    0.15
4900}
4901fn default_ocr_weight() -> f64 {
4902    0.10
4903}
4904fn default_homophone_weight() -> f64 {
4905    0.05
4906}
4907
4908impl Default for TypoTypeWeights {
4909    fn default() -> Self {
4910        Self {
4911            substitution: default_substitution_weight(),
4912            transposition: default_transposition_weight(),
4913            insertion: default_insertion_weight(),
4914            deletion: default_deletion_weight(),
4915            ocr_errors: default_ocr_weight(),
4916            homophones: default_homophone_weight(),
4917        }
4918    }
4919}
4920
4921/// Format variation configuration.
4922#[derive(Debug, Clone, Serialize, Deserialize, Default)]
4923pub struct FormatVariationSchemaConfig {
4924    /// Enable format variations
4925    #[serde(default)]
4926    pub enabled: bool,
4927    /// Date format variation settings
4928    #[serde(default)]
4929    pub dates: DateFormatVariationConfig,
4930    /// Amount format variation settings
4931    #[serde(default)]
4932    pub amounts: AmountFormatVariationConfig,
4933    /// Identifier format variation settings
4934    #[serde(default)]
4935    pub identifiers: IdentifierFormatVariationConfig,
4936}
4937
4938/// Date format variation configuration.
4939#[derive(Debug, Clone, Serialize, Deserialize)]
4940pub struct DateFormatVariationConfig {
4941    /// Enable date format variations
4942    #[serde(default)]
4943    pub enabled: bool,
4944    /// Overall variation rate
4945    #[serde(default = "default_date_variation_rate")]
4946    pub rate: f64,
4947    /// Include ISO format (2024-01-15)
4948    #[serde(default = "default_true")]
4949    pub iso_format: bool,
4950    /// Include US format (01/15/2024)
4951    #[serde(default)]
4952    pub us_format: bool,
4953    /// Include EU format (15.01.2024)
4954    #[serde(default)]
4955    pub eu_format: bool,
4956    /// Include long format (January 15, 2024)
4957    #[serde(default)]
4958    pub long_format: bool,
4959}
4960
4961fn default_date_variation_rate() -> f64 {
4962    0.05
4963}
4964
4965impl Default for DateFormatVariationConfig {
4966    fn default() -> Self {
4967        Self {
4968            enabled: false,
4969            rate: default_date_variation_rate(),
4970            iso_format: true,
4971            us_format: false,
4972            eu_format: false,
4973            long_format: false,
4974        }
4975    }
4976}
4977
4978/// Amount format variation configuration.
4979#[derive(Debug, Clone, Serialize, Deserialize)]
4980pub struct AmountFormatVariationConfig {
4981    /// Enable amount format variations
4982    #[serde(default)]
4983    pub enabled: bool,
4984    /// Overall variation rate
4985    #[serde(default = "default_amount_variation_rate")]
4986    pub rate: f64,
4987    /// Include US comma format (1,234.56)
4988    #[serde(default)]
4989    pub us_comma_format: bool,
4990    /// Include EU format (1.234,56)
4991    #[serde(default)]
4992    pub eu_format: bool,
4993    /// Include currency prefix ($1,234.56)
4994    #[serde(default)]
4995    pub currency_prefix: bool,
4996    /// Include accounting format with parentheses for negatives
4997    #[serde(default)]
4998    pub accounting_format: bool,
4999}
5000
5001fn default_amount_variation_rate() -> f64 {
5002    0.02
5003}
5004
5005impl Default for AmountFormatVariationConfig {
5006    fn default() -> Self {
5007        Self {
5008            enabled: false,
5009            rate: default_amount_variation_rate(),
5010            us_comma_format: false,
5011            eu_format: false,
5012            currency_prefix: false,
5013            accounting_format: false,
5014        }
5015    }
5016}
5017
5018/// Identifier format variation configuration.
5019#[derive(Debug, Clone, Serialize, Deserialize)]
5020pub struct IdentifierFormatVariationConfig {
5021    /// Enable identifier format variations
5022    #[serde(default)]
5023    pub enabled: bool,
5024    /// Overall variation rate
5025    #[serde(default = "default_identifier_variation_rate")]
5026    pub rate: f64,
5027    /// Case variations (uppercase, lowercase, mixed)
5028    #[serde(default)]
5029    pub case_variations: bool,
5030    /// Padding variations (leading zeros)
5031    #[serde(default)]
5032    pub padding_variations: bool,
5033    /// Separator variations (dash vs underscore)
5034    #[serde(default)]
5035    pub separator_variations: bool,
5036}
5037
5038fn default_identifier_variation_rate() -> f64 {
5039    0.02
5040}
5041
5042impl Default for IdentifierFormatVariationConfig {
5043    fn default() -> Self {
5044        Self {
5045            enabled: false,
5046            rate: default_identifier_variation_rate(),
5047            case_variations: false,
5048            padding_variations: false,
5049            separator_variations: false,
5050        }
5051    }
5052}
5053
5054/// Duplicate injection configuration.
5055#[derive(Debug, Clone, Serialize, Deserialize)]
5056pub struct DuplicateSchemaConfig {
5057    /// Enable duplicate injection
5058    #[serde(default)]
5059    pub enabled: bool,
5060    /// Overall duplicate rate
5061    #[serde(default = "default_duplicate_rate")]
5062    pub rate: f64,
5063    /// Exact duplicate proportion (out of duplicates)
5064    #[serde(default = "default_exact_duplicate_ratio")]
5065    pub exact_duplicate_ratio: f64,
5066    /// Near duplicate proportion (slight variations)
5067    #[serde(default = "default_near_duplicate_ratio")]
5068    pub near_duplicate_ratio: f64,
5069    /// Fuzzy duplicate proportion (typos in key fields)
5070    #[serde(default = "default_fuzzy_duplicate_ratio")]
5071    pub fuzzy_duplicate_ratio: f64,
5072    /// Maximum date offset for near/fuzzy duplicates (days)
5073    #[serde(default = "default_max_date_offset")]
5074    pub max_date_offset_days: u32,
5075    /// Maximum amount variance for near duplicates (fraction)
5076    #[serde(default = "default_max_amount_variance")]
5077    pub max_amount_variance: f64,
5078}
5079
5080fn default_duplicate_rate() -> f64 {
5081    0.005
5082}
5083fn default_exact_duplicate_ratio() -> f64 {
5084    0.4
5085}
5086fn default_near_duplicate_ratio() -> f64 {
5087    0.35
5088}
5089fn default_fuzzy_duplicate_ratio() -> f64 {
5090    0.25
5091}
5092fn default_max_date_offset() -> u32 {
5093    3
5094}
5095fn default_max_amount_variance() -> f64 {
5096    0.01
5097}
5098
5099impl Default for DuplicateSchemaConfig {
5100    fn default() -> Self {
5101        Self {
5102            enabled: false,
5103            rate: default_duplicate_rate(),
5104            exact_duplicate_ratio: default_exact_duplicate_ratio(),
5105            near_duplicate_ratio: default_near_duplicate_ratio(),
5106            fuzzy_duplicate_ratio: default_fuzzy_duplicate_ratio(),
5107            max_date_offset_days: default_max_date_offset(),
5108            max_amount_variance: default_max_amount_variance(),
5109        }
5110    }
5111}
5112
5113/// Encoding issue configuration.
5114#[derive(Debug, Clone, Serialize, Deserialize)]
5115pub struct EncodingIssueSchemaConfig {
5116    /// Enable encoding issue injection
5117    #[serde(default)]
5118    pub enabled: bool,
5119    /// Overall encoding issue rate
5120    #[serde(default = "default_encoding_rate")]
5121    pub rate: f64,
5122    /// Include mojibake (UTF-8/Latin-1 confusion)
5123    #[serde(default)]
5124    pub mojibake: bool,
5125    /// Include HTML entity corruption
5126    #[serde(default)]
5127    pub html_entities: bool,
5128    /// Include BOM issues
5129    #[serde(default)]
5130    pub bom_issues: bool,
5131}
5132
5133fn default_encoding_rate() -> f64 {
5134    0.001
5135}
5136
5137impl Default for EncodingIssueSchemaConfig {
5138    fn default() -> Self {
5139        Self {
5140            enabled: false,
5141            rate: default_encoding_rate(),
5142            mojibake: false,
5143            html_entities: false,
5144            bom_issues: false,
5145        }
5146    }
5147}
5148
5149/// Per-sink quality profiles for different output formats.
5150#[derive(Debug, Clone, Serialize, Deserialize, Default)]
5151pub struct SinkQualityProfiles {
5152    /// CSV-specific quality settings
5153    #[serde(default)]
5154    pub csv: Option<SinkQualityOverride>,
5155    /// JSON-specific quality settings
5156    #[serde(default)]
5157    pub json: Option<SinkQualityOverride>,
5158    /// Parquet-specific quality settings
5159    #[serde(default)]
5160    pub parquet: Option<SinkQualityOverride>,
5161}
5162
5163/// Quality setting overrides for a specific sink type.
5164#[derive(Debug, Clone, Serialize, Deserialize)]
5165pub struct SinkQualityOverride {
5166    /// Override enabled state
5167    pub enabled: Option<bool>,
5168    /// Override missing value rate
5169    pub missing_rate: Option<f64>,
5170    /// Override typo rate
5171    pub typo_rate: Option<f64>,
5172    /// Override format variation rate
5173    pub format_variation_rate: Option<f64>,
5174    /// Override duplicate rate
5175    pub duplicate_rate: Option<f64>,
5176}
5177
5178// =============================================================================
5179// Accounting Standards Configuration
5180// =============================================================================
5181
5182/// Accounting standards framework configuration for generating standards-compliant data.
5183///
5184/// Supports US GAAP, IFRS, and French GAAP (PCG) frameworks with specific standards:
5185/// - ASC 606/IFRS 15/PCG: Revenue Recognition
5186/// - ASC 842/IFRS 16/PCG: Leases
5187/// - ASC 820/IFRS 13/PCG: Fair Value Measurement
5188/// - ASC 360/IAS 36/PCG: Impairment
5189#[derive(Debug, Clone, Serialize, Deserialize, Default)]
5190pub struct AccountingStandardsConfig {
5191    /// Enable accounting standards generation
5192    #[serde(default)]
5193    pub enabled: bool,
5194
5195    /// Accounting framework to use.
5196    /// When `None`, the country pack's `accounting.framework` is used as fallback;
5197    /// if that is also absent the orchestrator defaults to US GAAP.
5198    #[serde(default, skip_serializing_if = "Option::is_none")]
5199    pub framework: Option<AccountingFrameworkConfig>,
5200
5201    /// Revenue recognition configuration (ASC 606/IFRS 15)
5202    #[serde(default)]
5203    pub revenue_recognition: RevenueRecognitionConfig,
5204
5205    /// Lease accounting configuration (ASC 842/IFRS 16)
5206    #[serde(default)]
5207    pub leases: LeaseAccountingConfig,
5208
5209    /// Fair value measurement configuration (ASC 820/IFRS 13)
5210    #[serde(default)]
5211    pub fair_value: FairValueConfig,
5212
5213    /// Impairment testing configuration (ASC 360/IAS 36)
5214    #[serde(default)]
5215    pub impairment: ImpairmentConfig,
5216
5217    /// Business combination configuration (IFRS 3 / ASC 805)
5218    #[serde(default)]
5219    pub business_combinations: BusinessCombinationsConfig,
5220
5221    /// Expected Credit Loss configuration (IFRS 9 / ASC 326)
5222    #[serde(default)]
5223    pub expected_credit_loss: EclConfig,
5224
5225    /// Generate framework differences for dual reporting
5226    #[serde(default)]
5227    pub generate_differences: bool,
5228}
5229
5230/// Accounting framework selection.
5231#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
5232#[serde(rename_all = "snake_case")]
5233pub enum AccountingFrameworkConfig {
5234    /// US Generally Accepted Accounting Principles
5235    #[default]
5236    UsGaap,
5237    /// International Financial Reporting Standards
5238    Ifrs,
5239    /// Generate data for both frameworks with reconciliation
5240    DualReporting,
5241    /// French GAAP (Plan Comptable Général – PCG)
5242    FrenchGaap,
5243    /// German GAAP (Handelsgesetzbuch – HGB, §238-263)
5244    GermanGaap,
5245}
5246
5247/// Revenue recognition configuration (ASC 606/IFRS 15).
5248#[derive(Debug, Clone, Serialize, Deserialize)]
5249pub struct RevenueRecognitionConfig {
5250    /// Enable revenue recognition generation
5251    #[serde(default)]
5252    pub enabled: bool,
5253
5254    /// Generate customer contracts
5255    #[serde(default = "default_true")]
5256    pub generate_contracts: bool,
5257
5258    /// Average number of performance obligations per contract
5259    #[serde(default = "default_avg_obligations")]
5260    pub avg_obligations_per_contract: f64,
5261
5262    /// Rate of contracts with variable consideration
5263    #[serde(default = "default_variable_consideration_rate")]
5264    pub variable_consideration_rate: f64,
5265
5266    /// Rate of over-time revenue recognition (vs point-in-time)
5267    #[serde(default = "default_over_time_rate")]
5268    pub over_time_recognition_rate: f64,
5269
5270    /// Number of contracts to generate
5271    #[serde(default = "default_contract_count")]
5272    pub contract_count: usize,
5273}
5274
5275fn default_avg_obligations() -> f64 {
5276    2.0
5277}
5278
5279fn default_variable_consideration_rate() -> f64 {
5280    0.15
5281}
5282
5283fn default_over_time_rate() -> f64 {
5284    0.30
5285}
5286
5287fn default_contract_count() -> usize {
5288    100
5289}
5290
5291impl Default for RevenueRecognitionConfig {
5292    fn default() -> Self {
5293        Self {
5294            enabled: false,
5295            generate_contracts: true,
5296            avg_obligations_per_contract: default_avg_obligations(),
5297            variable_consideration_rate: default_variable_consideration_rate(),
5298            over_time_recognition_rate: default_over_time_rate(),
5299            contract_count: default_contract_count(),
5300        }
5301    }
5302}
5303
5304/// Lease accounting configuration (ASC 842/IFRS 16).
5305#[derive(Debug, Clone, Serialize, Deserialize)]
5306pub struct LeaseAccountingConfig {
5307    /// Enable lease accounting generation
5308    #[serde(default)]
5309    pub enabled: bool,
5310
5311    /// Number of leases to generate
5312    #[serde(default = "default_lease_count")]
5313    pub lease_count: usize,
5314
5315    /// Percentage of finance leases (vs operating)
5316    #[serde(default = "default_finance_lease_pct")]
5317    pub finance_lease_percent: f64,
5318
5319    /// Average lease term in months
5320    #[serde(default = "default_avg_lease_term")]
5321    pub avg_lease_term_months: u32,
5322
5323    /// Generate amortization schedules
5324    #[serde(default = "default_true")]
5325    pub generate_amortization: bool,
5326
5327    /// Real estate lease percentage
5328    #[serde(default = "default_real_estate_pct")]
5329    pub real_estate_percent: f64,
5330}
5331
5332fn default_lease_count() -> usize {
5333    50
5334}
5335
5336fn default_finance_lease_pct() -> f64 {
5337    0.30
5338}
5339
5340fn default_avg_lease_term() -> u32 {
5341    60
5342}
5343
5344fn default_real_estate_pct() -> f64 {
5345    0.40
5346}
5347
5348impl Default for LeaseAccountingConfig {
5349    fn default() -> Self {
5350        Self {
5351            enabled: false,
5352            lease_count: default_lease_count(),
5353            finance_lease_percent: default_finance_lease_pct(),
5354            avg_lease_term_months: default_avg_lease_term(),
5355            generate_amortization: true,
5356            real_estate_percent: default_real_estate_pct(),
5357        }
5358    }
5359}
5360
5361/// Fair value measurement configuration (ASC 820/IFRS 13).
5362#[derive(Debug, Clone, Serialize, Deserialize)]
5363pub struct FairValueConfig {
5364    /// Enable fair value measurement generation
5365    #[serde(default)]
5366    pub enabled: bool,
5367
5368    /// Number of fair value measurements to generate
5369    #[serde(default = "default_fv_count")]
5370    pub measurement_count: usize,
5371
5372    /// Level 1 (quoted prices) percentage
5373    #[serde(default = "default_level1_pct")]
5374    pub level1_percent: f64,
5375
5376    /// Level 2 (observable inputs) percentage
5377    #[serde(default = "default_level2_pct")]
5378    pub level2_percent: f64,
5379
5380    /// Level 3 (unobservable inputs) percentage
5381    #[serde(default = "default_level3_pct")]
5382    pub level3_percent: f64,
5383
5384    /// Include sensitivity analysis for Level 3
5385    #[serde(default)]
5386    pub include_sensitivity_analysis: bool,
5387}
5388
5389fn default_fv_count() -> usize {
5390    25
5391}
5392
5393fn default_level1_pct() -> f64 {
5394    0.40
5395}
5396
5397fn default_level2_pct() -> f64 {
5398    0.35
5399}
5400
5401fn default_level3_pct() -> f64 {
5402    0.25
5403}
5404
5405impl Default for FairValueConfig {
5406    fn default() -> Self {
5407        Self {
5408            enabled: false,
5409            measurement_count: default_fv_count(),
5410            level1_percent: default_level1_pct(),
5411            level2_percent: default_level2_pct(),
5412            level3_percent: default_level3_pct(),
5413            include_sensitivity_analysis: false,
5414        }
5415    }
5416}
5417
5418/// Impairment testing configuration (ASC 360/IAS 36).
5419#[derive(Debug, Clone, Serialize, Deserialize)]
5420pub struct ImpairmentConfig {
5421    /// Enable impairment testing generation
5422    #[serde(default)]
5423    pub enabled: bool,
5424
5425    /// Number of impairment tests to generate
5426    #[serde(default = "default_impairment_count")]
5427    pub test_count: usize,
5428
5429    /// Rate of tests resulting in impairment
5430    #[serde(default = "default_impairment_rate")]
5431    pub impairment_rate: f64,
5432
5433    /// Generate cash flow projections
5434    #[serde(default = "default_true")]
5435    pub generate_projections: bool,
5436
5437    /// Include goodwill impairment tests
5438    #[serde(default)]
5439    pub include_goodwill: bool,
5440}
5441
5442fn default_impairment_count() -> usize {
5443    15
5444}
5445
5446fn default_impairment_rate() -> f64 {
5447    0.10
5448}
5449
5450impl Default for ImpairmentConfig {
5451    fn default() -> Self {
5452        Self {
5453            enabled: false,
5454            test_count: default_impairment_count(),
5455            impairment_rate: default_impairment_rate(),
5456            generate_projections: true,
5457            include_goodwill: false,
5458        }
5459    }
5460}
5461
5462// =============================================================================
5463// Business Combinations Configuration (IFRS 3 / ASC 805)
5464// =============================================================================
5465
5466/// Configuration for generating business combination (acquisition) data.
5467#[derive(Debug, Clone, Serialize, Deserialize)]
5468pub struct BusinessCombinationsConfig {
5469    /// Enable business combination generation
5470    #[serde(default)]
5471    pub enabled: bool,
5472
5473    /// Number of acquisitions to generate per company (1-5)
5474    #[serde(default = "default_bc_acquisition_count")]
5475    pub acquisition_count: usize,
5476}
5477
5478fn default_bc_acquisition_count() -> usize {
5479    2
5480}
5481
5482impl Default for BusinessCombinationsConfig {
5483    fn default() -> Self {
5484        Self {
5485            enabled: false,
5486            acquisition_count: default_bc_acquisition_count(),
5487        }
5488    }
5489}
5490
5491// =============================================================================
5492// ECL Configuration (IFRS 9 / ASC 326)
5493// =============================================================================
5494
5495/// Configuration for Expected Credit Loss generation.
5496#[derive(Debug, Clone, Serialize, Deserialize)]
5497pub struct EclConfig {
5498    /// Enable ECL generation.
5499    #[serde(default)]
5500    pub enabled: bool,
5501
5502    /// Weight for base economic scenario (0–1).
5503    #[serde(default = "default_ecl_base_weight")]
5504    pub base_scenario_weight: f64,
5505
5506    /// Multiplier for base scenario (typically 1.0).
5507    #[serde(default = "default_ecl_base_multiplier")]
5508    pub base_scenario_multiplier: f64,
5509
5510    /// Weight for optimistic economic scenario (0–1).
5511    #[serde(default = "default_ecl_optimistic_weight")]
5512    pub optimistic_scenario_weight: f64,
5513
5514    /// Multiplier for optimistic scenario (< 1.0 means lower losses).
5515    #[serde(default = "default_ecl_optimistic_multiplier")]
5516    pub optimistic_scenario_multiplier: f64,
5517
5518    /// Weight for pessimistic economic scenario (0–1).
5519    #[serde(default = "default_ecl_pessimistic_weight")]
5520    pub pessimistic_scenario_weight: f64,
5521
5522    /// Multiplier for pessimistic scenario (> 1.0 means higher losses).
5523    #[serde(default = "default_ecl_pessimistic_multiplier")]
5524    pub pessimistic_scenario_multiplier: f64,
5525}
5526
5527fn default_ecl_base_weight() -> f64 {
5528    0.50
5529}
5530fn default_ecl_base_multiplier() -> f64 {
5531    1.0
5532}
5533fn default_ecl_optimistic_weight() -> f64 {
5534    0.30
5535}
5536fn default_ecl_optimistic_multiplier() -> f64 {
5537    0.8
5538}
5539fn default_ecl_pessimistic_weight() -> f64 {
5540    0.20
5541}
5542fn default_ecl_pessimistic_multiplier() -> f64 {
5543    1.4
5544}
5545
5546impl Default for EclConfig {
5547    fn default() -> Self {
5548        Self {
5549            enabled: false,
5550            base_scenario_weight: default_ecl_base_weight(),
5551            base_scenario_multiplier: default_ecl_base_multiplier(),
5552            optimistic_scenario_weight: default_ecl_optimistic_weight(),
5553            optimistic_scenario_multiplier: default_ecl_optimistic_multiplier(),
5554            pessimistic_scenario_weight: default_ecl_pessimistic_weight(),
5555            pessimistic_scenario_multiplier: default_ecl_pessimistic_multiplier(),
5556        }
5557    }
5558}
5559
5560// =============================================================================
5561// Audit Standards Configuration
5562// =============================================================================
5563
5564/// Audit standards framework configuration for generating standards-compliant audit data.
5565///
5566/// Supports ISA (International Standards on Auditing) and PCAOB standards:
5567/// - ISA 200-720: Complete coverage of audit standards
5568/// - ISA 520: Analytical Procedures
5569/// - ISA 505: External Confirmations
5570/// - ISA 700/705/706/701: Audit Reports
5571/// - PCAOB AS 2201: ICFR Auditing
5572#[derive(Debug, Clone, Serialize, Deserialize, Default)]
5573pub struct AuditStandardsConfig {
5574    /// Enable audit standards generation
5575    #[serde(default)]
5576    pub enabled: bool,
5577
5578    /// ISA compliance configuration
5579    #[serde(default)]
5580    pub isa_compliance: IsaComplianceConfig,
5581
5582    /// Analytical procedures configuration (ISA 520)
5583    #[serde(default)]
5584    pub analytical_procedures: AnalyticalProceduresConfig,
5585
5586    /// External confirmations configuration (ISA 505)
5587    #[serde(default)]
5588    pub confirmations: ConfirmationsConfig,
5589
5590    /// Audit opinion configuration (ISA 700/705/706/701)
5591    #[serde(default)]
5592    pub opinion: AuditOpinionConfig,
5593
5594    /// Generate complete audit trail with traceability
5595    #[serde(default)]
5596    pub generate_audit_trail: bool,
5597
5598    /// SOX 302/404 compliance configuration
5599    #[serde(default)]
5600    pub sox: SoxComplianceConfig,
5601
5602    /// PCAOB-specific configuration
5603    #[serde(default)]
5604    pub pcaob: PcaobConfig,
5605}
5606
5607/// ISA compliance level configuration.
5608#[derive(Debug, Clone, Serialize, Deserialize)]
5609pub struct IsaComplianceConfig {
5610    /// Enable ISA compliance tracking
5611    #[serde(default)]
5612    pub enabled: bool,
5613
5614    /// Compliance level: "basic", "standard", "comprehensive"
5615    #[serde(default = "default_compliance_level")]
5616    pub compliance_level: String,
5617
5618    /// Generate ISA requirement mappings
5619    #[serde(default = "default_true")]
5620    pub generate_isa_mappings: bool,
5621
5622    /// Generate ISA coverage summary
5623    #[serde(default = "default_true")]
5624    pub generate_coverage_summary: bool,
5625
5626    /// Include PCAOB standard mappings (for dual framework)
5627    #[serde(default)]
5628    pub include_pcaob: bool,
5629
5630    /// Framework to use: "isa", "pcaob", "dual"
5631    #[serde(default = "default_audit_framework")]
5632    pub framework: String,
5633}
5634
5635fn default_compliance_level() -> String {
5636    "standard".to_string()
5637}
5638
5639fn default_audit_framework() -> String {
5640    "isa".to_string()
5641}
5642
5643impl Default for IsaComplianceConfig {
5644    fn default() -> Self {
5645        Self {
5646            enabled: false,
5647            compliance_level: default_compliance_level(),
5648            generate_isa_mappings: true,
5649            generate_coverage_summary: true,
5650            include_pcaob: false,
5651            framework: default_audit_framework(),
5652        }
5653    }
5654}
5655
5656/// Analytical procedures configuration (ISA 520).
5657#[derive(Debug, Clone, Serialize, Deserialize)]
5658pub struct AnalyticalProceduresConfig {
5659    /// Enable analytical procedures generation
5660    #[serde(default)]
5661    pub enabled: bool,
5662
5663    /// Number of procedures per account/area
5664    #[serde(default = "default_procedures_per_account")]
5665    pub procedures_per_account: usize,
5666
5667    /// Probability of variance exceeding threshold
5668    #[serde(default = "default_variance_probability")]
5669    pub variance_probability: f64,
5670
5671    /// Include variance investigations
5672    #[serde(default = "default_true")]
5673    pub generate_investigations: bool,
5674
5675    /// Include financial ratio analysis
5676    #[serde(default = "default_true")]
5677    pub include_ratio_analysis: bool,
5678}
5679
5680fn default_procedures_per_account() -> usize {
5681    3
5682}
5683
5684fn default_variance_probability() -> f64 {
5685    0.20
5686}
5687
5688impl Default for AnalyticalProceduresConfig {
5689    fn default() -> Self {
5690        Self {
5691            enabled: false,
5692            procedures_per_account: default_procedures_per_account(),
5693            variance_probability: default_variance_probability(),
5694            generate_investigations: true,
5695            include_ratio_analysis: true,
5696        }
5697    }
5698}
5699
5700/// External confirmations configuration (ISA 505).
5701#[derive(Debug, Clone, Serialize, Deserialize)]
5702pub struct ConfirmationsConfig {
5703    /// Enable confirmation generation
5704    #[serde(default)]
5705    pub enabled: bool,
5706
5707    /// Number of confirmations to generate
5708    #[serde(default = "default_confirmation_count")]
5709    pub confirmation_count: usize,
5710
5711    /// Positive response rate
5712    #[serde(default = "default_positive_response_rate")]
5713    pub positive_response_rate: f64,
5714
5715    /// Exception rate (responses with differences)
5716    #[serde(default = "default_exception_rate_confirm")]
5717    pub exception_rate: f64,
5718
5719    /// Non-response rate
5720    #[serde(default = "default_non_response_rate")]
5721    pub non_response_rate: f64,
5722
5723    /// Generate alternative procedures for non-responses
5724    #[serde(default = "default_true")]
5725    pub generate_alternative_procedures: bool,
5726}
5727
5728fn default_confirmation_count() -> usize {
5729    50
5730}
5731
5732fn default_positive_response_rate() -> f64 {
5733    0.85
5734}
5735
5736fn default_exception_rate_confirm() -> f64 {
5737    0.10
5738}
5739
5740fn default_non_response_rate() -> f64 {
5741    0.05
5742}
5743
5744impl Default for ConfirmationsConfig {
5745    fn default() -> Self {
5746        Self {
5747            enabled: false,
5748            confirmation_count: default_confirmation_count(),
5749            positive_response_rate: default_positive_response_rate(),
5750            exception_rate: default_exception_rate_confirm(),
5751            non_response_rate: default_non_response_rate(),
5752            generate_alternative_procedures: true,
5753        }
5754    }
5755}
5756
5757/// Audit opinion configuration (ISA 700/705/706/701).
5758#[derive(Debug, Clone, Serialize, Deserialize)]
5759pub struct AuditOpinionConfig {
5760    /// Enable audit opinion generation
5761    #[serde(default)]
5762    pub enabled: bool,
5763
5764    /// Generate Key Audit Matters (KAM) / Critical Audit Matters (CAM)
5765    #[serde(default = "default_true")]
5766    pub generate_kam: bool,
5767
5768    /// Average number of KAMs/CAMs per opinion
5769    #[serde(default = "default_kam_count")]
5770    pub average_kam_count: usize,
5771
5772    /// Rate of modified opinions
5773    #[serde(default = "default_modified_opinion_rate")]
5774    pub modified_opinion_rate: f64,
5775
5776    /// Include emphasis of matter paragraphs
5777    #[serde(default)]
5778    pub include_emphasis_of_matter: bool,
5779
5780    /// Include going concern conclusions
5781    #[serde(default = "default_true")]
5782    pub include_going_concern: bool,
5783}
5784
5785fn default_kam_count() -> usize {
5786    3
5787}
5788
5789fn default_modified_opinion_rate() -> f64 {
5790    0.05
5791}
5792
5793impl Default for AuditOpinionConfig {
5794    fn default() -> Self {
5795        Self {
5796            enabled: false,
5797            generate_kam: true,
5798            average_kam_count: default_kam_count(),
5799            modified_opinion_rate: default_modified_opinion_rate(),
5800            include_emphasis_of_matter: false,
5801            include_going_concern: true,
5802        }
5803    }
5804}
5805
5806/// SOX compliance configuration (Sections 302/404).
5807#[derive(Debug, Clone, Serialize, Deserialize)]
5808pub struct SoxComplianceConfig {
5809    /// Enable SOX compliance generation
5810    #[serde(default)]
5811    pub enabled: bool,
5812
5813    /// Generate Section 302 CEO/CFO certifications
5814    #[serde(default = "default_true")]
5815    pub generate_302_certifications: bool,
5816
5817    /// Generate Section 404 ICFR assessments
5818    #[serde(default = "default_true")]
5819    pub generate_404_assessments: bool,
5820
5821    /// Materiality threshold for SOX testing
5822    #[serde(default = "default_sox_materiality_threshold")]
5823    pub materiality_threshold: f64,
5824
5825    /// Rate of material weaknesses
5826    #[serde(default = "default_material_weakness_rate")]
5827    pub material_weakness_rate: f64,
5828
5829    /// Rate of significant deficiencies
5830    #[serde(default = "default_significant_deficiency_rate")]
5831    pub significant_deficiency_rate: f64,
5832}
5833
5834fn default_material_weakness_rate() -> f64 {
5835    0.02
5836}
5837
5838fn default_significant_deficiency_rate() -> f64 {
5839    0.08
5840}
5841
5842impl Default for SoxComplianceConfig {
5843    fn default() -> Self {
5844        Self {
5845            enabled: false,
5846            generate_302_certifications: true,
5847            generate_404_assessments: true,
5848            materiality_threshold: default_sox_materiality_threshold(),
5849            material_weakness_rate: default_material_weakness_rate(),
5850            significant_deficiency_rate: default_significant_deficiency_rate(),
5851        }
5852    }
5853}
5854
5855/// PCAOB-specific configuration.
5856#[derive(Debug, Clone, Serialize, Deserialize)]
5857pub struct PcaobConfig {
5858    /// Enable PCAOB-specific elements
5859    #[serde(default)]
5860    pub enabled: bool,
5861
5862    /// Treat as PCAOB audit (vs ISA-only)
5863    #[serde(default)]
5864    pub is_pcaob_audit: bool,
5865
5866    /// Generate Critical Audit Matters (CAM)
5867    #[serde(default = "default_true")]
5868    pub generate_cam: bool,
5869
5870    /// Include ICFR opinion (for integrated audits)
5871    #[serde(default)]
5872    pub include_icfr_opinion: bool,
5873
5874    /// Generate PCAOB-ISA standard mappings
5875    #[serde(default)]
5876    pub generate_standard_mappings: bool,
5877}
5878
5879impl Default for PcaobConfig {
5880    fn default() -> Self {
5881        Self {
5882            enabled: false,
5883            is_pcaob_audit: false,
5884            generate_cam: true,
5885            include_icfr_opinion: false,
5886            generate_standard_mappings: false,
5887        }
5888    }
5889}
5890
5891// =============================================================================
5892// Advanced Distribution Configuration
5893// =============================================================================
5894
5895/// Advanced distribution configuration for realistic data generation.
5896///
5897/// This section enables sophisticated distribution models including:
5898/// - Mixture models (multi-modal distributions)
5899/// - Cross-field correlations
5900/// - Conditional distributions
5901/// - Regime changes and economic cycles
5902/// - Statistical validation
5903#[derive(Debug, Clone, Serialize, Deserialize, Default)]
5904pub struct AdvancedDistributionConfig {
5905    /// Enable advanced distribution features.
5906    #[serde(default)]
5907    pub enabled: bool,
5908
5909    /// Mixture model configuration for amounts.
5910    #[serde(default)]
5911    pub amounts: MixtureDistributionSchemaConfig,
5912
5913    /// Cross-field correlation configuration.
5914    #[serde(default)]
5915    pub correlations: CorrelationSchemaConfig,
5916
5917    /// Conditional distribution configurations.
5918    #[serde(default)]
5919    pub conditional: Vec<ConditionalDistributionSchemaConfig>,
5920
5921    /// Regime change configuration.
5922    #[serde(default)]
5923    pub regime_changes: RegimeChangeSchemaConfig,
5924
5925    /// Industry-specific distribution profile.
5926    #[serde(default)]
5927    pub industry_profile: Option<IndustryProfileType>,
5928
5929    /// Statistical validation configuration.
5930    #[serde(default)]
5931    pub validation: StatisticalValidationSchemaConfig,
5932
5933    /// v3.4.4+ — Pareto heavy-tailed distribution for monetary amounts.
5934    /// When set and `enabled`, overrides `amounts` mixture model for the
5935    /// non-fraud amount-sampling path (fraud patterns remain orthogonal).
5936    /// Useful for capex, strategic contracts, and any domain where a small
5937    /// number of very large values dominates the tail.
5938    #[serde(default)]
5939    pub pareto: Option<ParetoSchemaConfig>,
5940}
5941
5942/// Schema-level Pareto distribution configuration (v3.4.4+).
5943///
5944/// Thin wrapper around `datasynth_core::distributions::ParetoConfig` that
5945/// adds an `enabled` gate and serde-friendly field names.
5946#[derive(Debug, Clone, Serialize, Deserialize)]
5947pub struct ParetoSchemaConfig {
5948    /// Enable Pareto sampling. When true, replaces the `amounts` mixture
5949    /// model for the non-fraud amount-sampling path.
5950    #[serde(default)]
5951    pub enabled: bool,
5952
5953    /// Shape parameter (tail heaviness). Lower values → heavier tail.
5954    /// Typical range: 1.5-3.0. Default: 2.0.
5955    #[serde(default = "default_pareto_alpha")]
5956    pub alpha: f64,
5957
5958    /// Scale / minimum value. All samples are >= x_min.
5959    /// Typical: 1000 (for capex) to 100,000 (for large contracts). Default: 100.
5960    #[serde(default = "default_pareto_x_min")]
5961    pub x_min: f64,
5962
5963    /// Optional upper clamp. `None` = unbounded (recommended for realistic
5964    /// heavy tails).
5965    #[serde(default)]
5966    pub max_value: Option<f64>,
5967
5968    /// Decimal places for rounding. Default: 2.
5969    #[serde(default = "default_pareto_decimal_places")]
5970    pub decimal_places: u8,
5971}
5972
5973fn default_pareto_alpha() -> f64 {
5974    2.0
5975}
5976
5977fn default_pareto_x_min() -> f64 {
5978    100.0
5979}
5980
5981fn default_pareto_decimal_places() -> u8 {
5982    2
5983}
5984
5985impl Default for ParetoSchemaConfig {
5986    fn default() -> Self {
5987        Self {
5988            enabled: false,
5989            alpha: default_pareto_alpha(),
5990            x_min: default_pareto_x_min(),
5991            max_value: None,
5992            decimal_places: default_pareto_decimal_places(),
5993        }
5994    }
5995}
5996
5997impl ParetoSchemaConfig {
5998    /// Convert this schema config into a `datasynth_core::distributions::ParetoConfig`.
5999    pub fn to_core_config(&self) -> datasynth_core::distributions::ParetoConfig {
6000        datasynth_core::distributions::ParetoConfig {
6001            alpha: self.alpha,
6002            x_min: self.x_min,
6003            max_value: self.max_value,
6004            decimal_places: self.decimal_places,
6005        }
6006    }
6007}
6008
6009/// Industry profile types for pre-configured distribution settings.
6010#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
6011#[serde(rename_all = "snake_case")]
6012pub enum IndustryProfileType {
6013    /// Retail industry profile (POS sales, inventory, seasonal)
6014    Retail,
6015    /// Manufacturing industry profile (raw materials, maintenance, capital)
6016    Manufacturing,
6017    /// Financial services profile (wire transfers, ACH, fee income)
6018    FinancialServices,
6019    /// Healthcare profile (claims, procedures, supplies)
6020    Healthcare,
6021    /// Technology profile (subscriptions, services, R&D)
6022    Technology,
6023}
6024
6025/// Mixture model distribution configuration.
6026#[derive(Debug, Clone, Serialize, Deserialize)]
6027pub struct MixtureDistributionSchemaConfig {
6028    /// Enable mixture model for amount generation.
6029    #[serde(default)]
6030    pub enabled: bool,
6031
6032    /// Distribution type: "gaussian" or "lognormal".
6033    #[serde(default = "default_mixture_type")]
6034    pub distribution_type: MixtureDistributionType,
6035
6036    /// Mixture components with weights.
6037    #[serde(default)]
6038    pub components: Vec<MixtureComponentConfig>,
6039
6040    /// Minimum value constraint.
6041    #[serde(default = "default_min_amount")]
6042    pub min_value: f64,
6043
6044    /// Maximum value constraint (optional).
6045    #[serde(default)]
6046    pub max_value: Option<f64>,
6047
6048    /// Decimal places for rounding.
6049    #[serde(default = "default_decimal_places")]
6050    pub decimal_places: u8,
6051}
6052
6053fn default_mixture_type() -> MixtureDistributionType {
6054    MixtureDistributionType::LogNormal
6055}
6056
6057fn default_min_amount() -> f64 {
6058    0.01
6059}
6060
6061fn default_decimal_places() -> u8 {
6062    2
6063}
6064
6065impl Default for MixtureDistributionSchemaConfig {
6066    fn default() -> Self {
6067        Self {
6068            enabled: false,
6069            distribution_type: MixtureDistributionType::LogNormal,
6070            components: Vec::new(),
6071            min_value: 0.01,
6072            max_value: None,
6073            decimal_places: 2,
6074        }
6075    }
6076}
6077
6078impl MixtureDistributionSchemaConfig {
6079    /// Convert this schema-level config into a [`LogNormalMixtureConfig`]
6080    /// suitable for `LogNormalMixtureSampler::new`. Returns `None` if there
6081    /// are no components (schema default is an empty list, which cannot
6082    /// drive a sampler).
6083    ///
6084    /// Callers should gate this with `self.enabled` before invoking.
6085    pub fn to_log_normal_config(
6086        &self,
6087    ) -> Option<datasynth_core::distributions::LogNormalMixtureConfig> {
6088        if self.components.is_empty() {
6089            return None;
6090        }
6091        Some(datasynth_core::distributions::LogNormalMixtureConfig {
6092            components: self
6093                .components
6094                .iter()
6095                .map(|c| match &c.label {
6096                    Some(lbl) => datasynth_core::distributions::LogNormalComponent::with_label(
6097                        c.weight,
6098                        c.mu,
6099                        c.sigma,
6100                        lbl.clone(),
6101                    ),
6102                    None => datasynth_core::distributions::LogNormalComponent::new(
6103                        c.weight, c.mu, c.sigma,
6104                    ),
6105                })
6106                .collect(),
6107            min_value: self.min_value,
6108            max_value: self.max_value,
6109            decimal_places: self.decimal_places,
6110        })
6111    }
6112
6113    /// Convert this schema-level config into a [`GaussianMixtureConfig`].
6114    /// Returns `None` if there are no components.
6115    pub fn to_gaussian_config(
6116        &self,
6117    ) -> Option<datasynth_core::distributions::GaussianMixtureConfig> {
6118        if self.components.is_empty() {
6119            return None;
6120        }
6121        Some(datasynth_core::distributions::GaussianMixtureConfig {
6122            components: self
6123                .components
6124                .iter()
6125                .map(|c| {
6126                    datasynth_core::distributions::GaussianComponent::new(c.weight, c.mu, c.sigma)
6127                })
6128                .collect(),
6129            allow_negative: true,
6130            min_value: Some(self.min_value),
6131            max_value: self.max_value,
6132        })
6133    }
6134}
6135
6136/// Mixture distribution type.
6137#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
6138#[serde(rename_all = "snake_case")]
6139pub enum MixtureDistributionType {
6140    /// Gaussian (normal) mixture
6141    Gaussian,
6142    /// Log-normal mixture (for positive amounts)
6143    #[default]
6144    LogNormal,
6145}
6146
6147/// Configuration for a single mixture component.
6148#[derive(Debug, Clone, Serialize, Deserialize)]
6149pub struct MixtureComponentConfig {
6150    /// Weight of this component (must sum to 1.0 across all components).
6151    pub weight: f64,
6152
6153    /// Location parameter (mean for Gaussian, mu for log-normal).
6154    pub mu: f64,
6155
6156    /// Scale parameter (std dev for Gaussian, sigma for log-normal).
6157    pub sigma: f64,
6158
6159    /// Optional label for this component (e.g., "routine", "significant", "major").
6160    #[serde(default)]
6161    pub label: Option<String>,
6162}
6163
6164/// Cross-field correlation configuration.
6165#[derive(Debug, Clone, Serialize, Deserialize)]
6166pub struct CorrelationSchemaConfig {
6167    /// Enable correlation modeling.
6168    #[serde(default)]
6169    pub enabled: bool,
6170
6171    /// Copula type for dependency modeling.
6172    #[serde(default)]
6173    pub copula_type: CopulaSchemaType,
6174
6175    /// Field definitions for correlation.
6176    #[serde(default)]
6177    pub fields: Vec<CorrelatedFieldConfig>,
6178
6179    /// Correlation matrix (upper triangular, row-major).
6180    /// For n fields, this should have n*(n-1)/2 values.
6181    #[serde(default)]
6182    pub matrix: Vec<f64>,
6183
6184    /// Expected correlations for validation.
6185    #[serde(default)]
6186    pub expected_correlations: Vec<ExpectedCorrelationConfig>,
6187}
6188
6189impl Default for CorrelationSchemaConfig {
6190    fn default() -> Self {
6191        Self {
6192            enabled: false,
6193            copula_type: CopulaSchemaType::Gaussian,
6194            fields: Vec::new(),
6195            matrix: Vec::new(),
6196            expected_correlations: Vec::new(),
6197        }
6198    }
6199}
6200
6201impl CorrelationSchemaConfig {
6202    /// v3.5.4+: extract the correlation for a specific field pair from
6203    /// either the upper-triangular flat matrix (n*(n-1)/2 values) or a
6204    /// full symmetric n×n matrix (n*n values). Returns `None` when the
6205    /// named fields aren't both present or the matrix shape doesn't
6206    /// match.
6207    pub fn correlation_between(&self, field_a: &str, field_b: &str) -> Option<f64> {
6208        let idx_a = self.fields.iter().position(|f| f.name == field_a)?;
6209        let idx_b = self.fields.iter().position(|f| f.name == field_b)?;
6210        if idx_a == idx_b {
6211            return Some(1.0);
6212        }
6213        let (i, j) = if idx_a < idx_b {
6214            (idx_a, idx_b)
6215        } else {
6216            (idx_b, idx_a)
6217        };
6218        let n = self.fields.len();
6219        // Full n×n symmetric matrix?
6220        if self.matrix.len() == n * n {
6221            return self.matrix.get(idx_a * n + idx_b).copied();
6222        }
6223        // Upper triangular flat (row-major, excluding diagonal)?
6224        let expected_tri = n * (n - 1) / 2;
6225        if self.matrix.len() == expected_tri {
6226            // Row i, col j where j > i: flat index is
6227            //   sum_{k=0..i}((n-1-k)) + (j - i - 1)
6228            // = i*(n-1) - i*(i-1)/2 + (j - i - 1)
6229            let flat = i * (n - 1) - i * (i.saturating_sub(1)) / 2 + (j - i - 1);
6230            return self.matrix.get(flat).copied();
6231        }
6232        None
6233    }
6234
6235    /// Convert this schema config to a core `CopulaConfig` when the
6236    /// declared field pair `(field_a, field_b)` has a valid correlation
6237    /// entry. Returns `None` when disabled, fields missing, or matrix
6238    /// malformed.
6239    pub fn to_core_config_for_pair(
6240        &self,
6241        field_a: &str,
6242        field_b: &str,
6243    ) -> Option<datasynth_core::distributions::CopulaConfig> {
6244        if !self.enabled {
6245            return None;
6246        }
6247        let rho = self.correlation_between(field_a, field_b)?;
6248        use datasynth_core::distributions::{CopulaConfig, CopulaType};
6249        let copula_type = match self.copula_type {
6250            CopulaSchemaType::Gaussian => CopulaType::Gaussian,
6251            CopulaSchemaType::Clayton => CopulaType::Clayton,
6252            CopulaSchemaType::Gumbel => CopulaType::Gumbel,
6253            CopulaSchemaType::Frank => CopulaType::Frank,
6254            CopulaSchemaType::StudentT => CopulaType::StudentT,
6255        };
6256        // Gaussian / StudentT interpret theta as correlation; others
6257        // as a shape parameter. Minimal v3.5.4 only wires Gaussian in
6258        // the runtime, but the converter is general so follow-ups can
6259        // light up the other copulas.
6260        let theta = rho.clamp(-0.999, 0.999);
6261        Some(CopulaConfig {
6262            copula_type,
6263            theta,
6264            degrees_of_freedom: 4.0,
6265        })
6266    }
6267}
6268
6269/// Copula type for dependency modeling.
6270#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
6271#[serde(rename_all = "snake_case")]
6272pub enum CopulaSchemaType {
6273    /// Gaussian copula (symmetric, no tail dependence)
6274    #[default]
6275    Gaussian,
6276    /// Clayton copula (lower tail dependence)
6277    Clayton,
6278    /// Gumbel copula (upper tail dependence)
6279    Gumbel,
6280    /// Frank copula (symmetric, no tail dependence)
6281    Frank,
6282    /// Student-t copula (both tail dependencies)
6283    StudentT,
6284}
6285
6286/// Configuration for a correlated field.
6287#[derive(Debug, Clone, Serialize, Deserialize)]
6288pub struct CorrelatedFieldConfig {
6289    /// Field name.
6290    pub name: String,
6291
6292    /// Marginal distribution type.
6293    #[serde(default)]
6294    pub distribution: MarginalDistributionConfig,
6295}
6296
6297/// Marginal distribution configuration.
6298#[derive(Debug, Clone, Serialize, Deserialize)]
6299#[serde(tag = "type", rename_all = "snake_case")]
6300pub enum MarginalDistributionConfig {
6301    /// Normal distribution.
6302    Normal {
6303        /// Mean
6304        mu: f64,
6305        /// Standard deviation
6306        sigma: f64,
6307    },
6308    /// Log-normal distribution.
6309    LogNormal {
6310        /// Location parameter
6311        mu: f64,
6312        /// Scale parameter
6313        sigma: f64,
6314    },
6315    /// Uniform distribution.
6316    Uniform {
6317        /// Minimum value
6318        min: f64,
6319        /// Maximum value
6320        max: f64,
6321    },
6322    /// Discrete uniform distribution.
6323    DiscreteUniform {
6324        /// Minimum integer value
6325        min: i32,
6326        /// Maximum integer value
6327        max: i32,
6328    },
6329}
6330
6331impl Default for MarginalDistributionConfig {
6332    fn default() -> Self {
6333        Self::Normal {
6334            mu: 0.0,
6335            sigma: 1.0,
6336        }
6337    }
6338}
6339
6340/// Expected correlation for validation.
6341#[derive(Debug, Clone, Serialize, Deserialize)]
6342pub struct ExpectedCorrelationConfig {
6343    /// First field name.
6344    pub field1: String,
6345    /// Second field name.
6346    pub field2: String,
6347    /// Expected correlation coefficient.
6348    pub expected_r: f64,
6349    /// Acceptable tolerance.
6350    #[serde(default = "default_correlation_tolerance")]
6351    pub tolerance: f64,
6352}
6353
6354fn default_correlation_tolerance() -> f64 {
6355    0.10
6356}
6357
6358/// Conditional distribution configuration.
6359#[derive(Debug, Clone, Serialize, Deserialize)]
6360pub struct ConditionalDistributionSchemaConfig {
6361    /// Output field name to generate.
6362    pub output_field: String,
6363
6364    /// Input field name that conditions the distribution.
6365    pub input_field: String,
6366
6367    /// Breakpoints defining distribution changes.
6368    #[serde(default)]
6369    pub breakpoints: Vec<ConditionalBreakpointConfig>,
6370
6371    /// Default distribution when below all breakpoints.
6372    #[serde(default)]
6373    pub default_distribution: ConditionalDistributionParamsConfig,
6374
6375    /// Minimum output value constraint.
6376    #[serde(default)]
6377    pub min_value: Option<f64>,
6378
6379    /// Maximum output value constraint.
6380    #[serde(default)]
6381    pub max_value: Option<f64>,
6382
6383    /// Decimal places for output rounding.
6384    #[serde(default = "default_decimal_places")]
6385    pub decimal_places: u8,
6386}
6387
6388/// Breakpoint for conditional distribution.
6389#[derive(Debug, Clone, Serialize, Deserialize)]
6390pub struct ConditionalBreakpointConfig {
6391    /// Input value threshold.
6392    pub threshold: f64,
6393
6394    /// Distribution to use when input >= threshold.
6395    pub distribution: ConditionalDistributionParamsConfig,
6396}
6397
6398impl ConditionalDistributionSchemaConfig {
6399    /// Convert this schema config into a core
6400    /// [`ConditionalDistributionConfig`] suitable for
6401    /// [`ConditionalSampler::new`]. v3.5.3+.
6402    pub fn to_core_config(&self) -> datasynth_core::distributions::ConditionalDistributionConfig {
6403        use datasynth_core::distributions::{
6404            Breakpoint, ConditionalDistributionConfig, ConditionalDistributionParams,
6405        };
6406
6407        let default_distribution = convert_conditional_params(&self.default_distribution);
6408        let breakpoints: Vec<Breakpoint> = self
6409            .breakpoints
6410            .iter()
6411            .map(|bp| Breakpoint {
6412                threshold: bp.threshold,
6413                distribution: convert_conditional_params(&bp.distribution),
6414            })
6415            .collect();
6416
6417        // Use a sentinel default_distribution when the schema default is
6418        // its factory default (Fixed { value: 0.0 })  and we have
6419        // breakpoints — we don't want to clobber data for values below
6420        // the first breakpoint.
6421        let final_default = if breakpoints.is_empty() {
6422            default_distribution
6423        } else {
6424            match default_distribution {
6425                ConditionalDistributionParams::Fixed { value: 0.0 } => {
6426                    // Reuse the first breakpoint's distribution as the
6427                    // default to avoid surprising zeros.
6428                    breakpoints[0].distribution.clone()
6429                }
6430                other => other,
6431            }
6432        };
6433
6434        ConditionalDistributionConfig {
6435            output_field: self.output_field.clone(),
6436            input_field: self.input_field.clone(),
6437            breakpoints,
6438            default_distribution: final_default,
6439            min_value: self.min_value,
6440            max_value: self.max_value,
6441            decimal_places: self.decimal_places,
6442        }
6443    }
6444}
6445
6446fn convert_conditional_params(
6447    p: &ConditionalDistributionParamsConfig,
6448) -> datasynth_core::distributions::ConditionalDistributionParams {
6449    use datasynth_core::distributions::ConditionalDistributionParams as Core;
6450    match p {
6451        ConditionalDistributionParamsConfig::Fixed { value } => Core::Fixed { value: *value },
6452        ConditionalDistributionParamsConfig::Normal { mu, sigma } => Core::Normal {
6453            mu: *mu,
6454            sigma: *sigma,
6455        },
6456        ConditionalDistributionParamsConfig::LogNormal { mu, sigma } => Core::LogNormal {
6457            mu: *mu,
6458            sigma: *sigma,
6459        },
6460        ConditionalDistributionParamsConfig::Uniform { min, max } => Core::Uniform {
6461            min: *min,
6462            max: *max,
6463        },
6464        ConditionalDistributionParamsConfig::Beta {
6465            alpha,
6466            beta,
6467            min,
6468            max,
6469        } => Core::Beta {
6470            alpha: *alpha,
6471            beta: *beta,
6472            min: *min,
6473            max: *max,
6474        },
6475        ConditionalDistributionParamsConfig::Discrete { values, weights } => Core::Discrete {
6476            values: values.clone(),
6477            weights: weights.clone(),
6478        },
6479    }
6480}
6481
6482/// Distribution parameters for conditional distributions.
6483#[derive(Debug, Clone, Serialize, Deserialize)]
6484#[serde(tag = "type", rename_all = "snake_case")]
6485pub enum ConditionalDistributionParamsConfig {
6486    /// Fixed value.
6487    Fixed {
6488        /// The fixed value
6489        value: f64,
6490    },
6491    /// Normal distribution.
6492    Normal {
6493        /// Mean
6494        mu: f64,
6495        /// Standard deviation
6496        sigma: f64,
6497    },
6498    /// Log-normal distribution.
6499    LogNormal {
6500        /// Location parameter
6501        mu: f64,
6502        /// Scale parameter
6503        sigma: f64,
6504    },
6505    /// Uniform distribution.
6506    Uniform {
6507        /// Minimum
6508        min: f64,
6509        /// Maximum
6510        max: f64,
6511    },
6512    /// Beta distribution (scaled).
6513    Beta {
6514        /// Alpha parameter
6515        alpha: f64,
6516        /// Beta parameter
6517        beta: f64,
6518        /// Minimum output value
6519        min: f64,
6520        /// Maximum output value
6521        max: f64,
6522    },
6523    /// Discrete values with weights.
6524    Discrete {
6525        /// Possible values
6526        values: Vec<f64>,
6527        /// Weights (should sum to 1.0)
6528        weights: Vec<f64>,
6529    },
6530}
6531
6532impl Default for ConditionalDistributionParamsConfig {
6533    fn default() -> Self {
6534        Self::Normal {
6535            mu: 0.0,
6536            sigma: 1.0,
6537        }
6538    }
6539}
6540
6541/// Regime change configuration.
6542#[derive(Debug, Clone, Serialize, Deserialize, Default)]
6543pub struct RegimeChangeSchemaConfig {
6544    /// Enable regime change modeling.
6545    #[serde(default)]
6546    pub enabled: bool,
6547
6548    /// List of regime changes.
6549    #[serde(default)]
6550    pub changes: Vec<RegimeChangeEventConfig>,
6551
6552    /// Economic cycle configuration.
6553    #[serde(default)]
6554    pub economic_cycle: Option<EconomicCycleSchemaConfig>,
6555
6556    /// Parameter drift configurations.
6557    #[serde(default)]
6558    pub parameter_drifts: Vec<ParameterDriftSchemaConfig>,
6559}
6560
6561/// A single regime change event.
6562#[derive(Debug, Clone, Serialize, Deserialize)]
6563pub struct RegimeChangeEventConfig {
6564    /// Date when the change occurs (ISO 8601 format).
6565    pub date: String,
6566
6567    /// Type of regime change.
6568    pub change_type: RegimeChangeTypeConfig,
6569
6570    /// Description of the change.
6571    #[serde(default)]
6572    pub description: Option<String>,
6573
6574    /// Effects of this regime change.
6575    #[serde(default)]
6576    pub effects: Vec<RegimeEffectConfig>,
6577}
6578
6579/// Type of regime change.
6580#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
6581#[serde(rename_all = "snake_case")]
6582pub enum RegimeChangeTypeConfig {
6583    /// Acquisition - sudden volume and amount increase
6584    Acquisition,
6585    /// Divestiture - sudden volume and amount decrease
6586    Divestiture,
6587    /// Price increase - amounts increase
6588    PriceIncrease,
6589    /// Price decrease - amounts decrease
6590    PriceDecrease,
6591    /// New product launch - volume ramp-up
6592    ProductLaunch,
6593    /// Product discontinuation - volume ramp-down
6594    ProductDiscontinuation,
6595    /// Policy change - affects patterns
6596    PolicyChange,
6597    /// Competitor entry - market disruption
6598    CompetitorEntry,
6599    /// Custom effect
6600    Custom,
6601}
6602
6603/// Effect of a regime change on a specific field.
6604#[derive(Debug, Clone, Serialize, Deserialize)]
6605pub struct RegimeEffectConfig {
6606    /// Field being affected.
6607    pub field: String,
6608
6609    /// Multiplier to apply (1.0 = no change, 1.5 = 50% increase).
6610    pub multiplier: f64,
6611}
6612
6613/// Economic cycle configuration.
6614#[derive(Debug, Clone, Serialize, Deserialize)]
6615pub struct EconomicCycleSchemaConfig {
6616    /// Enable economic cycle modeling.
6617    #[serde(default)]
6618    pub enabled: bool,
6619
6620    /// Cycle period in months (e.g., 48 for 4-year business cycle).
6621    #[serde(default = "default_cycle_period")]
6622    pub period_months: u32,
6623
6624    /// Amplitude of cycle effect (0.0-1.0).
6625    #[serde(default = "default_cycle_amplitude")]
6626    pub amplitude: f64,
6627
6628    /// Phase offset in months.
6629    #[serde(default)]
6630    pub phase_offset: u32,
6631
6632    /// Recession periods (start_month, duration_months).
6633    #[serde(default)]
6634    pub recessions: Vec<RecessionPeriodConfig>,
6635}
6636
6637fn default_cycle_period() -> u32 {
6638    48
6639}
6640
6641fn default_cycle_amplitude() -> f64 {
6642    0.15
6643}
6644
6645impl Default for EconomicCycleSchemaConfig {
6646    fn default() -> Self {
6647        Self {
6648            enabled: false,
6649            period_months: 48,
6650            amplitude: 0.15,
6651            phase_offset: 0,
6652            recessions: Vec::new(),
6653        }
6654    }
6655}
6656
6657/// Recession period configuration.
6658#[derive(Debug, Clone, Serialize, Deserialize)]
6659pub struct RecessionPeriodConfig {
6660    /// Start month (0-indexed from generation start).
6661    pub start_month: u32,
6662
6663    /// Duration in months.
6664    pub duration_months: u32,
6665
6666    /// Severity (0.0-1.0, affects volume reduction).
6667    #[serde(default = "default_recession_severity")]
6668    pub severity: f64,
6669}
6670
6671impl RegimeChangeSchemaConfig {
6672    /// Populate the regime-change, economic-cycle, and parameter-drift
6673    /// slots on a `DriftConfig` from this schema config. v3.5.2+.
6674    ///
6675    /// `generation_start` must match `config.global.start_date` so that
6676    /// absolute regime-change dates can be mapped to 0-indexed periods.
6677    /// Unparseable / out-of-range dates are silently skipped to keep
6678    /// runtime robust against user typos.
6679    pub fn apply_to(
6680        &self,
6681        drift: &mut datasynth_core::distributions::DriftConfig,
6682        generation_start: chrono::NaiveDate,
6683    ) {
6684        if !self.enabled {
6685            return;
6686        }
6687
6688        // Enable drift if any regime-change feature wants it.
6689        drift.enabled = true;
6690
6691        // Regime-change events (absolute dates → period offsets).
6692        for event in &self.changes {
6693            let period = match chrono::NaiveDate::parse_from_str(&event.date, "%Y-%m-%d") {
6694                Ok(d) => {
6695                    let days = (d - generation_start).num_days();
6696                    if days < 0 {
6697                        continue;
6698                    }
6699                    // Approximate month by dividing by 30.4 so we don't
6700                    // need chrono::Months arithmetic.
6701                    (days as f64 / 30.4).round() as u32
6702                }
6703                Err(_) => continue,
6704            };
6705            let change_type = convert_regime_change_type(event.change_type);
6706            let core_effects = event
6707                .effects
6708                .iter()
6709                .map(|e| datasynth_core::distributions::RegimeEffect {
6710                    field: e.field.clone(),
6711                    multiplier: e.multiplier,
6712                })
6713                .collect();
6714            drift
6715                .regime_changes
6716                .push(datasynth_core::distributions::RegimeChange {
6717                    period,
6718                    change_type,
6719                    description: event.description.clone(),
6720                    effects: core_effects,
6721                    transition_periods: 0,
6722                });
6723        }
6724
6725        // Economic cycle.
6726        if let Some(ec) = &self.economic_cycle {
6727            if ec.enabled {
6728                let recession_periods: Vec<u32> = ec
6729                    .recessions
6730                    .iter()
6731                    .flat_map(|r| r.start_month..r.start_month + r.duration_months)
6732                    .collect();
6733                // Use the most-severe recession as the severity driver;
6734                // fall back to default when none declared.
6735                let severity = ec
6736                    .recessions
6737                    .iter()
6738                    .map(|r| 1.0 - r.severity)
6739                    .fold(0.75f64, f64::min);
6740                drift.economic_cycle = datasynth_core::distributions::EconomicCycleConfig {
6741                    enabled: true,
6742                    cycle_length: ec.period_months,
6743                    amplitude: ec.amplitude,
6744                    phase_offset: ec.phase_offset,
6745                    recession_periods,
6746                    recession_severity: severity,
6747                };
6748                drift.drift_type = datasynth_core::distributions::DriftType::Mixed;
6749            }
6750        }
6751
6752        // Parameter drifts.
6753        for pd in &self.parameter_drifts {
6754            let drift_type = match pd.drift_type {
6755                ParameterDriftTypeConfig::Linear => {
6756                    datasynth_core::distributions::ParameterDriftType::Linear
6757                }
6758                ParameterDriftTypeConfig::Exponential => {
6759                    datasynth_core::distributions::ParameterDriftType::Exponential
6760                }
6761                ParameterDriftTypeConfig::Logistic => {
6762                    datasynth_core::distributions::ParameterDriftType::Logistic
6763                }
6764                ParameterDriftTypeConfig::Step => {
6765                    datasynth_core::distributions::ParameterDriftType::Step
6766                }
6767            };
6768            drift
6769                .parameter_drifts
6770                .push(datasynth_core::distributions::ParameterDrift {
6771                    parameter: pd.parameter.clone(),
6772                    drift_type,
6773                    initial_value: pd.start_value,
6774                    target_or_rate: pd.end_value,
6775                    start_period: pd.start_period,
6776                    end_period: pd.end_period,
6777                    steepness: 1.0,
6778                });
6779        }
6780    }
6781}
6782
6783fn convert_regime_change_type(
6784    t: RegimeChangeTypeConfig,
6785) -> datasynth_core::distributions::RegimeChangeType {
6786    use datasynth_core::distributions::RegimeChangeType as Core;
6787    match t {
6788        RegimeChangeTypeConfig::Acquisition => Core::Acquisition,
6789        RegimeChangeTypeConfig::Divestiture => Core::Divestiture,
6790        RegimeChangeTypeConfig::PriceIncrease => Core::PriceIncrease,
6791        RegimeChangeTypeConfig::PriceDecrease => Core::PriceDecrease,
6792        RegimeChangeTypeConfig::ProductLaunch => Core::ProductLaunch,
6793        RegimeChangeTypeConfig::ProductDiscontinuation => Core::ProductDiscontinuation,
6794        RegimeChangeTypeConfig::PolicyChange => Core::PolicyChange,
6795        RegimeChangeTypeConfig::CompetitorEntry => Core::CompetitorEntry,
6796        RegimeChangeTypeConfig::Custom => Core::Custom,
6797    }
6798}
6799
6800fn default_recession_severity() -> f64 {
6801    0.20
6802}
6803
6804/// Parameter drift configuration.
6805#[derive(Debug, Clone, Serialize, Deserialize)]
6806pub struct ParameterDriftSchemaConfig {
6807    /// Parameter being drifted.
6808    pub parameter: String,
6809
6810    /// Drift type.
6811    pub drift_type: ParameterDriftTypeConfig,
6812
6813    /// Start value.
6814    pub start_value: f64,
6815
6816    /// End value.
6817    pub end_value: f64,
6818
6819    /// Start period (month, 0-indexed).
6820    #[serde(default)]
6821    pub start_period: u32,
6822
6823    /// End period (month, optional - defaults to end of generation).
6824    #[serde(default)]
6825    pub end_period: Option<u32>,
6826}
6827
6828/// Parameter drift type.
6829#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
6830#[serde(rename_all = "snake_case")]
6831pub enum ParameterDriftTypeConfig {
6832    /// Linear interpolation
6833    #[default]
6834    Linear,
6835    /// Exponential growth/decay
6836    Exponential,
6837    /// S-curve (logistic)
6838    Logistic,
6839    /// Step function
6840    Step,
6841}
6842
6843/// Statistical validation configuration.
6844#[derive(Debug, Clone, Serialize, Deserialize, Default)]
6845pub struct StatisticalValidationSchemaConfig {
6846    /// Enable statistical validation.
6847    #[serde(default)]
6848    pub enabled: bool,
6849
6850    /// Statistical tests to run.
6851    #[serde(default)]
6852    pub tests: Vec<StatisticalTestConfig>,
6853
6854    /// Validation reporting configuration.
6855    #[serde(default)]
6856    pub reporting: ValidationReportingConfig,
6857}
6858
6859/// Statistical test configuration.
6860#[derive(Debug, Clone, Serialize, Deserialize)]
6861#[serde(tag = "type", rename_all = "snake_case")]
6862pub enum StatisticalTestConfig {
6863    /// Benford's Law first digit test.
6864    BenfordFirstDigit {
6865        /// Threshold MAD for failure.
6866        #[serde(default = "default_benford_threshold")]
6867        threshold_mad: f64,
6868        /// Warning MAD threshold.
6869        #[serde(default = "default_benford_warning")]
6870        warning_mad: f64,
6871    },
6872    /// Distribution fit test.
6873    DistributionFit {
6874        /// Target distribution to test.
6875        target: TargetDistributionConfig,
6876        /// K-S test significance level.
6877        #[serde(default = "default_ks_significance")]
6878        ks_significance: f64,
6879        /// Test method (ks, anderson_darling, chi_squared).
6880        #[serde(default)]
6881        method: DistributionFitMethod,
6882    },
6883    /// Correlation check.
6884    CorrelationCheck {
6885        /// Expected correlations to validate.
6886        expected_correlations: Vec<ExpectedCorrelationConfig>,
6887    },
6888    /// Chi-squared test.
6889    ChiSquared {
6890        /// Number of bins.
6891        #[serde(default = "default_chi_squared_bins")]
6892        bins: usize,
6893        /// Significance level.
6894        #[serde(default = "default_chi_squared_significance")]
6895        significance: f64,
6896    },
6897    /// Anderson-Darling test.
6898    AndersonDarling {
6899        /// Target distribution.
6900        target: TargetDistributionConfig,
6901        /// Significance level.
6902        #[serde(default = "default_ad_significance")]
6903        significance: f64,
6904    },
6905}
6906
6907fn default_benford_threshold() -> f64 {
6908    0.015
6909}
6910
6911fn default_benford_warning() -> f64 {
6912    0.010
6913}
6914
6915fn default_ks_significance() -> f64 {
6916    0.05
6917}
6918
6919fn default_chi_squared_bins() -> usize {
6920    10
6921}
6922
6923fn default_chi_squared_significance() -> f64 {
6924    0.05
6925}
6926
6927fn default_ad_significance() -> f64 {
6928    0.05
6929}
6930
6931/// Target distribution for fit tests.
6932#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
6933#[serde(rename_all = "snake_case")]
6934pub enum TargetDistributionConfig {
6935    /// Normal distribution
6936    Normal,
6937    /// Log-normal distribution
6938    #[default]
6939    LogNormal,
6940    /// Exponential distribution
6941    Exponential,
6942    /// Uniform distribution
6943    Uniform,
6944}
6945
6946/// Distribution fit test method.
6947#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
6948#[serde(rename_all = "snake_case")]
6949pub enum DistributionFitMethod {
6950    /// Kolmogorov-Smirnov test
6951    #[default]
6952    KolmogorovSmirnov,
6953    /// Anderson-Darling test
6954    AndersonDarling,
6955    /// Chi-squared test
6956    ChiSquared,
6957}
6958
6959/// Validation reporting configuration.
6960#[derive(Debug, Clone, Serialize, Deserialize)]
6961pub struct ValidationReportingConfig {
6962    /// Output validation report to file.
6963    #[serde(default)]
6964    pub output_report: bool,
6965
6966    /// Report format.
6967    #[serde(default)]
6968    pub format: ValidationReportFormat,
6969
6970    /// Fail generation if validation fails.
6971    #[serde(default)]
6972    pub fail_on_error: bool,
6973
6974    /// Include detailed statistics in report.
6975    #[serde(default = "default_true")]
6976    pub include_details: bool,
6977}
6978
6979impl Default for ValidationReportingConfig {
6980    fn default() -> Self {
6981        Self {
6982            output_report: false,
6983            format: ValidationReportFormat::Json,
6984            fail_on_error: false,
6985            include_details: true,
6986        }
6987    }
6988}
6989
6990/// Validation report format.
6991#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
6992#[serde(rename_all = "snake_case")]
6993pub enum ValidationReportFormat {
6994    /// JSON format
6995    #[default]
6996    Json,
6997    /// YAML format
6998    Yaml,
6999    /// HTML report
7000    Html,
7001}
7002
7003// =============================================================================
7004// Temporal Patterns Configuration
7005// =============================================================================
7006
7007/// Temporal patterns configuration for business days, period-end dynamics, and processing lags.
7008///
7009/// This section enables sophisticated temporal modeling including:
7010/// - Business day calculations and settlement dates
7011/// - Regional holiday calendars
7012/// - Period-end decay curves (non-flat volume spikes)
7013/// - Processing lag modeling (event-to-posting delays)
7014#[derive(Debug, Clone, Serialize, Deserialize, Default)]
7015pub struct TemporalPatternsConfig {
7016    /// Enable temporal patterns features.
7017    #[serde(default)]
7018    pub enabled: bool,
7019
7020    /// Business day calculation configuration.
7021    #[serde(default)]
7022    pub business_days: BusinessDaySchemaConfig,
7023
7024    /// Regional calendar configuration.
7025    #[serde(default)]
7026    pub calendars: CalendarSchemaConfig,
7027
7028    /// Period-end dynamics configuration.
7029    #[serde(default)]
7030    pub period_end: PeriodEndSchemaConfig,
7031
7032    /// Processing lag configuration.
7033    #[serde(default)]
7034    pub processing_lags: ProcessingLagSchemaConfig,
7035
7036    /// Fiscal calendar configuration (custom year start, 4-4-5, 13-period).
7037    #[serde(default)]
7038    pub fiscal_calendar: FiscalCalendarSchemaConfig,
7039
7040    /// Intra-day patterns configuration (morning spike, lunch dip, EOD rush).
7041    #[serde(default)]
7042    pub intraday: IntraDaySchemaConfig,
7043
7044    /// Timezone handling configuration.
7045    #[serde(default)]
7046    pub timezones: TimezoneSchemaConfig,
7047}
7048
7049/// Business day calculation configuration.
7050#[derive(Debug, Clone, Serialize, Deserialize)]
7051pub struct BusinessDaySchemaConfig {
7052    /// Enable business day calculations.
7053    #[serde(default = "default_true")]
7054    pub enabled: bool,
7055
7056    /// Half-day policy: "full_day", "half_day", "non_business_day".
7057    #[serde(default = "default_half_day_policy")]
7058    pub half_day_policy: String,
7059
7060    /// Settlement rules configuration.
7061    #[serde(default)]
7062    pub settlement_rules: SettlementRulesSchemaConfig,
7063
7064    /// Month-end convention: "modified_following", "preceding", "following", "end_of_month".
7065    #[serde(default = "default_month_end_convention")]
7066    pub month_end_convention: String,
7067
7068    /// Weekend days (e.g., ["saturday", "sunday"] or ["friday", "saturday"] for Middle East).
7069    #[serde(default)]
7070    pub weekend_days: Option<Vec<String>>,
7071}
7072
7073fn default_half_day_policy() -> String {
7074    "half_day".to_string()
7075}
7076
7077fn default_month_end_convention() -> String {
7078    "modified_following".to_string()
7079}
7080
7081impl Default for BusinessDaySchemaConfig {
7082    fn default() -> Self {
7083        Self {
7084            enabled: true,
7085            half_day_policy: "half_day".to_string(),
7086            settlement_rules: SettlementRulesSchemaConfig::default(),
7087            month_end_convention: "modified_following".to_string(),
7088            weekend_days: None,
7089        }
7090    }
7091}
7092
7093/// Settlement rules configuration.
7094#[derive(Debug, Clone, Serialize, Deserialize)]
7095pub struct SettlementRulesSchemaConfig {
7096    /// Equity settlement days (T+N).
7097    #[serde(default = "default_settlement_2")]
7098    pub equity_days: i32,
7099
7100    /// Government bonds settlement days.
7101    #[serde(default = "default_settlement_1")]
7102    pub government_bonds_days: i32,
7103
7104    /// FX spot settlement days.
7105    #[serde(default = "default_settlement_2")]
7106    pub fx_spot_days: i32,
7107
7108    /// Corporate bonds settlement days.
7109    #[serde(default = "default_settlement_2")]
7110    pub corporate_bonds_days: i32,
7111
7112    /// Wire transfer cutoff time (HH:MM format).
7113    #[serde(default = "default_wire_cutoff")]
7114    pub wire_cutoff_time: String,
7115
7116    /// International wire settlement days.
7117    #[serde(default = "default_settlement_1")]
7118    pub wire_international_days: i32,
7119
7120    /// ACH settlement days.
7121    #[serde(default = "default_settlement_1")]
7122    pub ach_days: i32,
7123}
7124
7125fn default_settlement_1() -> i32 {
7126    1
7127}
7128
7129fn default_settlement_2() -> i32 {
7130    2
7131}
7132
7133fn default_wire_cutoff() -> String {
7134    "14:00".to_string()
7135}
7136
7137impl Default for SettlementRulesSchemaConfig {
7138    fn default() -> Self {
7139        Self {
7140            equity_days: 2,
7141            government_bonds_days: 1,
7142            fx_spot_days: 2,
7143            corporate_bonds_days: 2,
7144            wire_cutoff_time: "14:00".to_string(),
7145            wire_international_days: 1,
7146            ach_days: 1,
7147        }
7148    }
7149}
7150
7151/// Regional calendar configuration.
7152#[derive(Debug, Clone, Serialize, Deserialize, Default)]
7153pub struct CalendarSchemaConfig {
7154    /// List of regions to include (e.g., ["US", "DE", "BR", "SG", "KR"]).
7155    #[serde(default)]
7156    pub regions: Vec<String>,
7157
7158    /// Custom holidays (in addition to regional calendars).
7159    #[serde(default)]
7160    pub custom_holidays: Vec<CustomHolidaySchemaConfig>,
7161}
7162
7163/// Custom holiday configuration.
7164#[derive(Debug, Clone, Serialize, Deserialize)]
7165pub struct CustomHolidaySchemaConfig {
7166    /// Holiday name.
7167    pub name: String,
7168    /// Month (1-12).
7169    pub month: u8,
7170    /// Day of month.
7171    pub day: u8,
7172    /// Activity multiplier (0.0-1.0, default 0.05).
7173    #[serde(default = "default_holiday_multiplier")]
7174    pub activity_multiplier: f64,
7175}
7176
7177fn default_holiday_multiplier() -> f64 {
7178    0.05
7179}
7180
7181/// Period-end dynamics configuration.
7182#[derive(Debug, Clone, Serialize, Deserialize, Default)]
7183pub struct PeriodEndSchemaConfig {
7184    /// Model type: "flat", "exponential", "extended_crunch", "daily_profile".
7185    #[serde(default)]
7186    pub model: Option<String>,
7187
7188    /// Month-end configuration.
7189    #[serde(default)]
7190    pub month_end: Option<PeriodEndModelSchemaConfig>,
7191
7192    /// Quarter-end configuration.
7193    #[serde(default)]
7194    pub quarter_end: Option<PeriodEndModelSchemaConfig>,
7195
7196    /// Year-end configuration.
7197    #[serde(default)]
7198    pub year_end: Option<PeriodEndModelSchemaConfig>,
7199}
7200
7201/// Period-end model configuration.
7202#[derive(Debug, Clone, Serialize, Deserialize, Default)]
7203pub struct PeriodEndModelSchemaConfig {
7204    /// Inherit configuration from another period (e.g., "month_end").
7205    #[serde(default)]
7206    pub inherit_from: Option<String>,
7207
7208    /// Additional multiplier on top of inherited/base model.
7209    #[serde(default)]
7210    pub additional_multiplier: Option<f64>,
7211
7212    /// Days before period end to start acceleration (negative, e.g., -10).
7213    #[serde(default)]
7214    pub start_day: Option<i32>,
7215
7216    /// Base multiplier at start of acceleration.
7217    #[serde(default)]
7218    pub base_multiplier: Option<f64>,
7219
7220    /// Peak multiplier on last day.
7221    #[serde(default)]
7222    pub peak_multiplier: Option<f64>,
7223
7224    /// Decay rate for exponential model (0.1-0.5 typical).
7225    #[serde(default)]
7226    pub decay_rate: Option<f64>,
7227
7228    /// Sustained high days for crunch model.
7229    #[serde(default)]
7230    pub sustained_high_days: Option<i32>,
7231}
7232
7233/// Processing lag configuration.
7234#[derive(Debug, Clone, Serialize, Deserialize)]
7235pub struct ProcessingLagSchemaConfig {
7236    /// Enable processing lag calculations.
7237    #[serde(default = "default_true")]
7238    pub enabled: bool,
7239
7240    /// Sales order lag configuration (log-normal mu, sigma).
7241    #[serde(default)]
7242    pub sales_order_lag: Option<LagDistributionSchemaConfig>,
7243
7244    /// Purchase order lag configuration.
7245    #[serde(default)]
7246    pub purchase_order_lag: Option<LagDistributionSchemaConfig>,
7247
7248    /// Goods receipt lag configuration.
7249    #[serde(default)]
7250    pub goods_receipt_lag: Option<LagDistributionSchemaConfig>,
7251
7252    /// Invoice receipt lag configuration.
7253    #[serde(default)]
7254    pub invoice_receipt_lag: Option<LagDistributionSchemaConfig>,
7255
7256    /// Invoice issue lag configuration.
7257    #[serde(default)]
7258    pub invoice_issue_lag: Option<LagDistributionSchemaConfig>,
7259
7260    /// Payment lag configuration.
7261    #[serde(default)]
7262    pub payment_lag: Option<LagDistributionSchemaConfig>,
7263
7264    /// Journal entry lag configuration.
7265    #[serde(default)]
7266    pub journal_entry_lag: Option<LagDistributionSchemaConfig>,
7267
7268    /// Cross-day posting configuration.
7269    #[serde(default)]
7270    pub cross_day_posting: Option<CrossDayPostingSchemaConfig>,
7271}
7272
7273impl Default for ProcessingLagSchemaConfig {
7274    fn default() -> Self {
7275        Self {
7276            enabled: true,
7277            sales_order_lag: None,
7278            purchase_order_lag: None,
7279            goods_receipt_lag: None,
7280            invoice_receipt_lag: None,
7281            invoice_issue_lag: None,
7282            payment_lag: None,
7283            journal_entry_lag: None,
7284            cross_day_posting: None,
7285        }
7286    }
7287}
7288
7289/// Lag distribution configuration (log-normal parameters).
7290#[derive(Debug, Clone, Serialize, Deserialize)]
7291pub struct LagDistributionSchemaConfig {
7292    /// Log-scale mean (mu for log-normal).
7293    pub mu: f64,
7294    /// Log-scale standard deviation (sigma for log-normal).
7295    pub sigma: f64,
7296    /// Minimum lag in hours.
7297    #[serde(default)]
7298    pub min_hours: Option<f64>,
7299    /// Maximum lag in hours.
7300    #[serde(default)]
7301    pub max_hours: Option<f64>,
7302}
7303
7304/// Cross-day posting configuration.
7305#[derive(Debug, Clone, Serialize, Deserialize)]
7306pub struct CrossDayPostingSchemaConfig {
7307    /// Enable cross-day posting logic.
7308    #[serde(default = "default_true")]
7309    pub enabled: bool,
7310
7311    /// Probability of next-day posting by hour (map of hour -> probability).
7312    /// E.g., { 17: 0.7, 19: 0.9, 21: 0.99 }
7313    #[serde(default)]
7314    pub probability_by_hour: std::collections::HashMap<u8, f64>,
7315}
7316
7317impl Default for CrossDayPostingSchemaConfig {
7318    fn default() -> Self {
7319        let mut probability_by_hour = std::collections::HashMap::new();
7320        probability_by_hour.insert(17, 0.3);
7321        probability_by_hour.insert(18, 0.6);
7322        probability_by_hour.insert(19, 0.8);
7323        probability_by_hour.insert(20, 0.9);
7324        probability_by_hour.insert(21, 0.95);
7325        probability_by_hour.insert(22, 0.99);
7326
7327        Self {
7328            enabled: true,
7329            probability_by_hour,
7330        }
7331    }
7332}
7333
7334// =============================================================================
7335// Fiscal Calendar Configuration (P2)
7336// =============================================================================
7337
7338/// Fiscal calendar configuration.
7339///
7340/// Supports calendar year, custom year start, 4-4-5 retail calendar,
7341/// and 13-period calendars.
7342#[derive(Debug, Clone, Serialize, Deserialize, Default)]
7343pub struct FiscalCalendarSchemaConfig {
7344    /// Enable non-standard fiscal calendar.
7345    #[serde(default)]
7346    pub enabled: bool,
7347
7348    /// Fiscal calendar type: "calendar_year", "custom", "four_four_five", "thirteen_period".
7349    #[serde(default = "default_fiscal_calendar_type")]
7350    pub calendar_type: String,
7351
7352    /// Month the fiscal year starts (1-12). Used for custom year start.
7353    #[serde(default)]
7354    pub year_start_month: Option<u8>,
7355
7356    /// Day the fiscal year starts (1-31). Used for custom year start.
7357    #[serde(default)]
7358    pub year_start_day: Option<u8>,
7359
7360    /// 4-4-5 calendar configuration (if calendar_type is "four_four_five").
7361    #[serde(default)]
7362    pub four_four_five: Option<FourFourFiveSchemaConfig>,
7363}
7364
7365fn default_fiscal_calendar_type() -> String {
7366    "calendar_year".to_string()
7367}
7368
7369/// 4-4-5 retail calendar configuration.
7370#[derive(Debug, Clone, Serialize, Deserialize)]
7371pub struct FourFourFiveSchemaConfig {
7372    /// Week pattern: "four_four_five", "four_five_four", "five_four_four".
7373    #[serde(default = "default_week_pattern")]
7374    pub pattern: String,
7375
7376    /// Anchor type: "first_sunday", "last_saturday", "nearest_saturday".
7377    #[serde(default = "default_anchor_type")]
7378    pub anchor_type: String,
7379
7380    /// Anchor month (1-12).
7381    #[serde(default = "default_anchor_month")]
7382    pub anchor_month: u8,
7383
7384    /// Where to place leap week: "q4_period3" or "q1_period1".
7385    #[serde(default = "default_leap_week_placement")]
7386    pub leap_week_placement: String,
7387}
7388
7389fn default_week_pattern() -> String {
7390    "four_four_five".to_string()
7391}
7392
7393fn default_anchor_type() -> String {
7394    "last_saturday".to_string()
7395}
7396
7397fn default_anchor_month() -> u8 {
7398    1 // January
7399}
7400
7401fn default_leap_week_placement() -> String {
7402    "q4_period3".to_string()
7403}
7404
7405impl Default for FourFourFiveSchemaConfig {
7406    fn default() -> Self {
7407        Self {
7408            pattern: "four_four_five".to_string(),
7409            anchor_type: "last_saturday".to_string(),
7410            anchor_month: 1,
7411            leap_week_placement: "q4_period3".to_string(),
7412        }
7413    }
7414}
7415
7416// =============================================================================
7417// Intra-Day Patterns Configuration (P2)
7418// =============================================================================
7419
7420/// Intra-day patterns configuration.
7421///
7422/// Defines time-of-day segments with different activity multipliers
7423/// for realistic modeling of morning spikes, lunch dips, and end-of-day rushes.
7424#[derive(Debug, Clone, Serialize, Deserialize, Default)]
7425pub struct IntraDaySchemaConfig {
7426    /// Enable intra-day patterns.
7427    #[serde(default)]
7428    pub enabled: bool,
7429
7430    /// Custom intra-day segments.
7431    #[serde(default)]
7432    pub segments: Vec<IntraDaySegmentSchemaConfig>,
7433}
7434
7435/// Intra-day segment configuration.
7436#[derive(Debug, Clone, Serialize, Deserialize)]
7437pub struct IntraDaySegmentSchemaConfig {
7438    /// Name of the segment (e.g., "morning_spike", "lunch_dip").
7439    pub name: String,
7440
7441    /// Start time (HH:MM format).
7442    pub start: String,
7443
7444    /// End time (HH:MM format).
7445    pub end: String,
7446
7447    /// Activity multiplier (1.0 = normal).
7448    #[serde(default = "default_multiplier")]
7449    pub multiplier: f64,
7450
7451    /// Posting type: "human", "system", "both".
7452    #[serde(default = "default_posting_type")]
7453    pub posting_type: String,
7454}
7455
7456fn default_multiplier() -> f64 {
7457    1.0
7458}
7459
7460fn default_posting_type() -> String {
7461    "both".to_string()
7462}
7463
7464// =============================================================================
7465// Timezone Configuration
7466// =============================================================================
7467
7468/// Timezone handling configuration for multi-region entities.
7469#[derive(Debug, Clone, Serialize, Deserialize, Default)]
7470pub struct TimezoneSchemaConfig {
7471    /// Enable timezone handling.
7472    #[serde(default)]
7473    pub enabled: bool,
7474
7475    /// Default timezone (IANA format, e.g., "America/New_York").
7476    #[serde(default = "default_timezone")]
7477    pub default_timezone: String,
7478
7479    /// Consolidation timezone for group reporting (IANA format).
7480    #[serde(default = "default_consolidation_timezone")]
7481    pub consolidation_timezone: String,
7482
7483    /// Entity-to-timezone mappings.
7484    /// Supports patterns like "EU_*" -> "Europe/London".
7485    #[serde(default)]
7486    pub entity_mappings: Vec<EntityTimezoneMapping>,
7487}
7488
7489fn default_timezone() -> String {
7490    "America/New_York".to_string()
7491}
7492
7493fn default_consolidation_timezone() -> String {
7494    "UTC".to_string()
7495}
7496
7497/// Mapping from entity pattern to timezone.
7498#[derive(Debug, Clone, Serialize, Deserialize)]
7499pub struct EntityTimezoneMapping {
7500    /// Entity code pattern (e.g., "EU_*", "*_APAC", "1000").
7501    pub pattern: String,
7502
7503    /// Timezone (IANA format, e.g., "Europe/London").
7504    pub timezone: String,
7505}
7506
7507// =============================================================================
7508// Vendor Network Configuration
7509// =============================================================================
7510
7511/// Configuration for multi-tier vendor network generation.
7512#[derive(Debug, Clone, Serialize, Deserialize)]
7513pub struct VendorNetworkSchemaConfig {
7514    /// Enable vendor network generation.
7515    #[serde(default)]
7516    pub enabled: bool,
7517
7518    /// Maximum depth of supply chain tiers (1-3).
7519    #[serde(default = "default_vendor_tier_depth")]
7520    pub depth: u8,
7521
7522    /// Tier 1 vendor count configuration.
7523    #[serde(default)]
7524    pub tier1: TierCountSchemaConfig,
7525
7526    /// Tier 2 vendors per Tier 1 parent.
7527    #[serde(default)]
7528    pub tier2_per_parent: TierCountSchemaConfig,
7529
7530    /// Tier 3 vendors per Tier 2 parent.
7531    #[serde(default)]
7532    pub tier3_per_parent: TierCountSchemaConfig,
7533
7534    /// Vendor cluster distribution.
7535    #[serde(default)]
7536    pub clusters: VendorClusterSchemaConfig,
7537
7538    /// Concentration limits.
7539    #[serde(default)]
7540    pub dependencies: DependencySchemaConfig,
7541}
7542
7543fn default_vendor_tier_depth() -> u8 {
7544    3
7545}
7546
7547impl Default for VendorNetworkSchemaConfig {
7548    fn default() -> Self {
7549        Self {
7550            enabled: false,
7551            depth: 3,
7552            tier1: TierCountSchemaConfig { min: 50, max: 100 },
7553            tier2_per_parent: TierCountSchemaConfig { min: 4, max: 10 },
7554            tier3_per_parent: TierCountSchemaConfig { min: 2, max: 5 },
7555            clusters: VendorClusterSchemaConfig::default(),
7556            dependencies: DependencySchemaConfig::default(),
7557        }
7558    }
7559}
7560
7561/// Tier count configuration.
7562#[derive(Debug, Clone, Serialize, Deserialize)]
7563pub struct TierCountSchemaConfig {
7564    /// Minimum count.
7565    #[serde(default = "default_tier_min")]
7566    pub min: usize,
7567
7568    /// Maximum count.
7569    #[serde(default = "default_tier_max")]
7570    pub max: usize,
7571}
7572
7573fn default_tier_min() -> usize {
7574    5
7575}
7576
7577fn default_tier_max() -> usize {
7578    20
7579}
7580
7581impl Default for TierCountSchemaConfig {
7582    fn default() -> Self {
7583        Self {
7584            min: default_tier_min(),
7585            max: default_tier_max(),
7586        }
7587    }
7588}
7589
7590/// Vendor cluster distribution configuration.
7591#[derive(Debug, Clone, Serialize, Deserialize)]
7592pub struct VendorClusterSchemaConfig {
7593    /// Reliable strategic vendors percentage (default: 0.20).
7594    #[serde(default = "default_reliable_strategic")]
7595    pub reliable_strategic: f64,
7596
7597    /// Standard operational vendors percentage (default: 0.50).
7598    #[serde(default = "default_standard_operational")]
7599    pub standard_operational: f64,
7600
7601    /// Transactional vendors percentage (default: 0.25).
7602    #[serde(default = "default_transactional")]
7603    pub transactional: f64,
7604
7605    /// Problematic vendors percentage (default: 0.05).
7606    #[serde(default = "default_problematic")]
7607    pub problematic: f64,
7608}
7609
7610fn default_reliable_strategic() -> f64 {
7611    0.20
7612}
7613
7614fn default_standard_operational() -> f64 {
7615    0.50
7616}
7617
7618fn default_transactional() -> f64 {
7619    0.25
7620}
7621
7622fn default_problematic() -> f64 {
7623    0.05
7624}
7625
7626impl Default for VendorClusterSchemaConfig {
7627    fn default() -> Self {
7628        Self {
7629            reliable_strategic: 0.20,
7630            standard_operational: 0.50,
7631            transactional: 0.25,
7632            problematic: 0.05,
7633        }
7634    }
7635}
7636
7637/// Dependency and concentration limits configuration.
7638#[derive(Debug, Clone, Serialize, Deserialize)]
7639pub struct DependencySchemaConfig {
7640    /// Maximum concentration for a single vendor (default: 0.15).
7641    #[serde(default = "default_max_single_vendor")]
7642    pub max_single_vendor_concentration: f64,
7643
7644    /// Maximum concentration for top 5 vendors (default: 0.45).
7645    #[serde(default = "default_max_top5")]
7646    pub top_5_concentration: f64,
7647
7648    /// Percentage of single-source vendors (default: 0.05).
7649    #[serde(default = "default_single_source_percent")]
7650    pub single_source_percent: f64,
7651}
7652
7653fn default_max_single_vendor() -> f64 {
7654    0.15
7655}
7656
7657fn default_max_top5() -> f64 {
7658    0.45
7659}
7660
7661fn default_single_source_percent() -> f64 {
7662    0.05
7663}
7664
7665impl Default for DependencySchemaConfig {
7666    fn default() -> Self {
7667        Self {
7668            max_single_vendor_concentration: 0.15,
7669            top_5_concentration: 0.45,
7670            single_source_percent: 0.05,
7671        }
7672    }
7673}
7674
7675// =============================================================================
7676// Customer Segmentation Configuration
7677// =============================================================================
7678
7679/// Configuration for customer segmentation generation.
7680#[derive(Debug, Clone, Default, Serialize, Deserialize)]
7681pub struct CustomerSegmentationSchemaConfig {
7682    /// Enable customer segmentation generation.
7683    #[serde(default)]
7684    pub enabled: bool,
7685
7686    /// Value segment distribution.
7687    #[serde(default)]
7688    pub value_segments: ValueSegmentsSchemaConfig,
7689
7690    /// Lifecycle stage configuration.
7691    #[serde(default)]
7692    pub lifecycle: LifecycleSchemaConfig,
7693
7694    /// Network (referrals, hierarchies) configuration.
7695    #[serde(default)]
7696    pub networks: CustomerNetworksSchemaConfig,
7697}
7698
7699/// Customer value segments distribution configuration.
7700#[derive(Debug, Clone, Serialize, Deserialize)]
7701pub struct ValueSegmentsSchemaConfig {
7702    /// Enterprise segment configuration.
7703    #[serde(default)]
7704    pub enterprise: SegmentDetailSchemaConfig,
7705
7706    /// Mid-market segment configuration.
7707    #[serde(default)]
7708    pub mid_market: SegmentDetailSchemaConfig,
7709
7710    /// SMB segment configuration.
7711    #[serde(default)]
7712    pub smb: SegmentDetailSchemaConfig,
7713
7714    /// Consumer segment configuration.
7715    #[serde(default)]
7716    pub consumer: SegmentDetailSchemaConfig,
7717}
7718
7719impl Default for ValueSegmentsSchemaConfig {
7720    fn default() -> Self {
7721        Self {
7722            enterprise: SegmentDetailSchemaConfig {
7723                revenue_share: 0.40,
7724                customer_share: 0.05,
7725                avg_order_value_range: "50000+".to_string(),
7726            },
7727            mid_market: SegmentDetailSchemaConfig {
7728                revenue_share: 0.35,
7729                customer_share: 0.20,
7730                avg_order_value_range: "5000-50000".to_string(),
7731            },
7732            smb: SegmentDetailSchemaConfig {
7733                revenue_share: 0.20,
7734                customer_share: 0.50,
7735                avg_order_value_range: "500-5000".to_string(),
7736            },
7737            consumer: SegmentDetailSchemaConfig {
7738                revenue_share: 0.05,
7739                customer_share: 0.25,
7740                avg_order_value_range: "50-500".to_string(),
7741            },
7742        }
7743    }
7744}
7745
7746/// Individual segment detail configuration.
7747#[derive(Debug, Clone, Serialize, Deserialize)]
7748pub struct SegmentDetailSchemaConfig {
7749    /// Revenue share for this segment.
7750    #[serde(default)]
7751    pub revenue_share: f64,
7752
7753    /// Customer share for this segment.
7754    #[serde(default)]
7755    pub customer_share: f64,
7756
7757    /// Average order value range (e.g., "5000-50000" or "50000+").
7758    #[serde(default)]
7759    pub avg_order_value_range: String,
7760}
7761
7762impl Default for SegmentDetailSchemaConfig {
7763    fn default() -> Self {
7764        Self {
7765            revenue_share: 0.25,
7766            customer_share: 0.25,
7767            avg_order_value_range: "1000-10000".to_string(),
7768        }
7769    }
7770}
7771
7772/// Customer lifecycle stage configuration.
7773#[derive(Debug, Clone, Serialize, Deserialize)]
7774pub struct LifecycleSchemaConfig {
7775    /// Prospect stage rate.
7776    #[serde(default)]
7777    pub prospect_rate: f64,
7778
7779    /// New customer stage rate.
7780    #[serde(default = "default_new_rate")]
7781    pub new_rate: f64,
7782
7783    /// Growth stage rate.
7784    #[serde(default = "default_growth_rate")]
7785    pub growth_rate: f64,
7786
7787    /// Mature stage rate.
7788    #[serde(default = "default_mature_rate")]
7789    pub mature_rate: f64,
7790
7791    /// At-risk stage rate.
7792    #[serde(default = "default_at_risk_rate")]
7793    pub at_risk_rate: f64,
7794
7795    /// Churned stage rate.
7796    #[serde(default = "default_churned_rate")]
7797    pub churned_rate: f64,
7798
7799    /// Won-back stage rate (churned customers reacquired).
7800    #[serde(default)]
7801    pub won_back_rate: f64,
7802}
7803
7804fn default_new_rate() -> f64 {
7805    0.10
7806}
7807
7808fn default_growth_rate() -> f64 {
7809    0.15
7810}
7811
7812fn default_mature_rate() -> f64 {
7813    0.60
7814}
7815
7816fn default_at_risk_rate() -> f64 {
7817    0.10
7818}
7819
7820fn default_churned_rate() -> f64 {
7821    0.05
7822}
7823
7824impl Default for LifecycleSchemaConfig {
7825    fn default() -> Self {
7826        Self {
7827            prospect_rate: 0.0,
7828            new_rate: 0.10,
7829            growth_rate: 0.15,
7830            mature_rate: 0.60,
7831            at_risk_rate: 0.10,
7832            churned_rate: 0.05,
7833            won_back_rate: 0.0,
7834        }
7835    }
7836}
7837
7838/// Customer networks configuration (referrals, hierarchies).
7839#[derive(Debug, Clone, Default, Serialize, Deserialize)]
7840pub struct CustomerNetworksSchemaConfig {
7841    /// Referral network configuration.
7842    #[serde(default)]
7843    pub referrals: ReferralSchemaConfig,
7844
7845    /// Corporate hierarchy configuration.
7846    #[serde(default)]
7847    pub corporate_hierarchies: HierarchySchemaConfig,
7848}
7849
7850/// Referral network configuration.
7851#[derive(Debug, Clone, Serialize, Deserialize)]
7852pub struct ReferralSchemaConfig {
7853    /// Enable referral generation.
7854    #[serde(default = "default_true")]
7855    pub enabled: bool,
7856
7857    /// Rate of customers acquired via referral.
7858    #[serde(default = "default_referral_rate")]
7859    pub referral_rate: f64,
7860}
7861
7862fn default_referral_rate() -> f64 {
7863    0.15
7864}
7865
7866impl Default for ReferralSchemaConfig {
7867    fn default() -> Self {
7868        Self {
7869            enabled: true,
7870            referral_rate: 0.15,
7871        }
7872    }
7873}
7874
7875/// Corporate hierarchy configuration.
7876#[derive(Debug, Clone, Serialize, Deserialize)]
7877pub struct HierarchySchemaConfig {
7878    /// Enable corporate hierarchy generation.
7879    #[serde(default = "default_true")]
7880    pub enabled: bool,
7881
7882    /// Rate of customers in hierarchies.
7883    #[serde(default = "default_hierarchy_rate")]
7884    pub probability: f64,
7885}
7886
7887fn default_hierarchy_rate() -> f64 {
7888    0.30
7889}
7890
7891impl Default for HierarchySchemaConfig {
7892    fn default() -> Self {
7893        Self {
7894            enabled: true,
7895            probability: 0.30,
7896        }
7897    }
7898}
7899
7900// =============================================================================
7901// Relationship Strength Configuration
7902// =============================================================================
7903
7904/// Configuration for relationship strength calculation.
7905#[derive(Debug, Clone, Default, Serialize, Deserialize)]
7906pub struct RelationshipStrengthSchemaConfig {
7907    /// Enable relationship strength calculation.
7908    #[serde(default)]
7909    pub enabled: bool,
7910
7911    /// Calculation weights.
7912    #[serde(default)]
7913    pub calculation: StrengthCalculationSchemaConfig,
7914
7915    /// Strength thresholds for classification.
7916    #[serde(default)]
7917    pub thresholds: StrengthThresholdsSchemaConfig,
7918}
7919
7920/// Strength calculation weights configuration.
7921#[derive(Debug, Clone, Serialize, Deserialize)]
7922pub struct StrengthCalculationSchemaConfig {
7923    /// Weight for transaction volume (default: 0.30).
7924    #[serde(default = "default_volume_weight")]
7925    pub transaction_volume_weight: f64,
7926
7927    /// Weight for transaction count (default: 0.25).
7928    #[serde(default = "default_count_weight")]
7929    pub transaction_count_weight: f64,
7930
7931    /// Weight for relationship duration (default: 0.20).
7932    #[serde(default = "default_duration_weight")]
7933    pub relationship_duration_weight: f64,
7934
7935    /// Weight for recency (default: 0.15).
7936    #[serde(default = "default_recency_weight")]
7937    pub recency_weight: f64,
7938
7939    /// Weight for mutual connections (default: 0.10).
7940    #[serde(default = "default_mutual_weight")]
7941    pub mutual_connections_weight: f64,
7942
7943    /// Recency half-life in days (default: 90).
7944    #[serde(default = "default_recency_half_life")]
7945    pub recency_half_life_days: u32,
7946}
7947
7948fn default_volume_weight() -> f64 {
7949    0.30
7950}
7951
7952fn default_count_weight() -> f64 {
7953    0.25
7954}
7955
7956fn default_duration_weight() -> f64 {
7957    0.20
7958}
7959
7960fn default_recency_weight() -> f64 {
7961    0.15
7962}
7963
7964fn default_mutual_weight() -> f64 {
7965    0.10
7966}
7967
7968fn default_recency_half_life() -> u32 {
7969    90
7970}
7971
7972impl Default for StrengthCalculationSchemaConfig {
7973    fn default() -> Self {
7974        Self {
7975            transaction_volume_weight: 0.30,
7976            transaction_count_weight: 0.25,
7977            relationship_duration_weight: 0.20,
7978            recency_weight: 0.15,
7979            mutual_connections_weight: 0.10,
7980            recency_half_life_days: 90,
7981        }
7982    }
7983}
7984
7985/// Strength thresholds for relationship classification.
7986#[derive(Debug, Clone, Serialize, Deserialize)]
7987pub struct StrengthThresholdsSchemaConfig {
7988    /// Threshold for strong relationships (default: 0.7).
7989    #[serde(default = "default_strong_threshold")]
7990    pub strong: f64,
7991
7992    /// Threshold for moderate relationships (default: 0.4).
7993    #[serde(default = "default_moderate_threshold")]
7994    pub moderate: f64,
7995
7996    /// Threshold for weak relationships (default: 0.1).
7997    #[serde(default = "default_weak_threshold")]
7998    pub weak: f64,
7999}
8000
8001fn default_strong_threshold() -> f64 {
8002    0.7
8003}
8004
8005fn default_moderate_threshold() -> f64 {
8006    0.4
8007}
8008
8009fn default_weak_threshold() -> f64 {
8010    0.1
8011}
8012
8013impl Default for StrengthThresholdsSchemaConfig {
8014    fn default() -> Self {
8015        Self {
8016            strong: 0.7,
8017            moderate: 0.4,
8018            weak: 0.1,
8019        }
8020    }
8021}
8022
8023// =============================================================================
8024// Cross-Process Links Configuration
8025// =============================================================================
8026
8027/// Configuration for cross-process linkages.
8028#[derive(Debug, Clone, Serialize, Deserialize)]
8029pub struct CrossProcessLinksSchemaConfig {
8030    /// Enable cross-process link generation.
8031    #[serde(default)]
8032    pub enabled: bool,
8033
8034    /// Enable inventory links between P2P and O2C.
8035    #[serde(default = "default_true")]
8036    pub inventory_p2p_o2c: bool,
8037
8038    /// Enable payment to bank reconciliation links.
8039    #[serde(default = "default_true")]
8040    pub payment_bank_reconciliation: bool,
8041
8042    /// Enable intercompany bilateral matching.
8043    #[serde(default = "default_true")]
8044    pub intercompany_bilateral: bool,
8045
8046    /// Percentage of GR/Deliveries to link via inventory (0.0 - 1.0).
8047    #[serde(default = "default_inventory_link_rate")]
8048    pub inventory_link_rate: f64,
8049}
8050
8051fn default_inventory_link_rate() -> f64 {
8052    0.30
8053}
8054
8055impl Default for CrossProcessLinksSchemaConfig {
8056    fn default() -> Self {
8057        Self {
8058            enabled: false,
8059            inventory_p2p_o2c: true,
8060            payment_bank_reconciliation: true,
8061            intercompany_bilateral: true,
8062            inventory_link_rate: 0.30,
8063        }
8064    }
8065}
8066
8067// =============================================================================
8068// Organizational Events Configuration
8069// =============================================================================
8070
8071/// Configuration for organizational events (acquisitions, divestitures, etc.).
8072#[derive(Debug, Clone, Serialize, Deserialize, Default)]
8073pub struct OrganizationalEventsSchemaConfig {
8074    /// Enable organizational events.
8075    #[serde(default)]
8076    pub enabled: bool,
8077
8078    /// Effect blending mode (multiplicative, additive, maximum, minimum).
8079    #[serde(default)]
8080    pub effect_blending: EffectBlendingModeConfig,
8081
8082    /// Organizational events (acquisitions, divestitures, reorganizations, etc.).
8083    #[serde(default)]
8084    pub events: Vec<OrganizationalEventSchemaConfig>,
8085
8086    /// Process evolution events.
8087    #[serde(default)]
8088    pub process_evolution: Vec<ProcessEvolutionSchemaConfig>,
8089
8090    /// Technology transition events.
8091    #[serde(default)]
8092    pub technology_transitions: Vec<TechnologyTransitionSchemaConfig>,
8093}
8094
8095/// Effect blending mode for combining multiple event effects.
8096#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
8097#[serde(rename_all = "snake_case")]
8098pub enum EffectBlendingModeConfig {
8099    /// Multiply effects together.
8100    #[default]
8101    Multiplicative,
8102    /// Add effects together.
8103    Additive,
8104    /// Take the maximum effect.
8105    Maximum,
8106    /// Take the minimum effect.
8107    Minimum,
8108}
8109
8110/// Configuration for a single organizational event.
8111#[derive(Debug, Clone, Serialize, Deserialize)]
8112pub struct OrganizationalEventSchemaConfig {
8113    /// Event ID.
8114    pub id: String,
8115
8116    /// Event type and configuration.
8117    pub event_type: OrganizationalEventTypeSchemaConfig,
8118
8119    /// Effective date.
8120    pub effective_date: String,
8121
8122    /// Transition duration in months.
8123    #[serde(default = "default_org_transition_months")]
8124    pub transition_months: u32,
8125
8126    /// Description.
8127    #[serde(default)]
8128    pub description: Option<String>,
8129}
8130
8131fn default_org_transition_months() -> u32 {
8132    6
8133}
8134
8135/// Organizational event type configuration.
8136#[derive(Debug, Clone, Serialize, Deserialize)]
8137#[serde(tag = "type", rename_all = "snake_case")]
8138pub enum OrganizationalEventTypeSchemaConfig {
8139    /// Acquisition event.
8140    Acquisition {
8141        /// Acquired entity code.
8142        acquired_entity: String,
8143        /// Volume increase multiplier.
8144        #[serde(default = "default_acquisition_volume")]
8145        volume_increase: f64,
8146        /// Integration error rate.
8147        #[serde(default = "default_acquisition_error")]
8148        integration_error_rate: f64,
8149        /// Parallel posting days.
8150        #[serde(default = "default_parallel_days")]
8151        parallel_posting_days: u32,
8152    },
8153    /// Divestiture event.
8154    Divestiture {
8155        /// Divested entity code.
8156        divested_entity: String,
8157        /// Volume reduction factor.
8158        #[serde(default = "default_divestiture_volume")]
8159        volume_reduction: f64,
8160        /// Remove entity from generation.
8161        #[serde(default = "default_true_val")]
8162        remove_entity: bool,
8163    },
8164    /// Reorganization event.
8165    Reorganization {
8166        /// Cost center remapping.
8167        #[serde(default)]
8168        cost_center_remapping: std::collections::HashMap<String, String>,
8169        /// Transition error rate.
8170        #[serde(default = "default_reorg_error")]
8171        transition_error_rate: f64,
8172    },
8173    /// Leadership change event.
8174    LeadershipChange {
8175        /// Role that changed.
8176        role: String,
8177        /// Policy changes.
8178        #[serde(default)]
8179        policy_changes: Vec<String>,
8180    },
8181    /// Workforce reduction event.
8182    WorkforceReduction {
8183        /// Reduction percentage.
8184        #[serde(default = "default_workforce_reduction")]
8185        reduction_percent: f64,
8186        /// Error rate increase.
8187        #[serde(default = "default_workforce_error")]
8188        error_rate_increase: f64,
8189    },
8190    /// Merger event.
8191    Merger {
8192        /// Merged entity code.
8193        merged_entity: String,
8194        /// Volume increase multiplier.
8195        #[serde(default = "default_merger_volume")]
8196        volume_increase: f64,
8197    },
8198}
8199
8200fn default_acquisition_volume() -> f64 {
8201    1.35
8202}
8203
8204fn default_acquisition_error() -> f64 {
8205    0.05
8206}
8207
8208fn default_parallel_days() -> u32 {
8209    30
8210}
8211
8212fn default_divestiture_volume() -> f64 {
8213    0.70
8214}
8215
8216fn default_true_val() -> bool {
8217    true
8218}
8219
8220fn default_reorg_error() -> f64 {
8221    0.04
8222}
8223
8224fn default_workforce_reduction() -> f64 {
8225    0.10
8226}
8227
8228fn default_workforce_error() -> f64 {
8229    0.05
8230}
8231
8232fn default_merger_volume() -> f64 {
8233    1.80
8234}
8235
8236/// Configuration for a process evolution event.
8237#[derive(Debug, Clone, Serialize, Deserialize)]
8238pub struct ProcessEvolutionSchemaConfig {
8239    /// Event ID.
8240    pub id: String,
8241
8242    /// Event type.
8243    pub event_type: ProcessEvolutionTypeSchemaConfig,
8244
8245    /// Effective date.
8246    pub effective_date: String,
8247
8248    /// Description.
8249    #[serde(default)]
8250    pub description: Option<String>,
8251}
8252
8253/// Process evolution type configuration.
8254#[derive(Debug, Clone, Serialize, Deserialize)]
8255#[serde(tag = "type", rename_all = "snake_case")]
8256pub enum ProcessEvolutionTypeSchemaConfig {
8257    /// Process automation.
8258    ProcessAutomation {
8259        /// Process name.
8260        process_name: String,
8261        /// Manual rate before.
8262        #[serde(default = "default_manual_before")]
8263        manual_rate_before: f64,
8264        /// Manual rate after.
8265        #[serde(default = "default_manual_after")]
8266        manual_rate_after: f64,
8267    },
8268    /// Approval workflow change.
8269    ApprovalWorkflowChange {
8270        /// Description.
8271        description: String,
8272    },
8273    /// Control enhancement.
8274    ControlEnhancement {
8275        /// Control ID.
8276        control_id: String,
8277        /// Error reduction.
8278        #[serde(default = "default_error_reduction")]
8279        error_reduction: f64,
8280    },
8281}
8282
8283fn default_manual_before() -> f64 {
8284    0.80
8285}
8286
8287fn default_manual_after() -> f64 {
8288    0.15
8289}
8290
8291fn default_error_reduction() -> f64 {
8292    0.02
8293}
8294
8295/// Configuration for a technology transition event.
8296#[derive(Debug, Clone, Serialize, Deserialize)]
8297pub struct TechnologyTransitionSchemaConfig {
8298    /// Event ID.
8299    pub id: String,
8300
8301    /// Event type.
8302    pub event_type: TechnologyTransitionTypeSchemaConfig,
8303
8304    /// Description.
8305    #[serde(default)]
8306    pub description: Option<String>,
8307}
8308
8309/// Technology transition type configuration.
8310#[derive(Debug, Clone, Serialize, Deserialize)]
8311#[serde(tag = "type", rename_all = "snake_case")]
8312pub enum TechnologyTransitionTypeSchemaConfig {
8313    /// ERP migration.
8314    ErpMigration {
8315        /// Source system.
8316        source_system: String,
8317        /// Target system.
8318        target_system: String,
8319        /// Cutover date.
8320        cutover_date: String,
8321        /// Stabilization end date.
8322        stabilization_end: String,
8323        /// Duplicate rate during migration.
8324        #[serde(default = "default_erp_duplicate_rate")]
8325        duplicate_rate: f64,
8326        /// Format mismatch rate.
8327        #[serde(default = "default_format_mismatch")]
8328        format_mismatch_rate: f64,
8329    },
8330    /// Module implementation.
8331    ModuleImplementation {
8332        /// Module name.
8333        module_name: String,
8334        /// Go-live date.
8335        go_live_date: String,
8336    },
8337}
8338
8339fn default_erp_duplicate_rate() -> f64 {
8340    0.02
8341}
8342
8343fn default_format_mismatch() -> f64 {
8344    0.03
8345}
8346
8347// =============================================================================
8348// Behavioral Drift Configuration
8349// =============================================================================
8350
8351/// Configuration for behavioral drift (vendor, customer, employee behavior).
8352///
8353/// **Deprecated (v4.1.2):** this schema section is currently
8354/// validated-but-inert — no runtime code consumes its fields. Users
8355/// who want behavioral drift-style effects should reach for
8356/// `distributions.regime_changes` (v3.5.2+), which drives the
8357/// `DriftController` via the parameter-drift path. The schema type
8358/// remains for backward-compatible YAML loading; it will be removed
8359/// in a future major version once `regime_changes` gains per-entity
8360/// (vendor / customer / employee) targeting.
8361#[derive(Debug, Clone, Serialize, Deserialize, Default)]
8362pub struct BehavioralDriftSchemaConfig {
8363    /// Enable behavioral drift.
8364    #[serde(default)]
8365    pub enabled: bool,
8366
8367    /// Vendor behavior drift.
8368    #[serde(default)]
8369    pub vendor_behavior: VendorBehaviorSchemaConfig,
8370
8371    /// Customer behavior drift.
8372    #[serde(default)]
8373    pub customer_behavior: CustomerBehaviorSchemaConfig,
8374
8375    /// Employee behavior drift.
8376    #[serde(default)]
8377    pub employee_behavior: EmployeeBehaviorSchemaConfig,
8378
8379    /// Collective behavior drift.
8380    #[serde(default)]
8381    pub collective: CollectiveBehaviorSchemaConfig,
8382}
8383
8384/// Vendor behavior drift configuration.
8385#[derive(Debug, Clone, Default, Serialize, Deserialize)]
8386pub struct VendorBehaviorSchemaConfig {
8387    /// Payment terms drift.
8388    #[serde(default)]
8389    pub payment_terms_drift: PaymentTermsDriftSchemaConfig,
8390
8391    /// Quality drift.
8392    #[serde(default)]
8393    pub quality_drift: QualityDriftSchemaConfig,
8394}
8395
8396/// Payment terms drift configuration.
8397#[derive(Debug, Clone, Serialize, Deserialize)]
8398pub struct PaymentTermsDriftSchemaConfig {
8399    /// Extension rate per year (days).
8400    #[serde(default = "default_extension_rate")]
8401    pub extension_rate_per_year: f64,
8402
8403    /// Economic sensitivity.
8404    #[serde(default = "default_economic_sensitivity")]
8405    pub economic_sensitivity: f64,
8406}
8407
8408fn default_extension_rate() -> f64 {
8409    2.5
8410}
8411
8412fn default_economic_sensitivity() -> f64 {
8413    1.0
8414}
8415
8416impl Default for PaymentTermsDriftSchemaConfig {
8417    fn default() -> Self {
8418        Self {
8419            extension_rate_per_year: 2.5,
8420            economic_sensitivity: 1.0,
8421        }
8422    }
8423}
8424
8425/// Quality drift configuration.
8426#[derive(Debug, Clone, Serialize, Deserialize)]
8427pub struct QualityDriftSchemaConfig {
8428    /// New vendor improvement rate (per year).
8429    #[serde(default = "default_improvement_rate")]
8430    pub new_vendor_improvement_rate: f64,
8431
8432    /// Complacency decline rate (per year after first year).
8433    #[serde(default = "default_decline_rate")]
8434    pub complacency_decline_rate: f64,
8435}
8436
8437fn default_improvement_rate() -> f64 {
8438    0.02
8439}
8440
8441fn default_decline_rate() -> f64 {
8442    0.01
8443}
8444
8445impl Default for QualityDriftSchemaConfig {
8446    fn default() -> Self {
8447        Self {
8448            new_vendor_improvement_rate: 0.02,
8449            complacency_decline_rate: 0.01,
8450        }
8451    }
8452}
8453
8454/// Customer behavior drift configuration.
8455#[derive(Debug, Clone, Default, Serialize, Deserialize)]
8456pub struct CustomerBehaviorSchemaConfig {
8457    /// Payment drift.
8458    #[serde(default)]
8459    pub payment_drift: CustomerPaymentDriftSchemaConfig,
8460
8461    /// Order drift.
8462    #[serde(default)]
8463    pub order_drift: OrderDriftSchemaConfig,
8464}
8465
8466/// Customer payment drift configuration.
8467#[derive(Debug, Clone, Serialize, Deserialize)]
8468pub struct CustomerPaymentDriftSchemaConfig {
8469    /// Days extension during downturn (min, max).
8470    #[serde(default = "default_downturn_extension")]
8471    pub downturn_days_extension: (u32, u32),
8472
8473    /// Bad debt increase during downturn.
8474    #[serde(default = "default_bad_debt_increase")]
8475    pub downturn_bad_debt_increase: f64,
8476}
8477
8478fn default_downturn_extension() -> (u32, u32) {
8479    (5, 15)
8480}
8481
8482fn default_bad_debt_increase() -> f64 {
8483    0.02
8484}
8485
8486impl Default for CustomerPaymentDriftSchemaConfig {
8487    fn default() -> Self {
8488        Self {
8489            downturn_days_extension: (5, 15),
8490            downturn_bad_debt_increase: 0.02,
8491        }
8492    }
8493}
8494
8495/// Order drift configuration.
8496#[derive(Debug, Clone, Serialize, Deserialize)]
8497pub struct OrderDriftSchemaConfig {
8498    /// Digital shift rate (per year).
8499    #[serde(default = "default_digital_shift")]
8500    pub digital_shift_rate: f64,
8501}
8502
8503fn default_digital_shift() -> f64 {
8504    0.05
8505}
8506
8507impl Default for OrderDriftSchemaConfig {
8508    fn default() -> Self {
8509        Self {
8510            digital_shift_rate: 0.05,
8511        }
8512    }
8513}
8514
8515/// Employee behavior drift configuration.
8516#[derive(Debug, Clone, Default, Serialize, Deserialize)]
8517pub struct EmployeeBehaviorSchemaConfig {
8518    /// Approval drift.
8519    #[serde(default)]
8520    pub approval_drift: ApprovalDriftSchemaConfig,
8521
8522    /// Error drift.
8523    #[serde(default)]
8524    pub error_drift: ErrorDriftSchemaConfig,
8525}
8526
8527/// Approval drift configuration.
8528#[derive(Debug, Clone, Serialize, Deserialize)]
8529pub struct ApprovalDriftSchemaConfig {
8530    /// EOM intensity increase per year.
8531    #[serde(default = "default_eom_intensity")]
8532    pub eom_intensity_increase_per_year: f64,
8533
8534    /// Rubber stamp volume threshold.
8535    #[serde(default = "default_rubber_stamp")]
8536    pub rubber_stamp_volume_threshold: u32,
8537}
8538
8539fn default_eom_intensity() -> f64 {
8540    0.05
8541}
8542
8543fn default_rubber_stamp() -> u32 {
8544    50
8545}
8546
8547impl Default for ApprovalDriftSchemaConfig {
8548    fn default() -> Self {
8549        Self {
8550            eom_intensity_increase_per_year: 0.05,
8551            rubber_stamp_volume_threshold: 50,
8552        }
8553    }
8554}
8555
8556/// Error drift configuration.
8557#[derive(Debug, Clone, Serialize, Deserialize)]
8558pub struct ErrorDriftSchemaConfig {
8559    /// New employee error rate.
8560    #[serde(default = "default_new_error")]
8561    pub new_employee_error_rate: f64,
8562
8563    /// Learning curve months.
8564    #[serde(default = "default_learning_months")]
8565    pub learning_curve_months: u32,
8566}
8567
8568fn default_new_error() -> f64 {
8569    0.08
8570}
8571
8572fn default_learning_months() -> u32 {
8573    6
8574}
8575
8576impl Default for ErrorDriftSchemaConfig {
8577    fn default() -> Self {
8578        Self {
8579            new_employee_error_rate: 0.08,
8580            learning_curve_months: 6,
8581        }
8582    }
8583}
8584
8585/// Collective behavior drift configuration.
8586#[derive(Debug, Clone, Serialize, Deserialize, Default)]
8587pub struct CollectiveBehaviorSchemaConfig {
8588    /// Automation adoption configuration.
8589    #[serde(default)]
8590    pub automation_adoption: AutomationAdoptionSchemaConfig,
8591}
8592
8593/// Automation adoption configuration.
8594#[derive(Debug, Clone, Serialize, Deserialize)]
8595pub struct AutomationAdoptionSchemaConfig {
8596    /// Enable S-curve adoption model.
8597    #[serde(default)]
8598    pub s_curve_enabled: bool,
8599
8600    /// Adoption midpoint in months.
8601    #[serde(default = "default_midpoint")]
8602    pub adoption_midpoint_months: u32,
8603
8604    /// Steepness of adoption curve.
8605    #[serde(default = "default_steepness")]
8606    pub steepness: f64,
8607}
8608
8609fn default_midpoint() -> u32 {
8610    24
8611}
8612
8613fn default_steepness() -> f64 {
8614    0.15
8615}
8616
8617impl Default for AutomationAdoptionSchemaConfig {
8618    fn default() -> Self {
8619        Self {
8620            s_curve_enabled: false,
8621            adoption_midpoint_months: 24,
8622            steepness: 0.15,
8623        }
8624    }
8625}
8626
8627// =============================================================================
8628// Market Drift Configuration
8629// =============================================================================
8630
8631/// Configuration for market drift (economic cycles, commodities, price shocks).
8632///
8633/// **Deprecated (v4.1.2):** validated-but-inert. Use
8634/// `distributions.regime_changes.economic_cycle` +
8635/// `distributions.regime_changes.parameter_drifts` for the
8636/// equivalent runtime behaviour (shipped in v3.5.2). The schema
8637/// type remains for backward-compatible YAML loading; will be
8638/// removed in v5.0.
8639#[derive(Debug, Clone, Serialize, Deserialize, Default)]
8640pub struct MarketDriftSchemaConfig {
8641    /// Enable market drift.
8642    #[serde(default)]
8643    pub enabled: bool,
8644
8645    /// Economic cycle configuration.
8646    #[serde(default)]
8647    pub economic_cycle: MarketEconomicCycleSchemaConfig,
8648
8649    /// Industry-specific cycles.
8650    #[serde(default)]
8651    pub industry_cycles: std::collections::HashMap<String, IndustryCycleSchemaConfig>,
8652
8653    /// Commodity drift configuration.
8654    #[serde(default)]
8655    pub commodities: CommoditiesSchemaConfig,
8656}
8657
8658/// Market economic cycle configuration.
8659#[derive(Debug, Clone, Serialize, Deserialize)]
8660pub struct MarketEconomicCycleSchemaConfig {
8661    /// Enable economic cycle.
8662    #[serde(default)]
8663    pub enabled: bool,
8664
8665    /// Cycle type.
8666    #[serde(default)]
8667    pub cycle_type: CycleTypeSchemaConfig,
8668
8669    /// Cycle period in months.
8670    #[serde(default = "default_market_cycle_period")]
8671    pub period_months: u32,
8672
8673    /// Amplitude.
8674    #[serde(default = "default_market_amplitude")]
8675    pub amplitude: f64,
8676
8677    /// Recession configuration.
8678    #[serde(default)]
8679    pub recession: RecessionSchemaConfig,
8680}
8681
8682fn default_market_cycle_period() -> u32 {
8683    48
8684}
8685
8686fn default_market_amplitude() -> f64 {
8687    0.15
8688}
8689
8690impl Default for MarketEconomicCycleSchemaConfig {
8691    fn default() -> Self {
8692        Self {
8693            enabled: false,
8694            cycle_type: CycleTypeSchemaConfig::Sinusoidal,
8695            period_months: 48,
8696            amplitude: 0.15,
8697            recession: RecessionSchemaConfig::default(),
8698        }
8699    }
8700}
8701
8702/// Cycle type configuration.
8703#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
8704#[serde(rename_all = "snake_case")]
8705pub enum CycleTypeSchemaConfig {
8706    /// Sinusoidal cycle.
8707    #[default]
8708    Sinusoidal,
8709    /// Asymmetric cycle.
8710    Asymmetric,
8711    /// Mean-reverting cycle.
8712    MeanReverting,
8713}
8714
8715/// Recession configuration.
8716#[derive(Debug, Clone, Serialize, Deserialize)]
8717pub struct RecessionSchemaConfig {
8718    /// Enable recession simulation.
8719    #[serde(default)]
8720    pub enabled: bool,
8721
8722    /// Probability per year.
8723    #[serde(default = "default_recession_prob")]
8724    pub probability_per_year: f64,
8725
8726    /// Severity.
8727    #[serde(default)]
8728    pub severity: RecessionSeveritySchemaConfig,
8729
8730    /// Specific recession periods.
8731    #[serde(default)]
8732    pub recession_periods: Vec<RecessionPeriodSchemaConfig>,
8733}
8734
8735fn default_recession_prob() -> f64 {
8736    0.10
8737}
8738
8739impl Default for RecessionSchemaConfig {
8740    fn default() -> Self {
8741        Self {
8742            enabled: false,
8743            probability_per_year: 0.10,
8744            severity: RecessionSeveritySchemaConfig::Moderate,
8745            recession_periods: Vec::new(),
8746        }
8747    }
8748}
8749
8750/// Recession severity configuration.
8751#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
8752#[serde(rename_all = "snake_case")]
8753pub enum RecessionSeveritySchemaConfig {
8754    /// Mild recession.
8755    Mild,
8756    /// Moderate recession.
8757    #[default]
8758    Moderate,
8759    /// Severe recession.
8760    Severe,
8761}
8762
8763/// Recession period configuration.
8764#[derive(Debug, Clone, Serialize, Deserialize)]
8765pub struct RecessionPeriodSchemaConfig {
8766    /// Start month.
8767    pub start_month: u32,
8768    /// Duration in months.
8769    pub duration_months: u32,
8770}
8771
8772/// Industry cycle configuration.
8773#[derive(Debug, Clone, Serialize, Deserialize)]
8774pub struct IndustryCycleSchemaConfig {
8775    /// Period in months.
8776    #[serde(default = "default_industry_period")]
8777    pub period_months: u32,
8778
8779    /// Amplitude.
8780    #[serde(default = "default_industry_amp")]
8781    pub amplitude: f64,
8782}
8783
8784fn default_industry_period() -> u32 {
8785    36
8786}
8787
8788fn default_industry_amp() -> f64 {
8789    0.20
8790}
8791
8792/// Commodities drift configuration.
8793#[derive(Debug, Clone, Serialize, Deserialize, Default)]
8794pub struct CommoditiesSchemaConfig {
8795    /// Enable commodity drift.
8796    #[serde(default)]
8797    pub enabled: bool,
8798
8799    /// Commodity items.
8800    #[serde(default)]
8801    pub items: Vec<CommodityItemSchemaConfig>,
8802}
8803
8804/// Commodity item configuration.
8805#[derive(Debug, Clone, Serialize, Deserialize)]
8806pub struct CommodityItemSchemaConfig {
8807    /// Commodity name.
8808    pub name: String,
8809
8810    /// Volatility.
8811    #[serde(default = "default_volatility")]
8812    pub volatility: f64,
8813
8814    /// COGS pass-through.
8815    #[serde(default)]
8816    pub cogs_pass_through: f64,
8817
8818    /// Overhead pass-through.
8819    #[serde(default)]
8820    pub overhead_pass_through: f64,
8821}
8822
8823fn default_volatility() -> f64 {
8824    0.20
8825}
8826
8827// =============================================================================
8828// Drift Labeling Configuration
8829// =============================================================================
8830
8831/// Configuration for drift ground truth labeling.
8832///
8833/// **Deprecated (v4.1.2):** validated-but-inert. The v3.3.0
8834/// analytics-metadata phase (`DriftEventGenerator` +
8835/// `AnalyticsMetadataSnapshot.drift_events`) produces drift labels
8836/// at runtime — configure it via `analytics_metadata.drift_events`
8837/// instead. The schema type remains for backward-compatible YAML
8838/// loading; will be removed in v5.0.
8839#[derive(Debug, Clone, Serialize, Deserialize, Default)]
8840pub struct DriftLabelingSchemaConfig {
8841    /// Enable drift labeling.
8842    #[serde(default)]
8843    pub enabled: bool,
8844
8845    /// Statistical drift labeling.
8846    #[serde(default)]
8847    pub statistical: StatisticalDriftLabelingSchemaConfig,
8848
8849    /// Categorical drift labeling.
8850    #[serde(default)]
8851    pub categorical: CategoricalDriftLabelingSchemaConfig,
8852
8853    /// Temporal drift labeling.
8854    #[serde(default)]
8855    pub temporal: TemporalDriftLabelingSchemaConfig,
8856
8857    /// Regulatory calendar preset.
8858    #[serde(default)]
8859    pub regulatory_calendar_preset: Option<String>,
8860}
8861
8862/// Statistical drift labeling configuration.
8863#[derive(Debug, Clone, Serialize, Deserialize)]
8864pub struct StatisticalDriftLabelingSchemaConfig {
8865    /// Enable statistical drift labeling.
8866    #[serde(default = "default_true_val")]
8867    pub enabled: bool,
8868
8869    /// Minimum magnitude threshold.
8870    #[serde(default = "default_min_magnitude")]
8871    pub min_magnitude_threshold: f64,
8872}
8873
8874fn default_min_magnitude() -> f64 {
8875    0.05
8876}
8877
8878impl Default for StatisticalDriftLabelingSchemaConfig {
8879    fn default() -> Self {
8880        Self {
8881            enabled: true,
8882            min_magnitude_threshold: 0.05,
8883        }
8884    }
8885}
8886
8887/// Categorical drift labeling configuration.
8888#[derive(Debug, Clone, Serialize, Deserialize)]
8889pub struct CategoricalDriftLabelingSchemaConfig {
8890    /// Enable categorical drift labeling.
8891    #[serde(default = "default_true_val")]
8892    pub enabled: bool,
8893}
8894
8895impl Default for CategoricalDriftLabelingSchemaConfig {
8896    fn default() -> Self {
8897        Self { enabled: true }
8898    }
8899}
8900
8901/// Temporal drift labeling configuration.
8902#[derive(Debug, Clone, Serialize, Deserialize)]
8903pub struct TemporalDriftLabelingSchemaConfig {
8904    /// Enable temporal drift labeling.
8905    #[serde(default = "default_true_val")]
8906    pub enabled: bool,
8907}
8908
8909impl Default for TemporalDriftLabelingSchemaConfig {
8910    fn default() -> Self {
8911        Self { enabled: true }
8912    }
8913}
8914
8915// =============================================================================
8916// Enhanced Anomaly Injection Configuration
8917// =============================================================================
8918
8919/// Enhanced anomaly injection configuration.
8920///
8921/// Provides comprehensive anomaly injection capabilities including:
8922/// - Multi-stage fraud schemes (embezzlement, revenue manipulation, kickbacks)
8923/// - Correlated anomaly injection (co-occurrence patterns, error cascades)
8924/// - Near-miss generation for false positive reduction
8925/// - Detection difficulty classification
8926/// - Context-aware injection based on entity behavior
8927#[derive(Debug, Clone, Serialize, Deserialize, Default)]
8928pub struct EnhancedAnomalyConfig {
8929    /// Enable enhanced anomaly injection.
8930    #[serde(default)]
8931    pub enabled: bool,
8932
8933    /// Base anomaly rates.
8934    #[serde(default)]
8935    pub rates: AnomalyRateConfig,
8936
8937    /// Multi-stage fraud scheme configuration.
8938    #[serde(default)]
8939    pub multi_stage_schemes: MultiStageSchemeConfig,
8940
8941    /// Correlated anomaly injection configuration.
8942    #[serde(default)]
8943    pub correlated_injection: CorrelatedInjectionConfig,
8944
8945    /// Near-miss generation configuration.
8946    #[serde(default)]
8947    pub near_miss: NearMissConfig,
8948
8949    /// Detection difficulty classification configuration.
8950    #[serde(default)]
8951    pub difficulty_classification: DifficultyClassificationConfig,
8952
8953    /// Context-aware injection configuration.
8954    #[serde(default)]
8955    pub context_aware: ContextAwareConfig,
8956
8957    /// Enhanced labeling configuration.
8958    #[serde(default)]
8959    pub labeling: EnhancedLabelingConfig,
8960}
8961
8962/// Base anomaly rate configuration.
8963#[derive(Debug, Clone, Serialize, Deserialize)]
8964pub struct AnomalyRateConfig {
8965    /// Total anomaly rate (0.0 to 1.0).
8966    #[serde(default = "default_total_anomaly_rate")]
8967    pub total_rate: f64,
8968
8969    /// Fraud anomaly rate.
8970    #[serde(default = "default_fraud_anomaly_rate")]
8971    pub fraud_rate: f64,
8972
8973    /// Error anomaly rate.
8974    #[serde(default = "default_error_anomaly_rate")]
8975    pub error_rate: f64,
8976
8977    /// Process issue rate.
8978    #[serde(default = "default_process_anomaly_rate")]
8979    pub process_rate: f64,
8980}
8981
8982fn default_total_anomaly_rate() -> f64 {
8983    0.03
8984}
8985fn default_fraud_anomaly_rate() -> f64 {
8986    0.01
8987}
8988fn default_error_anomaly_rate() -> f64 {
8989    0.015
8990}
8991fn default_process_anomaly_rate() -> f64 {
8992    0.005
8993}
8994
8995impl Default for AnomalyRateConfig {
8996    fn default() -> Self {
8997        Self {
8998            total_rate: default_total_anomaly_rate(),
8999            fraud_rate: default_fraud_anomaly_rate(),
9000            error_rate: default_error_anomaly_rate(),
9001            process_rate: default_process_anomaly_rate(),
9002        }
9003    }
9004}
9005
9006/// Multi-stage fraud scheme configuration.
9007#[derive(Debug, Clone, Serialize, Deserialize, Default)]
9008pub struct MultiStageSchemeConfig {
9009    /// Enable multi-stage fraud schemes.
9010    #[serde(default)]
9011    pub enabled: bool,
9012
9013    /// Embezzlement scheme configuration.
9014    #[serde(default)]
9015    pub embezzlement: EmbezzlementSchemeConfig,
9016
9017    /// Revenue manipulation scheme configuration.
9018    #[serde(default)]
9019    pub revenue_manipulation: RevenueManipulationSchemeConfig,
9020
9021    /// Vendor kickback scheme configuration.
9022    #[serde(default)]
9023    pub kickback: KickbackSchemeConfig,
9024}
9025
9026/// Embezzlement scheme configuration.
9027#[derive(Debug, Clone, Serialize, Deserialize)]
9028pub struct EmbezzlementSchemeConfig {
9029    /// Probability of starting an embezzlement scheme per perpetrator per year.
9030    #[serde(default = "default_embezzlement_probability")]
9031    pub probability: f64,
9032
9033    /// Testing stage configuration.
9034    #[serde(default)]
9035    pub testing_stage: SchemeStageConfig,
9036
9037    /// Escalation stage configuration.
9038    #[serde(default)]
9039    pub escalation_stage: SchemeStageConfig,
9040
9041    /// Acceleration stage configuration.
9042    #[serde(default)]
9043    pub acceleration_stage: SchemeStageConfig,
9044
9045    /// Desperation stage configuration.
9046    #[serde(default)]
9047    pub desperation_stage: SchemeStageConfig,
9048}
9049
9050fn default_embezzlement_probability() -> f64 {
9051    0.02
9052}
9053
9054impl Default for EmbezzlementSchemeConfig {
9055    fn default() -> Self {
9056        Self {
9057            probability: default_embezzlement_probability(),
9058            testing_stage: SchemeStageConfig {
9059                duration_months: 2,
9060                amount_min: 100.0,
9061                amount_max: 500.0,
9062                transaction_count_min: 2,
9063                transaction_count_max: 5,
9064                difficulty: "hard".to_string(),
9065            },
9066            escalation_stage: SchemeStageConfig {
9067                duration_months: 6,
9068                amount_min: 500.0,
9069                amount_max: 2000.0,
9070                transaction_count_min: 3,
9071                transaction_count_max: 8,
9072                difficulty: "moderate".to_string(),
9073            },
9074            acceleration_stage: SchemeStageConfig {
9075                duration_months: 3,
9076                amount_min: 2000.0,
9077                amount_max: 10000.0,
9078                transaction_count_min: 5,
9079                transaction_count_max: 12,
9080                difficulty: "easy".to_string(),
9081            },
9082            desperation_stage: SchemeStageConfig {
9083                duration_months: 1,
9084                amount_min: 10000.0,
9085                amount_max: 50000.0,
9086                transaction_count_min: 3,
9087                transaction_count_max: 6,
9088                difficulty: "trivial".to_string(),
9089            },
9090        }
9091    }
9092}
9093
9094/// Revenue manipulation scheme configuration.
9095#[derive(Debug, Clone, Serialize, Deserialize)]
9096pub struct RevenueManipulationSchemeConfig {
9097    /// Probability of starting a revenue manipulation scheme per period.
9098    #[serde(default = "default_revenue_manipulation_probability")]
9099    pub probability: f64,
9100
9101    /// Early revenue recognition inflation target (Q4).
9102    #[serde(default = "default_early_recognition_target")]
9103    pub early_recognition_target: f64,
9104
9105    /// Expense deferral inflation target (Q1).
9106    #[serde(default = "default_expense_deferral_target")]
9107    pub expense_deferral_target: f64,
9108
9109    /// Reserve release inflation target (Q2).
9110    #[serde(default = "default_reserve_release_target")]
9111    pub reserve_release_target: f64,
9112
9113    /// Channel stuffing inflation target (Q4).
9114    #[serde(default = "default_channel_stuffing_target")]
9115    pub channel_stuffing_target: f64,
9116}
9117
9118fn default_revenue_manipulation_probability() -> f64 {
9119    0.01
9120}
9121fn default_early_recognition_target() -> f64 {
9122    0.02
9123}
9124fn default_expense_deferral_target() -> f64 {
9125    0.03
9126}
9127fn default_reserve_release_target() -> f64 {
9128    0.02
9129}
9130fn default_channel_stuffing_target() -> f64 {
9131    0.05
9132}
9133
9134impl Default for RevenueManipulationSchemeConfig {
9135    fn default() -> Self {
9136        Self {
9137            probability: default_revenue_manipulation_probability(),
9138            early_recognition_target: default_early_recognition_target(),
9139            expense_deferral_target: default_expense_deferral_target(),
9140            reserve_release_target: default_reserve_release_target(),
9141            channel_stuffing_target: default_channel_stuffing_target(),
9142        }
9143    }
9144}
9145
9146/// Vendor kickback scheme configuration.
9147#[derive(Debug, Clone, Serialize, Deserialize)]
9148pub struct KickbackSchemeConfig {
9149    /// Probability of starting a kickback scheme.
9150    #[serde(default = "default_kickback_probability")]
9151    pub probability: f64,
9152
9153    /// Minimum price inflation percentage.
9154    #[serde(default = "default_kickback_inflation_min")]
9155    pub inflation_min: f64,
9156
9157    /// Maximum price inflation percentage.
9158    #[serde(default = "default_kickback_inflation_max")]
9159    pub inflation_max: f64,
9160
9161    /// Kickback percentage (of inflation).
9162    #[serde(default = "default_kickback_percent")]
9163    pub kickback_percent: f64,
9164
9165    /// Setup duration in months.
9166    #[serde(default = "default_kickback_setup_months")]
9167    pub setup_months: u32,
9168
9169    /// Main operation duration in months.
9170    #[serde(default = "default_kickback_operation_months")]
9171    pub operation_months: u32,
9172}
9173
9174fn default_kickback_probability() -> f64 {
9175    0.01
9176}
9177fn default_kickback_inflation_min() -> f64 {
9178    0.10
9179}
9180fn default_kickback_inflation_max() -> f64 {
9181    0.25
9182}
9183fn default_kickback_percent() -> f64 {
9184    0.50
9185}
9186fn default_kickback_setup_months() -> u32 {
9187    3
9188}
9189fn default_kickback_operation_months() -> u32 {
9190    12
9191}
9192
9193impl Default for KickbackSchemeConfig {
9194    fn default() -> Self {
9195        Self {
9196            probability: default_kickback_probability(),
9197            inflation_min: default_kickback_inflation_min(),
9198            inflation_max: default_kickback_inflation_max(),
9199            kickback_percent: default_kickback_percent(),
9200            setup_months: default_kickback_setup_months(),
9201            operation_months: default_kickback_operation_months(),
9202        }
9203    }
9204}
9205
9206/// Individual scheme stage configuration.
9207#[derive(Debug, Clone, Serialize, Deserialize)]
9208pub struct SchemeStageConfig {
9209    /// Duration in months.
9210    pub duration_months: u32,
9211
9212    /// Minimum transaction amount.
9213    pub amount_min: f64,
9214
9215    /// Maximum transaction amount.
9216    pub amount_max: f64,
9217
9218    /// Minimum number of transactions.
9219    pub transaction_count_min: u32,
9220
9221    /// Maximum number of transactions.
9222    pub transaction_count_max: u32,
9223
9224    /// Detection difficulty level (trivial, easy, moderate, hard, expert).
9225    pub difficulty: String,
9226}
9227
9228impl Default for SchemeStageConfig {
9229    fn default() -> Self {
9230        Self {
9231            duration_months: 3,
9232            amount_min: 100.0,
9233            amount_max: 1000.0,
9234            transaction_count_min: 2,
9235            transaction_count_max: 10,
9236            difficulty: "moderate".to_string(),
9237        }
9238    }
9239}
9240
9241/// Correlated anomaly injection configuration.
9242#[derive(Debug, Clone, Serialize, Deserialize)]
9243pub struct CorrelatedInjectionConfig {
9244    /// Enable correlated anomaly injection.
9245    #[serde(default)]
9246    pub enabled: bool,
9247
9248    /// Enable fraud concealment co-occurrence patterns.
9249    #[serde(default = "default_true_val")]
9250    pub fraud_concealment: bool,
9251
9252    /// Enable error cascade patterns.
9253    #[serde(default = "default_true_val")]
9254    pub error_cascade: bool,
9255
9256    /// Enable temporal clustering (period-end spikes).
9257    #[serde(default = "default_true_val")]
9258    pub temporal_clustering: bool,
9259
9260    /// Temporal clustering configuration.
9261    #[serde(default)]
9262    pub temporal_clustering_config: TemporalClusteringConfig,
9263
9264    /// Co-occurrence patterns.
9265    #[serde(default)]
9266    pub co_occurrence_patterns: Vec<CoOccurrencePatternConfig>,
9267}
9268
9269impl Default for CorrelatedInjectionConfig {
9270    fn default() -> Self {
9271        Self {
9272            enabled: false,
9273            fraud_concealment: true,
9274            error_cascade: true,
9275            temporal_clustering: true,
9276            temporal_clustering_config: TemporalClusteringConfig::default(),
9277            co_occurrence_patterns: Vec::new(),
9278        }
9279    }
9280}
9281
9282/// Temporal clustering configuration.
9283#[derive(Debug, Clone, Serialize, Deserialize)]
9284pub struct TemporalClusteringConfig {
9285    /// Period-end error multiplier.
9286    #[serde(default = "default_period_end_multiplier")]
9287    pub period_end_multiplier: f64,
9288
9289    /// Number of business days before period end to apply multiplier.
9290    #[serde(default = "default_period_end_days")]
9291    pub period_end_days: u32,
9292
9293    /// Quarter-end additional multiplier.
9294    #[serde(default = "default_quarter_end_multiplier")]
9295    pub quarter_end_multiplier: f64,
9296
9297    /// Year-end additional multiplier.
9298    #[serde(default = "default_year_end_multiplier")]
9299    pub year_end_multiplier: f64,
9300}
9301
9302fn default_period_end_multiplier() -> f64 {
9303    2.5
9304}
9305fn default_period_end_days() -> u32 {
9306    5
9307}
9308fn default_quarter_end_multiplier() -> f64 {
9309    1.5
9310}
9311fn default_year_end_multiplier() -> f64 {
9312    2.0
9313}
9314
9315impl Default for TemporalClusteringConfig {
9316    fn default() -> Self {
9317        Self {
9318            period_end_multiplier: default_period_end_multiplier(),
9319            period_end_days: default_period_end_days(),
9320            quarter_end_multiplier: default_quarter_end_multiplier(),
9321            year_end_multiplier: default_year_end_multiplier(),
9322        }
9323    }
9324}
9325
9326/// Co-occurrence pattern configuration.
9327#[derive(Debug, Clone, Serialize, Deserialize)]
9328pub struct CoOccurrencePatternConfig {
9329    /// Pattern name.
9330    pub name: String,
9331
9332    /// Primary anomaly type that triggers the pattern.
9333    pub primary_type: String,
9334
9335    /// Correlated anomalies.
9336    pub correlated: Vec<CorrelatedAnomalyConfig>,
9337}
9338
9339/// Correlated anomaly configuration.
9340#[derive(Debug, Clone, Serialize, Deserialize)]
9341pub struct CorrelatedAnomalyConfig {
9342    /// Anomaly type.
9343    pub anomaly_type: String,
9344
9345    /// Probability of occurrence (0.0 to 1.0).
9346    pub probability: f64,
9347
9348    /// Minimum lag in days.
9349    pub lag_days_min: i32,
9350
9351    /// Maximum lag in days.
9352    pub lag_days_max: i32,
9353}
9354
9355/// Near-miss generation configuration.
9356#[derive(Debug, Clone, Serialize, Deserialize)]
9357pub struct NearMissConfig {
9358    /// Enable near-miss generation.
9359    #[serde(default)]
9360    pub enabled: bool,
9361
9362    /// Proportion of "anomalies" that are actually near-misses (0.0 to 1.0).
9363    #[serde(default = "default_near_miss_proportion")]
9364    pub proportion: f64,
9365
9366    /// Enable near-duplicate pattern.
9367    #[serde(default = "default_true_val")]
9368    pub near_duplicate: bool,
9369
9370    /// Near-duplicate date difference range in days.
9371    #[serde(default)]
9372    pub near_duplicate_days: NearDuplicateDaysConfig,
9373
9374    /// Enable threshold proximity pattern.
9375    #[serde(default = "default_true_val")]
9376    pub threshold_proximity: bool,
9377
9378    /// Threshold proximity range (e.g., 0.90-0.99 of threshold).
9379    #[serde(default)]
9380    pub threshold_proximity_range: ThresholdProximityRangeConfig,
9381
9382    /// Enable unusual but legitimate patterns.
9383    #[serde(default = "default_true_val")]
9384    pub unusual_legitimate: bool,
9385
9386    /// Types of unusual legitimate patterns to generate.
9387    #[serde(default = "default_unusual_legitimate_types")]
9388    pub unusual_legitimate_types: Vec<String>,
9389
9390    /// Enable corrected error patterns.
9391    #[serde(default = "default_true_val")]
9392    pub corrected_errors: bool,
9393
9394    /// Corrected error correction lag range in days.
9395    #[serde(default)]
9396    pub corrected_error_lag: CorrectedErrorLagConfig,
9397}
9398
9399fn default_near_miss_proportion() -> f64 {
9400    0.30
9401}
9402
9403fn default_unusual_legitimate_types() -> Vec<String> {
9404    vec![
9405        "year_end_bonus".to_string(),
9406        "contract_prepayment".to_string(),
9407        "insurance_claim".to_string(),
9408        "settlement_payment".to_string(),
9409    ]
9410}
9411
9412impl Default for NearMissConfig {
9413    fn default() -> Self {
9414        Self {
9415            enabled: false,
9416            proportion: default_near_miss_proportion(),
9417            near_duplicate: true,
9418            near_duplicate_days: NearDuplicateDaysConfig::default(),
9419            threshold_proximity: true,
9420            threshold_proximity_range: ThresholdProximityRangeConfig::default(),
9421            unusual_legitimate: true,
9422            unusual_legitimate_types: default_unusual_legitimate_types(),
9423            corrected_errors: true,
9424            corrected_error_lag: CorrectedErrorLagConfig::default(),
9425        }
9426    }
9427}
9428
9429/// Near-duplicate days configuration.
9430#[derive(Debug, Clone, Serialize, Deserialize)]
9431pub struct NearDuplicateDaysConfig {
9432    /// Minimum days apart.
9433    #[serde(default = "default_near_duplicate_min")]
9434    pub min: u32,
9435
9436    /// Maximum days apart.
9437    #[serde(default = "default_near_duplicate_max")]
9438    pub max: u32,
9439}
9440
9441fn default_near_duplicate_min() -> u32 {
9442    1
9443}
9444fn default_near_duplicate_max() -> u32 {
9445    3
9446}
9447
9448impl Default for NearDuplicateDaysConfig {
9449    fn default() -> Self {
9450        Self {
9451            min: default_near_duplicate_min(),
9452            max: default_near_duplicate_max(),
9453        }
9454    }
9455}
9456
9457/// Threshold proximity range configuration.
9458#[derive(Debug, Clone, Serialize, Deserialize)]
9459pub struct ThresholdProximityRangeConfig {
9460    /// Minimum proximity (e.g., 0.90 = 90% of threshold).
9461    #[serde(default = "default_threshold_proximity_min")]
9462    pub min: f64,
9463
9464    /// Maximum proximity (e.g., 0.99 = 99% of threshold).
9465    #[serde(default = "default_threshold_proximity_max")]
9466    pub max: f64,
9467}
9468
9469fn default_threshold_proximity_min() -> f64 {
9470    0.90
9471}
9472fn default_threshold_proximity_max() -> f64 {
9473    0.99
9474}
9475
9476impl Default for ThresholdProximityRangeConfig {
9477    fn default() -> Self {
9478        Self {
9479            min: default_threshold_proximity_min(),
9480            max: default_threshold_proximity_max(),
9481        }
9482    }
9483}
9484
9485/// Corrected error lag configuration.
9486#[derive(Debug, Clone, Serialize, Deserialize)]
9487pub struct CorrectedErrorLagConfig {
9488    /// Minimum correction lag in days.
9489    #[serde(default = "default_corrected_error_lag_min")]
9490    pub min: u32,
9491
9492    /// Maximum correction lag in days.
9493    #[serde(default = "default_corrected_error_lag_max")]
9494    pub max: u32,
9495}
9496
9497fn default_corrected_error_lag_min() -> u32 {
9498    1
9499}
9500fn default_corrected_error_lag_max() -> u32 {
9501    5
9502}
9503
9504impl Default for CorrectedErrorLagConfig {
9505    fn default() -> Self {
9506        Self {
9507            min: default_corrected_error_lag_min(),
9508            max: default_corrected_error_lag_max(),
9509        }
9510    }
9511}
9512
9513/// Detection difficulty classification configuration.
9514#[derive(Debug, Clone, Serialize, Deserialize)]
9515pub struct DifficultyClassificationConfig {
9516    /// Enable detection difficulty classification.
9517    #[serde(default)]
9518    pub enabled: bool,
9519
9520    /// Target distribution of difficulty levels.
9521    #[serde(default)]
9522    pub target_distribution: DifficultyDistributionConfig,
9523}
9524
9525impl Default for DifficultyClassificationConfig {
9526    fn default() -> Self {
9527        Self {
9528            enabled: true,
9529            target_distribution: DifficultyDistributionConfig::default(),
9530        }
9531    }
9532}
9533
9534/// Target distribution of detection difficulty levels.
9535#[derive(Debug, Clone, Serialize, Deserialize)]
9536pub struct DifficultyDistributionConfig {
9537    /// Proportion of trivial anomalies (expected 99% detection).
9538    #[serde(default = "default_difficulty_trivial")]
9539    pub trivial: f64,
9540
9541    /// Proportion of easy anomalies (expected 90% detection).
9542    #[serde(default = "default_difficulty_easy")]
9543    pub easy: f64,
9544
9545    /// Proportion of moderate anomalies (expected 70% detection).
9546    #[serde(default = "default_difficulty_moderate")]
9547    pub moderate: f64,
9548
9549    /// Proportion of hard anomalies (expected 40% detection).
9550    #[serde(default = "default_difficulty_hard")]
9551    pub hard: f64,
9552
9553    /// Proportion of expert anomalies (expected 15% detection).
9554    #[serde(default = "default_difficulty_expert")]
9555    pub expert: f64,
9556}
9557
9558fn default_difficulty_trivial() -> f64 {
9559    0.15
9560}
9561fn default_difficulty_easy() -> f64 {
9562    0.25
9563}
9564fn default_difficulty_moderate() -> f64 {
9565    0.30
9566}
9567fn default_difficulty_hard() -> f64 {
9568    0.20
9569}
9570fn default_difficulty_expert() -> f64 {
9571    0.10
9572}
9573
9574impl Default for DifficultyDistributionConfig {
9575    fn default() -> Self {
9576        Self {
9577            trivial: default_difficulty_trivial(),
9578            easy: default_difficulty_easy(),
9579            moderate: default_difficulty_moderate(),
9580            hard: default_difficulty_hard(),
9581            expert: default_difficulty_expert(),
9582        }
9583    }
9584}
9585
9586/// Context-aware injection configuration.
9587#[derive(Debug, Clone, Serialize, Deserialize, Default)]
9588pub struct ContextAwareConfig {
9589    /// Enable context-aware injection.
9590    #[serde(default)]
9591    pub enabled: bool,
9592
9593    /// Vendor-specific anomaly rules.
9594    #[serde(default)]
9595    pub vendor_rules: VendorAnomalyRulesConfig,
9596
9597    /// Employee-specific anomaly rules.
9598    #[serde(default)]
9599    pub employee_rules: EmployeeAnomalyRulesConfig,
9600
9601    /// Account-specific anomaly rules.
9602    #[serde(default)]
9603    pub account_rules: AccountAnomalyRulesConfig,
9604
9605    /// Behavioral baseline configuration.
9606    #[serde(default)]
9607    pub behavioral_baseline: BehavioralBaselineConfig,
9608}
9609
9610/// Vendor-specific anomaly rules configuration.
9611#[derive(Debug, Clone, Serialize, Deserialize)]
9612pub struct VendorAnomalyRulesConfig {
9613    /// Error rate multiplier for new vendors (< threshold days).
9614    #[serde(default = "default_new_vendor_multiplier")]
9615    pub new_vendor_error_multiplier: f64,
9616
9617    /// Days threshold for "new" vendor classification.
9618    #[serde(default = "default_new_vendor_threshold")]
9619    pub new_vendor_threshold_days: u32,
9620
9621    /// Error rate multiplier for international vendors.
9622    #[serde(default = "default_international_multiplier")]
9623    pub international_error_multiplier: f64,
9624
9625    /// Strategic vendor anomaly types (may differ from general vendors).
9626    #[serde(default = "default_strategic_vendor_types")]
9627    pub strategic_vendor_anomaly_types: Vec<String>,
9628}
9629
9630fn default_new_vendor_multiplier() -> f64 {
9631    2.5
9632}
9633fn default_new_vendor_threshold() -> u32 {
9634    90
9635}
9636fn default_international_multiplier() -> f64 {
9637    1.5
9638}
9639fn default_strategic_vendor_types() -> Vec<String> {
9640    vec![
9641        "pricing_dispute".to_string(),
9642        "contract_violation".to_string(),
9643    ]
9644}
9645
9646impl Default for VendorAnomalyRulesConfig {
9647    fn default() -> Self {
9648        Self {
9649            new_vendor_error_multiplier: default_new_vendor_multiplier(),
9650            new_vendor_threshold_days: default_new_vendor_threshold(),
9651            international_error_multiplier: default_international_multiplier(),
9652            strategic_vendor_anomaly_types: default_strategic_vendor_types(),
9653        }
9654    }
9655}
9656
9657/// Employee-specific anomaly rules configuration.
9658#[derive(Debug, Clone, Serialize, Deserialize)]
9659pub struct EmployeeAnomalyRulesConfig {
9660    /// Error rate for new employees (< threshold days).
9661    #[serde(default = "default_new_employee_rate")]
9662    pub new_employee_error_rate: f64,
9663
9664    /// Days threshold for "new" employee classification.
9665    #[serde(default = "default_new_employee_threshold")]
9666    pub new_employee_threshold_days: u32,
9667
9668    /// Transaction volume threshold for fatigue errors.
9669    #[serde(default = "default_volume_fatigue_threshold")]
9670    pub volume_fatigue_threshold: u32,
9671
9672    /// Error rate multiplier when primary approver is absent.
9673    #[serde(default = "default_coverage_multiplier")]
9674    pub coverage_error_multiplier: f64,
9675}
9676
9677fn default_new_employee_rate() -> f64 {
9678    0.05
9679}
9680fn default_new_employee_threshold() -> u32 {
9681    180
9682}
9683fn default_volume_fatigue_threshold() -> u32 {
9684    50
9685}
9686fn default_coverage_multiplier() -> f64 {
9687    1.8
9688}
9689
9690impl Default for EmployeeAnomalyRulesConfig {
9691    fn default() -> Self {
9692        Self {
9693            new_employee_error_rate: default_new_employee_rate(),
9694            new_employee_threshold_days: default_new_employee_threshold(),
9695            volume_fatigue_threshold: default_volume_fatigue_threshold(),
9696            coverage_error_multiplier: default_coverage_multiplier(),
9697        }
9698    }
9699}
9700
9701/// Account-specific anomaly rules configuration.
9702#[derive(Debug, Clone, Serialize, Deserialize)]
9703pub struct AccountAnomalyRulesConfig {
9704    /// Error rate multiplier for high-risk accounts.
9705    #[serde(default = "default_high_risk_multiplier")]
9706    pub high_risk_account_multiplier: f64,
9707
9708    /// Account codes considered high-risk.
9709    #[serde(default = "default_high_risk_accounts")]
9710    pub high_risk_accounts: Vec<String>,
9711
9712    /// Error rate multiplier for suspense accounts.
9713    #[serde(default = "default_suspense_multiplier")]
9714    pub suspense_account_multiplier: f64,
9715
9716    /// Account codes considered suspense accounts.
9717    #[serde(default = "default_suspense_accounts")]
9718    pub suspense_accounts: Vec<String>,
9719
9720    /// Error rate multiplier for intercompany accounts.
9721    #[serde(default = "default_intercompany_multiplier")]
9722    pub intercompany_account_multiplier: f64,
9723}
9724
9725fn default_high_risk_multiplier() -> f64 {
9726    2.0
9727}
9728fn default_high_risk_accounts() -> Vec<String> {
9729    vec![
9730        "1100".to_string(), // AR Control
9731        "2000".to_string(), // AP Control
9732        "3000".to_string(), // Cash
9733    ]
9734}
9735fn default_suspense_multiplier() -> f64 {
9736    3.0
9737}
9738fn default_suspense_accounts() -> Vec<String> {
9739    vec!["9999".to_string(), "9998".to_string()]
9740}
9741fn default_intercompany_multiplier() -> f64 {
9742    1.5
9743}
9744
9745impl Default for AccountAnomalyRulesConfig {
9746    fn default() -> Self {
9747        Self {
9748            high_risk_account_multiplier: default_high_risk_multiplier(),
9749            high_risk_accounts: default_high_risk_accounts(),
9750            suspense_account_multiplier: default_suspense_multiplier(),
9751            suspense_accounts: default_suspense_accounts(),
9752            intercompany_account_multiplier: default_intercompany_multiplier(),
9753        }
9754    }
9755}
9756
9757/// Behavioral baseline configuration.
9758#[derive(Debug, Clone, Serialize, Deserialize)]
9759pub struct BehavioralBaselineConfig {
9760    /// Enable behavioral baseline tracking.
9761    #[serde(default)]
9762    pub enabled: bool,
9763
9764    /// Number of days to build baseline from.
9765    #[serde(default = "default_baseline_period")]
9766    pub baseline_period_days: u32,
9767
9768    /// Standard deviation threshold for amount anomalies.
9769    #[serde(default = "default_deviation_threshold")]
9770    pub deviation_threshold_std: f64,
9771
9772    /// Standard deviation threshold for frequency anomalies.
9773    #[serde(default = "default_frequency_deviation")]
9774    pub frequency_deviation_threshold: f64,
9775}
9776
9777fn default_baseline_period() -> u32 {
9778    90
9779}
9780fn default_deviation_threshold() -> f64 {
9781    3.0
9782}
9783fn default_frequency_deviation() -> f64 {
9784    2.0
9785}
9786
9787impl Default for BehavioralBaselineConfig {
9788    fn default() -> Self {
9789        Self {
9790            enabled: false,
9791            baseline_period_days: default_baseline_period(),
9792            deviation_threshold_std: default_deviation_threshold(),
9793            frequency_deviation_threshold: default_frequency_deviation(),
9794        }
9795    }
9796}
9797
9798/// Enhanced labeling configuration.
9799#[derive(Debug, Clone, Serialize, Deserialize)]
9800pub struct EnhancedLabelingConfig {
9801    /// Enable severity scoring.
9802    #[serde(default = "default_true_val")]
9803    pub severity_scoring: bool,
9804
9805    /// Enable difficulty classification.
9806    #[serde(default = "default_true_val")]
9807    pub difficulty_classification: bool,
9808
9809    /// Materiality thresholds for severity classification.
9810    #[serde(default)]
9811    pub materiality_thresholds: MaterialityThresholdsConfig,
9812}
9813
9814impl Default for EnhancedLabelingConfig {
9815    fn default() -> Self {
9816        Self {
9817            severity_scoring: true,
9818            difficulty_classification: true,
9819            materiality_thresholds: MaterialityThresholdsConfig::default(),
9820        }
9821    }
9822}
9823
9824/// Materiality thresholds configuration.
9825#[derive(Debug, Clone, Serialize, Deserialize)]
9826pub struct MaterialityThresholdsConfig {
9827    /// Threshold for trivial impact (as percentage of total).
9828    #[serde(default = "default_materiality_trivial")]
9829    pub trivial: f64,
9830
9831    /// Threshold for immaterial impact.
9832    #[serde(default = "default_materiality_immaterial")]
9833    pub immaterial: f64,
9834
9835    /// Threshold for material impact.
9836    #[serde(default = "default_materiality_material")]
9837    pub material: f64,
9838
9839    /// Threshold for highly material impact.
9840    #[serde(default = "default_materiality_highly_material")]
9841    pub highly_material: f64,
9842}
9843
9844fn default_materiality_trivial() -> f64 {
9845    0.001
9846}
9847fn default_materiality_immaterial() -> f64 {
9848    0.01
9849}
9850fn default_materiality_material() -> f64 {
9851    0.05
9852}
9853fn default_materiality_highly_material() -> f64 {
9854    0.10
9855}
9856
9857impl Default for MaterialityThresholdsConfig {
9858    fn default() -> Self {
9859        Self {
9860            trivial: default_materiality_trivial(),
9861            immaterial: default_materiality_immaterial(),
9862            material: default_materiality_material(),
9863            highly_material: default_materiality_highly_material(),
9864        }
9865    }
9866}
9867
9868// =============================================================================
9869// Industry-Specific Configuration
9870// =============================================================================
9871
9872/// Industry-specific transaction and anomaly generation configuration.
9873///
9874/// This configuration enables generation of industry-authentic:
9875/// - Transaction types with appropriate terminology
9876/// - Master data (BOM, routings, clinical codes, etc.)
9877/// - Industry-specific anomaly patterns
9878/// - Regulatory framework compliance
9879#[derive(Debug, Clone, Serialize, Deserialize, Default)]
9880pub struct IndustrySpecificConfig {
9881    /// Enable industry-specific generation.
9882    #[serde(default)]
9883    pub enabled: bool,
9884
9885    /// Manufacturing industry settings.
9886    #[serde(default)]
9887    pub manufacturing: ManufacturingConfig,
9888
9889    /// Retail industry settings.
9890    #[serde(default)]
9891    pub retail: RetailConfig,
9892
9893    /// Healthcare industry settings.
9894    #[serde(default)]
9895    pub healthcare: HealthcareConfig,
9896
9897    /// Technology industry settings.
9898    #[serde(default)]
9899    pub technology: TechnologyConfig,
9900
9901    /// Financial services industry settings.
9902    #[serde(default)]
9903    pub financial_services: FinancialServicesConfig,
9904
9905    /// Professional services industry settings.
9906    #[serde(default)]
9907    pub professional_services: ProfessionalServicesConfig,
9908}
9909
9910/// Manufacturing industry configuration.
9911#[derive(Debug, Clone, Serialize, Deserialize)]
9912pub struct ManufacturingConfig {
9913    /// Enable manufacturing-specific generation.
9914    #[serde(default)]
9915    pub enabled: bool,
9916
9917    /// Bill of Materials depth (typical: 3-7).
9918    #[serde(default = "default_bom_depth")]
9919    pub bom_depth: u32,
9920
9921    /// Whether to use just-in-time inventory.
9922    #[serde(default)]
9923    pub just_in_time: bool,
9924
9925    /// Production order types to generate.
9926    #[serde(default = "default_production_order_types")]
9927    pub production_order_types: Vec<String>,
9928
9929    /// Quality framework (ISO_9001, Six_Sigma, etc.).
9930    #[serde(default)]
9931    pub quality_framework: Option<String>,
9932
9933    /// Number of supplier tiers to model (1-3).
9934    #[serde(default = "default_supplier_tiers")]
9935    pub supplier_tiers: u32,
9936
9937    /// Standard cost update frequency.
9938    #[serde(default = "default_cost_frequency")]
9939    pub standard_cost_frequency: String,
9940
9941    /// Target yield rate (0.95-0.99 typical).
9942    #[serde(default = "default_yield_rate")]
9943    pub target_yield_rate: f64,
9944
9945    /// Scrap percentage threshold for alerts.
9946    #[serde(default = "default_scrap_threshold")]
9947    pub scrap_alert_threshold: f64,
9948
9949    /// Manufacturing anomaly injection rates.
9950    #[serde(default)]
9951    pub anomaly_rates: ManufacturingAnomalyRates,
9952
9953    /// Cost accounting configuration (WIP → FG → COGS pipeline).
9954    #[serde(default)]
9955    pub cost_accounting: ManufacturingCostAccountingConfig,
9956}
9957
9958/// Configuration for manufacturing cost accounting JE generation.
9959#[derive(Debug, Clone, Serialize, Deserialize)]
9960pub struct ManufacturingCostAccountingConfig {
9961    /// Enable multi-stage cost flow (WIP → FG → COGS) instead of flat JEs.
9962    #[serde(default = "default_true")]
9963    pub enabled: bool,
9964
9965    /// Generate standard cost variance JEs.
9966    #[serde(default = "default_true")]
9967    pub variance_accounts_enabled: bool,
9968
9969    /// Generate warranty provisions from quality inspection failures.
9970    #[serde(default = "default_true")]
9971    pub warranty_provisions_enabled: bool,
9972
9973    /// Minimum defect rate (0.0-1.0) to trigger warranty provision generation.
9974    #[serde(default = "default_warranty_defect_threshold")]
9975    pub warranty_defect_threshold: f64,
9976}
9977
9978fn default_warranty_defect_threshold() -> f64 {
9979    0.01
9980}
9981
9982impl Default for ManufacturingCostAccountingConfig {
9983    fn default() -> Self {
9984        Self {
9985            enabled: true,
9986            variance_accounts_enabled: true,
9987            warranty_provisions_enabled: true,
9988            warranty_defect_threshold: 0.01,
9989        }
9990    }
9991}
9992
9993fn default_bom_depth() -> u32 {
9994    4
9995}
9996
9997fn default_production_order_types() -> Vec<String> {
9998    vec![
9999        "standard".to_string(),
10000        "rework".to_string(),
10001        "prototype".to_string(),
10002    ]
10003}
10004
10005fn default_supplier_tiers() -> u32 {
10006    2
10007}
10008
10009fn default_cost_frequency() -> String {
10010    "quarterly".to_string()
10011}
10012
10013fn default_yield_rate() -> f64 {
10014    0.97
10015}
10016
10017fn default_scrap_threshold() -> f64 {
10018    0.03
10019}
10020
10021impl Default for ManufacturingConfig {
10022    fn default() -> Self {
10023        Self {
10024            enabled: false,
10025            bom_depth: default_bom_depth(),
10026            just_in_time: false,
10027            production_order_types: default_production_order_types(),
10028            quality_framework: Some("ISO_9001".to_string()),
10029            supplier_tiers: default_supplier_tiers(),
10030            standard_cost_frequency: default_cost_frequency(),
10031            target_yield_rate: default_yield_rate(),
10032            scrap_alert_threshold: default_scrap_threshold(),
10033            anomaly_rates: ManufacturingAnomalyRates::default(),
10034            cost_accounting: ManufacturingCostAccountingConfig::default(),
10035        }
10036    }
10037}
10038
10039/// Manufacturing anomaly injection rates.
10040#[derive(Debug, Clone, Serialize, Deserialize)]
10041pub struct ManufacturingAnomalyRates {
10042    /// Yield manipulation rate.
10043    #[serde(default = "default_mfg_yield_rate")]
10044    pub yield_manipulation: f64,
10045
10046    /// Labor misallocation rate.
10047    #[serde(default = "default_mfg_labor_rate")]
10048    pub labor_misallocation: f64,
10049
10050    /// Phantom production rate.
10051    #[serde(default = "default_mfg_phantom_rate")]
10052    pub phantom_production: f64,
10053
10054    /// Standard cost manipulation rate.
10055    #[serde(default = "default_mfg_cost_rate")]
10056    pub standard_cost_manipulation: f64,
10057
10058    /// Inventory fraud rate.
10059    #[serde(default = "default_mfg_inventory_rate")]
10060    pub inventory_fraud: f64,
10061}
10062
10063fn default_mfg_yield_rate() -> f64 {
10064    0.015
10065}
10066
10067fn default_mfg_labor_rate() -> f64 {
10068    0.02
10069}
10070
10071fn default_mfg_phantom_rate() -> f64 {
10072    0.005
10073}
10074
10075fn default_mfg_cost_rate() -> f64 {
10076    0.01
10077}
10078
10079fn default_mfg_inventory_rate() -> f64 {
10080    0.008
10081}
10082
10083impl Default for ManufacturingAnomalyRates {
10084    fn default() -> Self {
10085        Self {
10086            yield_manipulation: default_mfg_yield_rate(),
10087            labor_misallocation: default_mfg_labor_rate(),
10088            phantom_production: default_mfg_phantom_rate(),
10089            standard_cost_manipulation: default_mfg_cost_rate(),
10090            inventory_fraud: default_mfg_inventory_rate(),
10091        }
10092    }
10093}
10094
10095/// Retail industry configuration.
10096#[derive(Debug, Clone, Serialize, Deserialize)]
10097pub struct RetailConfig {
10098    /// Enable retail-specific generation.
10099    #[serde(default)]
10100    pub enabled: bool,
10101
10102    /// Store type distribution.
10103    #[serde(default)]
10104    pub store_types: RetailStoreTypeConfig,
10105
10106    /// Average daily transactions per store.
10107    #[serde(default = "default_retail_daily_txns")]
10108    pub avg_daily_transactions: u32,
10109
10110    /// Enable loss prevention tracking.
10111    #[serde(default = "default_true")]
10112    pub loss_prevention: bool,
10113
10114    /// Shrinkage rate (0.01-0.03 typical).
10115    #[serde(default = "default_shrinkage_rate")]
10116    pub shrinkage_rate: f64,
10117
10118    /// Retail anomaly injection rates.
10119    #[serde(default)]
10120    pub anomaly_rates: RetailAnomalyRates,
10121}
10122
10123fn default_retail_daily_txns() -> u32 {
10124    500
10125}
10126
10127fn default_shrinkage_rate() -> f64 {
10128    0.015
10129}
10130
10131impl Default for RetailConfig {
10132    fn default() -> Self {
10133        Self {
10134            enabled: false,
10135            store_types: RetailStoreTypeConfig::default(),
10136            avg_daily_transactions: default_retail_daily_txns(),
10137            loss_prevention: true,
10138            shrinkage_rate: default_shrinkage_rate(),
10139            anomaly_rates: RetailAnomalyRates::default(),
10140        }
10141    }
10142}
10143
10144/// Retail store type distribution.
10145#[derive(Debug, Clone, Serialize, Deserialize)]
10146pub struct RetailStoreTypeConfig {
10147    /// Percentage of flagship stores.
10148    #[serde(default = "default_flagship_pct")]
10149    pub flagship: f64,
10150
10151    /// Percentage of regional stores.
10152    #[serde(default = "default_regional_pct")]
10153    pub regional: f64,
10154
10155    /// Percentage of outlet stores.
10156    #[serde(default = "default_outlet_pct")]
10157    pub outlet: f64,
10158
10159    /// Percentage of e-commerce.
10160    #[serde(default = "default_ecommerce_pct")]
10161    pub ecommerce: f64,
10162}
10163
10164fn default_flagship_pct() -> f64 {
10165    0.10
10166}
10167
10168fn default_regional_pct() -> f64 {
10169    0.50
10170}
10171
10172fn default_outlet_pct() -> f64 {
10173    0.25
10174}
10175
10176fn default_ecommerce_pct() -> f64 {
10177    0.15
10178}
10179
10180impl Default for RetailStoreTypeConfig {
10181    fn default() -> Self {
10182        Self {
10183            flagship: default_flagship_pct(),
10184            regional: default_regional_pct(),
10185            outlet: default_outlet_pct(),
10186            ecommerce: default_ecommerce_pct(),
10187        }
10188    }
10189}
10190
10191/// Retail anomaly injection rates.
10192#[derive(Debug, Clone, Serialize, Deserialize)]
10193pub struct RetailAnomalyRates {
10194    /// Sweethearting rate.
10195    #[serde(default = "default_sweethearting_rate")]
10196    pub sweethearting: f64,
10197
10198    /// Skimming rate.
10199    #[serde(default = "default_skimming_rate")]
10200    pub skimming: f64,
10201
10202    /// Refund fraud rate.
10203    #[serde(default = "default_refund_fraud_rate")]
10204    pub refund_fraud: f64,
10205
10206    /// Void abuse rate.
10207    #[serde(default = "default_void_abuse_rate")]
10208    pub void_abuse: f64,
10209
10210    /// Gift card fraud rate.
10211    #[serde(default = "default_gift_card_rate")]
10212    pub gift_card_fraud: f64,
10213
10214    /// Vendor kickback rate.
10215    #[serde(default = "default_retail_kickback_rate")]
10216    pub vendor_kickback: f64,
10217}
10218
10219fn default_sweethearting_rate() -> f64 {
10220    0.02
10221}
10222
10223fn default_skimming_rate() -> f64 {
10224    0.005
10225}
10226
10227fn default_refund_fraud_rate() -> f64 {
10228    0.015
10229}
10230
10231fn default_void_abuse_rate() -> f64 {
10232    0.01
10233}
10234
10235fn default_gift_card_rate() -> f64 {
10236    0.008
10237}
10238
10239fn default_retail_kickback_rate() -> f64 {
10240    0.003
10241}
10242
10243impl Default for RetailAnomalyRates {
10244    fn default() -> Self {
10245        Self {
10246            sweethearting: default_sweethearting_rate(),
10247            skimming: default_skimming_rate(),
10248            refund_fraud: default_refund_fraud_rate(),
10249            void_abuse: default_void_abuse_rate(),
10250            gift_card_fraud: default_gift_card_rate(),
10251            vendor_kickback: default_retail_kickback_rate(),
10252        }
10253    }
10254}
10255
10256/// Healthcare industry configuration.
10257#[derive(Debug, Clone, Serialize, Deserialize)]
10258pub struct HealthcareConfig {
10259    /// Enable healthcare-specific generation.
10260    #[serde(default)]
10261    pub enabled: bool,
10262
10263    /// Healthcare facility type.
10264    #[serde(default = "default_facility_type")]
10265    pub facility_type: String,
10266
10267    /// Payer mix distribution.
10268    #[serde(default)]
10269    pub payer_mix: HealthcarePayerMix,
10270
10271    /// Coding systems enabled.
10272    #[serde(default)]
10273    pub coding_systems: HealthcareCodingSystems,
10274
10275    /// Healthcare compliance settings.
10276    #[serde(default)]
10277    pub compliance: HealthcareComplianceConfig,
10278
10279    /// Average daily encounters.
10280    #[serde(default = "default_daily_encounters")]
10281    pub avg_daily_encounters: u32,
10282
10283    /// Average charges per encounter.
10284    #[serde(default = "default_charges_per_encounter")]
10285    pub avg_charges_per_encounter: u32,
10286
10287    /// Denial rate (0.0-1.0).
10288    #[serde(default = "default_hc_denial_rate")]
10289    pub denial_rate: f64,
10290
10291    /// Bad debt rate (0.0-1.0).
10292    #[serde(default = "default_hc_bad_debt_rate")]
10293    pub bad_debt_rate: f64,
10294
10295    /// Charity care rate (0.0-1.0).
10296    #[serde(default = "default_hc_charity_care_rate")]
10297    pub charity_care_rate: f64,
10298
10299    /// Healthcare anomaly injection rates.
10300    #[serde(default)]
10301    pub anomaly_rates: HealthcareAnomalyRates,
10302}
10303
10304fn default_facility_type() -> String {
10305    "hospital".to_string()
10306}
10307
10308fn default_daily_encounters() -> u32 {
10309    150
10310}
10311
10312fn default_charges_per_encounter() -> u32 {
10313    8
10314}
10315
10316fn default_hc_denial_rate() -> f64 {
10317    0.05
10318}
10319
10320fn default_hc_bad_debt_rate() -> f64 {
10321    0.03
10322}
10323
10324fn default_hc_charity_care_rate() -> f64 {
10325    0.02
10326}
10327
10328impl Default for HealthcareConfig {
10329    fn default() -> Self {
10330        Self {
10331            enabled: false,
10332            facility_type: default_facility_type(),
10333            payer_mix: HealthcarePayerMix::default(),
10334            coding_systems: HealthcareCodingSystems::default(),
10335            compliance: HealthcareComplianceConfig::default(),
10336            avg_daily_encounters: default_daily_encounters(),
10337            avg_charges_per_encounter: default_charges_per_encounter(),
10338            denial_rate: default_hc_denial_rate(),
10339            bad_debt_rate: default_hc_bad_debt_rate(),
10340            charity_care_rate: default_hc_charity_care_rate(),
10341            anomaly_rates: HealthcareAnomalyRates::default(),
10342        }
10343    }
10344}
10345
10346/// Healthcare payer mix distribution.
10347#[derive(Debug, Clone, Serialize, Deserialize)]
10348pub struct HealthcarePayerMix {
10349    /// Medicare percentage.
10350    #[serde(default = "default_medicare_pct")]
10351    pub medicare: f64,
10352
10353    /// Medicaid percentage.
10354    #[serde(default = "default_medicaid_pct")]
10355    pub medicaid: f64,
10356
10357    /// Commercial insurance percentage.
10358    #[serde(default = "default_commercial_pct")]
10359    pub commercial: f64,
10360
10361    /// Self-pay percentage.
10362    #[serde(default = "default_self_pay_pct")]
10363    pub self_pay: f64,
10364}
10365
10366fn default_medicare_pct() -> f64 {
10367    0.40
10368}
10369
10370fn default_medicaid_pct() -> f64 {
10371    0.20
10372}
10373
10374fn default_commercial_pct() -> f64 {
10375    0.30
10376}
10377
10378fn default_self_pay_pct() -> f64 {
10379    0.10
10380}
10381
10382impl Default for HealthcarePayerMix {
10383    fn default() -> Self {
10384        Self {
10385            medicare: default_medicare_pct(),
10386            medicaid: default_medicaid_pct(),
10387            commercial: default_commercial_pct(),
10388            self_pay: default_self_pay_pct(),
10389        }
10390    }
10391}
10392
10393/// Healthcare coding systems configuration.
10394#[derive(Debug, Clone, Serialize, Deserialize)]
10395pub struct HealthcareCodingSystems {
10396    /// Enable ICD-10 diagnosis coding.
10397    #[serde(default = "default_true")]
10398    pub icd10: bool,
10399
10400    /// Enable CPT procedure coding.
10401    #[serde(default = "default_true")]
10402    pub cpt: bool,
10403
10404    /// Enable DRG grouping.
10405    #[serde(default = "default_true")]
10406    pub drg: bool,
10407
10408    /// Enable HCPCS Level II coding.
10409    #[serde(default = "default_true")]
10410    pub hcpcs: bool,
10411
10412    /// Enable revenue codes.
10413    #[serde(default = "default_true")]
10414    pub revenue_codes: bool,
10415}
10416
10417impl Default for HealthcareCodingSystems {
10418    fn default() -> Self {
10419        Self {
10420            icd10: true,
10421            cpt: true,
10422            drg: true,
10423            hcpcs: true,
10424            revenue_codes: true,
10425        }
10426    }
10427}
10428
10429/// Healthcare compliance configuration.
10430#[derive(Debug, Clone, Serialize, Deserialize)]
10431pub struct HealthcareComplianceConfig {
10432    /// Enable HIPAA compliance.
10433    #[serde(default = "default_true")]
10434    pub hipaa: bool,
10435
10436    /// Enable Stark Law compliance.
10437    #[serde(default = "default_true")]
10438    pub stark_law: bool,
10439
10440    /// Enable Anti-Kickback Statute compliance.
10441    #[serde(default = "default_true")]
10442    pub anti_kickback: bool,
10443
10444    /// Enable False Claims Act compliance.
10445    #[serde(default = "default_true")]
10446    pub false_claims_act: bool,
10447
10448    /// Enable EMTALA compliance (for hospitals).
10449    #[serde(default = "default_true")]
10450    pub emtala: bool,
10451}
10452
10453impl Default for HealthcareComplianceConfig {
10454    fn default() -> Self {
10455        Self {
10456            hipaa: true,
10457            stark_law: true,
10458            anti_kickback: true,
10459            false_claims_act: true,
10460            emtala: true,
10461        }
10462    }
10463}
10464
10465/// Healthcare anomaly injection rates.
10466#[derive(Debug, Clone, Serialize, Deserialize)]
10467pub struct HealthcareAnomalyRates {
10468    /// Upcoding rate.
10469    #[serde(default = "default_upcoding_rate")]
10470    pub upcoding: f64,
10471
10472    /// Unbundling rate.
10473    #[serde(default = "default_unbundling_rate")]
10474    pub unbundling: f64,
10475
10476    /// Phantom billing rate.
10477    #[serde(default = "default_phantom_billing_rate")]
10478    pub phantom_billing: f64,
10479
10480    /// Kickback rate.
10481    #[serde(default = "default_healthcare_kickback_rate")]
10482    pub kickbacks: f64,
10483
10484    /// Duplicate billing rate.
10485    #[serde(default = "default_duplicate_billing_rate")]
10486    pub duplicate_billing: f64,
10487
10488    /// Medical necessity abuse rate.
10489    #[serde(default = "default_med_necessity_rate")]
10490    pub medical_necessity_abuse: f64,
10491}
10492
10493fn default_upcoding_rate() -> f64 {
10494    0.02
10495}
10496
10497fn default_unbundling_rate() -> f64 {
10498    0.015
10499}
10500
10501fn default_phantom_billing_rate() -> f64 {
10502    0.005
10503}
10504
10505fn default_healthcare_kickback_rate() -> f64 {
10506    0.003
10507}
10508
10509fn default_duplicate_billing_rate() -> f64 {
10510    0.008
10511}
10512
10513fn default_med_necessity_rate() -> f64 {
10514    0.01
10515}
10516
10517impl Default for HealthcareAnomalyRates {
10518    fn default() -> Self {
10519        Self {
10520            upcoding: default_upcoding_rate(),
10521            unbundling: default_unbundling_rate(),
10522            phantom_billing: default_phantom_billing_rate(),
10523            kickbacks: default_healthcare_kickback_rate(),
10524            duplicate_billing: default_duplicate_billing_rate(),
10525            medical_necessity_abuse: default_med_necessity_rate(),
10526        }
10527    }
10528}
10529
10530/// Technology industry configuration.
10531#[derive(Debug, Clone, Serialize, Deserialize)]
10532pub struct TechnologyConfig {
10533    /// Enable technology-specific generation.
10534    #[serde(default)]
10535    pub enabled: bool,
10536
10537    /// Revenue model type.
10538    #[serde(default = "default_revenue_model")]
10539    pub revenue_model: String,
10540
10541    /// Subscription revenue percentage (for SaaS).
10542    #[serde(default = "default_subscription_pct")]
10543    pub subscription_revenue_pct: f64,
10544
10545    /// License revenue percentage.
10546    #[serde(default = "default_license_pct")]
10547    pub license_revenue_pct: f64,
10548
10549    /// Services revenue percentage.
10550    #[serde(default = "default_services_pct")]
10551    pub services_revenue_pct: f64,
10552
10553    /// R&D capitalization settings.
10554    #[serde(default)]
10555    pub rd_capitalization: RdCapitalizationConfig,
10556
10557    /// Technology anomaly injection rates.
10558    #[serde(default)]
10559    pub anomaly_rates: TechnologyAnomalyRates,
10560}
10561
10562fn default_revenue_model() -> String {
10563    "saas".to_string()
10564}
10565
10566fn default_subscription_pct() -> f64 {
10567    0.60
10568}
10569
10570fn default_license_pct() -> f64 {
10571    0.25
10572}
10573
10574fn default_services_pct() -> f64 {
10575    0.15
10576}
10577
10578impl Default for TechnologyConfig {
10579    fn default() -> Self {
10580        Self {
10581            enabled: false,
10582            revenue_model: default_revenue_model(),
10583            subscription_revenue_pct: default_subscription_pct(),
10584            license_revenue_pct: default_license_pct(),
10585            services_revenue_pct: default_services_pct(),
10586            rd_capitalization: RdCapitalizationConfig::default(),
10587            anomaly_rates: TechnologyAnomalyRates::default(),
10588        }
10589    }
10590}
10591
10592/// R&D capitalization configuration.
10593#[derive(Debug, Clone, Serialize, Deserialize)]
10594pub struct RdCapitalizationConfig {
10595    /// Enable R&D capitalization.
10596    #[serde(default = "default_true")]
10597    pub enabled: bool,
10598
10599    /// Capitalization rate (0.0-1.0).
10600    #[serde(default = "default_cap_rate")]
10601    pub capitalization_rate: f64,
10602
10603    /// Useful life in years.
10604    #[serde(default = "default_useful_life")]
10605    pub useful_life_years: u32,
10606}
10607
10608fn default_cap_rate() -> f64 {
10609    0.30
10610}
10611
10612fn default_useful_life() -> u32 {
10613    3
10614}
10615
10616impl Default for RdCapitalizationConfig {
10617    fn default() -> Self {
10618        Self {
10619            enabled: true,
10620            capitalization_rate: default_cap_rate(),
10621            useful_life_years: default_useful_life(),
10622        }
10623    }
10624}
10625
10626/// Technology anomaly injection rates.
10627#[derive(Debug, Clone, Serialize, Deserialize)]
10628pub struct TechnologyAnomalyRates {
10629    /// Premature revenue recognition rate.
10630    #[serde(default = "default_premature_rev_rate")]
10631    pub premature_revenue: f64,
10632
10633    /// Side letter abuse rate.
10634    #[serde(default = "default_side_letter_rate")]
10635    pub side_letter_abuse: f64,
10636
10637    /// Channel stuffing rate.
10638    #[serde(default = "default_channel_stuffing_rate")]
10639    pub channel_stuffing: f64,
10640
10641    /// Improper capitalization rate.
10642    #[serde(default = "default_improper_cap_rate")]
10643    pub improper_capitalization: f64,
10644}
10645
10646fn default_premature_rev_rate() -> f64 {
10647    0.015
10648}
10649
10650fn default_side_letter_rate() -> f64 {
10651    0.008
10652}
10653
10654fn default_channel_stuffing_rate() -> f64 {
10655    0.01
10656}
10657
10658fn default_improper_cap_rate() -> f64 {
10659    0.012
10660}
10661
10662impl Default for TechnologyAnomalyRates {
10663    fn default() -> Self {
10664        Self {
10665            premature_revenue: default_premature_rev_rate(),
10666            side_letter_abuse: default_side_letter_rate(),
10667            channel_stuffing: default_channel_stuffing_rate(),
10668            improper_capitalization: default_improper_cap_rate(),
10669        }
10670    }
10671}
10672
10673/// Financial services industry configuration.
10674#[derive(Debug, Clone, Serialize, Deserialize)]
10675pub struct FinancialServicesConfig {
10676    /// Enable financial services-specific generation.
10677    #[serde(default)]
10678    pub enabled: bool,
10679
10680    /// Financial institution type.
10681    #[serde(default = "default_fi_type")]
10682    pub institution_type: String,
10683
10684    /// Regulatory framework.
10685    #[serde(default = "default_fi_regulatory")]
10686    pub regulatory_framework: String,
10687
10688    /// Financial services anomaly injection rates.
10689    #[serde(default)]
10690    pub anomaly_rates: FinancialServicesAnomalyRates,
10691}
10692
10693fn default_fi_type() -> String {
10694    "commercial_bank".to_string()
10695}
10696
10697fn default_fi_regulatory() -> String {
10698    "us_banking".to_string()
10699}
10700
10701impl Default for FinancialServicesConfig {
10702    fn default() -> Self {
10703        Self {
10704            enabled: false,
10705            institution_type: default_fi_type(),
10706            regulatory_framework: default_fi_regulatory(),
10707            anomaly_rates: FinancialServicesAnomalyRates::default(),
10708        }
10709    }
10710}
10711
10712/// Financial services anomaly injection rates.
10713#[derive(Debug, Clone, Serialize, Deserialize)]
10714pub struct FinancialServicesAnomalyRates {
10715    /// Loan fraud rate.
10716    #[serde(default = "default_loan_fraud_rate")]
10717    pub loan_fraud: f64,
10718
10719    /// Trading fraud rate.
10720    #[serde(default = "default_trading_fraud_rate")]
10721    pub trading_fraud: f64,
10722
10723    /// Insurance fraud rate.
10724    #[serde(default = "default_insurance_fraud_rate")]
10725    pub insurance_fraud: f64,
10726
10727    /// Account manipulation rate.
10728    #[serde(default = "default_account_manip_rate")]
10729    pub account_manipulation: f64,
10730}
10731
10732fn default_loan_fraud_rate() -> f64 {
10733    0.01
10734}
10735
10736fn default_trading_fraud_rate() -> f64 {
10737    0.008
10738}
10739
10740fn default_insurance_fraud_rate() -> f64 {
10741    0.012
10742}
10743
10744fn default_account_manip_rate() -> f64 {
10745    0.005
10746}
10747
10748impl Default for FinancialServicesAnomalyRates {
10749    fn default() -> Self {
10750        Self {
10751            loan_fraud: default_loan_fraud_rate(),
10752            trading_fraud: default_trading_fraud_rate(),
10753            insurance_fraud: default_insurance_fraud_rate(),
10754            account_manipulation: default_account_manip_rate(),
10755        }
10756    }
10757}
10758
10759/// Professional services industry configuration.
10760#[derive(Debug, Clone, Serialize, Deserialize)]
10761pub struct ProfessionalServicesConfig {
10762    /// Enable professional services-specific generation.
10763    #[serde(default)]
10764    pub enabled: bool,
10765
10766    /// Firm type.
10767    #[serde(default = "default_firm_type")]
10768    pub firm_type: String,
10769
10770    /// Billing model.
10771    #[serde(default = "default_billing_model")]
10772    pub billing_model: String,
10773
10774    /// Average hourly rate.
10775    #[serde(default = "default_hourly_rate")]
10776    pub avg_hourly_rate: f64,
10777
10778    /// Trust account settings (for law firms).
10779    #[serde(default)]
10780    pub trust_accounting: TrustAccountingConfig,
10781
10782    /// Professional services anomaly injection rates.
10783    #[serde(default)]
10784    pub anomaly_rates: ProfessionalServicesAnomalyRates,
10785}
10786
10787fn default_firm_type() -> String {
10788    "consulting".to_string()
10789}
10790
10791fn default_billing_model() -> String {
10792    "time_and_materials".to_string()
10793}
10794
10795fn default_hourly_rate() -> f64 {
10796    250.0
10797}
10798
10799impl Default for ProfessionalServicesConfig {
10800    fn default() -> Self {
10801        Self {
10802            enabled: false,
10803            firm_type: default_firm_type(),
10804            billing_model: default_billing_model(),
10805            avg_hourly_rate: default_hourly_rate(),
10806            trust_accounting: TrustAccountingConfig::default(),
10807            anomaly_rates: ProfessionalServicesAnomalyRates::default(),
10808        }
10809    }
10810}
10811
10812/// Trust accounting configuration for law firms.
10813#[derive(Debug, Clone, Serialize, Deserialize)]
10814pub struct TrustAccountingConfig {
10815    /// Enable trust accounting.
10816    #[serde(default)]
10817    pub enabled: bool,
10818
10819    /// Require three-way reconciliation.
10820    #[serde(default = "default_true")]
10821    pub require_three_way_reconciliation: bool,
10822}
10823
10824impl Default for TrustAccountingConfig {
10825    fn default() -> Self {
10826        Self {
10827            enabled: false,
10828            require_three_way_reconciliation: true,
10829        }
10830    }
10831}
10832
10833/// Professional services anomaly injection rates.
10834#[derive(Debug, Clone, Serialize, Deserialize)]
10835pub struct ProfessionalServicesAnomalyRates {
10836    /// Time billing fraud rate.
10837    #[serde(default = "default_time_fraud_rate")]
10838    pub time_billing_fraud: f64,
10839
10840    /// Expense report fraud rate.
10841    #[serde(default = "default_expense_fraud_rate")]
10842    pub expense_fraud: f64,
10843
10844    /// Trust misappropriation rate.
10845    #[serde(default = "default_trust_misappropriation_rate")]
10846    pub trust_misappropriation: f64,
10847}
10848
10849fn default_time_fraud_rate() -> f64 {
10850    0.02
10851}
10852
10853fn default_expense_fraud_rate() -> f64 {
10854    0.015
10855}
10856
10857fn default_trust_misappropriation_rate() -> f64 {
10858    0.003
10859}
10860
10861impl Default for ProfessionalServicesAnomalyRates {
10862    fn default() -> Self {
10863        Self {
10864            time_billing_fraud: default_time_fraud_rate(),
10865            expense_fraud: default_expense_fraud_rate(),
10866            trust_misappropriation: default_trust_misappropriation_rate(),
10867        }
10868    }
10869}
10870
10871/// Fingerprint privacy configuration for extraction and synthesis.
10872///
10873/// Controls the privacy parameters used when extracting fingerprints
10874/// from sensitive data. Supports predefined levels or custom (epsilon, delta) tuples.
10875///
10876/// ```yaml
10877/// fingerprint_privacy:
10878///   level: custom
10879///   epsilon: 0.5
10880///   delta: 1.0e-5
10881///   k_anonymity: 10
10882///   composition_method: renyi_dp
10883/// ```
10884#[derive(Debug, Clone, Serialize, Deserialize)]
10885pub struct FingerprintPrivacyConfig {
10886    /// Privacy level preset. Use "custom" for user-specified epsilon/delta.
10887    #[serde(default)]
10888    pub level: String,
10889    /// Custom epsilon value (only used when level = "custom").
10890    #[serde(default = "default_epsilon")]
10891    pub epsilon: f64,
10892    /// Custom delta value for (epsilon, delta)-DP (only used with RDP/zCDP).
10893    #[serde(default = "default_delta")]
10894    pub delta: f64,
10895    /// K-anonymity threshold.
10896    #[serde(default = "default_k_anonymity")]
10897    pub k_anonymity: u32,
10898    /// Composition method: "naive", "advanced", "renyi_dp", "zcdp".
10899    #[serde(default)]
10900    pub composition_method: String,
10901}
10902
10903fn default_epsilon() -> f64 {
10904    1.0
10905}
10906
10907fn default_delta() -> f64 {
10908    1e-5
10909}
10910
10911fn default_k_anonymity() -> u32 {
10912    5
10913}
10914
10915impl Default for FingerprintPrivacyConfig {
10916    fn default() -> Self {
10917        Self {
10918            level: "standard".to_string(),
10919            epsilon: default_epsilon(),
10920            delta: default_delta(),
10921            k_anonymity: default_k_anonymity(),
10922            composition_method: "naive".to_string(),
10923        }
10924    }
10925}
10926
10927/// Quality gates configuration for pass/fail thresholds on generation runs.
10928///
10929/// ```yaml
10930/// quality_gates:
10931///   enabled: true
10932///   profile: strict  # strict, default, lenient, custom
10933///   fail_on_violation: true
10934///   custom_gates:
10935///     - name: benford_compliance
10936///       metric: benford_mad
10937///       threshold: 0.015
10938///       comparison: lte
10939/// ```
10940#[derive(Debug, Clone, Serialize, Deserialize)]
10941pub struct QualityGatesSchemaConfig {
10942    /// Enable quality gate evaluation.
10943    #[serde(default)]
10944    pub enabled: bool,
10945    /// Gate profile: "strict", "default", "lenient", or "custom".
10946    #[serde(default = "default_gate_profile_name")]
10947    pub profile: String,
10948    /// Whether to fail the generation on gate violations.
10949    #[serde(default)]
10950    pub fail_on_violation: bool,
10951    /// Custom gate definitions (used when profile = "custom").
10952    #[serde(default)]
10953    pub custom_gates: Vec<QualityGateEntry>,
10954}
10955
10956fn default_gate_profile_name() -> String {
10957    "default".to_string()
10958}
10959
10960impl Default for QualityGatesSchemaConfig {
10961    fn default() -> Self {
10962        Self {
10963            enabled: false,
10964            profile: default_gate_profile_name(),
10965            fail_on_violation: false,
10966            custom_gates: Vec::new(),
10967        }
10968    }
10969}
10970
10971/// A single quality gate entry in configuration.
10972#[derive(Debug, Clone, Serialize, Deserialize)]
10973pub struct QualityGateEntry {
10974    /// Gate name.
10975    pub name: String,
10976    /// Metric to check: benford_mad, balance_coherence, document_chain_integrity,
10977    /// correlation_preservation, temporal_consistency, privacy_mia_auc,
10978    /// completion_rate, duplicate_rate, referential_integrity, ic_match_rate.
10979    pub metric: String,
10980    /// Threshold value.
10981    pub threshold: f64,
10982    /// Upper threshold for "between" comparison.
10983    #[serde(default)]
10984    pub upper_threshold: Option<f64>,
10985    /// Comparison operator: "gte", "lte", "eq", "between".
10986    #[serde(default = "default_gate_comparison")]
10987    pub comparison: String,
10988}
10989
10990fn default_gate_comparison() -> String {
10991    "gte".to_string()
10992}
10993
10994/// Compliance configuration for regulatory requirements.
10995///
10996/// ```yaml
10997/// compliance:
10998///   content_marking:
10999///     enabled: true
11000///     format: embedded  # embedded, sidecar, both
11001///   article10_report: true
11002/// ```
11003#[derive(Debug, Clone, Default, Serialize, Deserialize)]
11004pub struct ComplianceSchemaConfig {
11005    /// Synthetic content marking configuration (EU AI Act Article 50).
11006    #[serde(default)]
11007    pub content_marking: ContentMarkingSchemaConfig,
11008    /// Generate Article 10 data governance report.
11009    #[serde(default)]
11010    pub article10_report: bool,
11011    /// Certificate configuration for proving DP guarantees.
11012    #[serde(default)]
11013    pub certificates: CertificateSchemaConfig,
11014}
11015
11016/// Configuration for synthetic data certificates.
11017#[derive(Debug, Clone, Default, Serialize, Deserialize)]
11018pub struct CertificateSchemaConfig {
11019    /// Whether certificate generation is enabled.
11020    #[serde(default)]
11021    pub enabled: bool,
11022    /// Environment variable name for the signing key.
11023    #[serde(default)]
11024    pub signing_key_env: Option<String>,
11025    /// Whether to include quality metrics in the certificate.
11026    #[serde(default)]
11027    pub include_quality_metrics: bool,
11028}
11029
11030/// Content marking configuration for synthetic data output.
11031#[derive(Debug, Clone, Serialize, Deserialize)]
11032pub struct ContentMarkingSchemaConfig {
11033    /// Whether content marking is enabled.
11034    #[serde(default = "default_true")]
11035    pub enabled: bool,
11036    /// Marking format: "embedded", "sidecar", or "both".
11037    #[serde(default = "default_marking_format")]
11038    pub format: String,
11039}
11040
11041fn default_marking_format() -> String {
11042    "embedded".to_string()
11043}
11044
11045impl Default for ContentMarkingSchemaConfig {
11046    fn default() -> Self {
11047        Self {
11048            enabled: true,
11049            format: default_marking_format(),
11050        }
11051    }
11052}
11053
11054/// Webhook notification configuration.
11055#[derive(Debug, Clone, Default, Serialize, Deserialize)]
11056pub struct WebhookSchemaConfig {
11057    /// Whether webhooks are enabled.
11058    #[serde(default)]
11059    pub enabled: bool,
11060    /// Webhook endpoint configurations.
11061    #[serde(default)]
11062    pub endpoints: Vec<WebhookEndpointConfig>,
11063}
11064
11065/// Configuration for a single webhook endpoint.
11066#[derive(Debug, Clone, Serialize, Deserialize)]
11067pub struct WebhookEndpointConfig {
11068    /// Target URL for the webhook.
11069    pub url: String,
11070    /// Event types this endpoint subscribes to.
11071    #[serde(default)]
11072    pub events: Vec<String>,
11073    /// Optional secret for HMAC-SHA256 signature.
11074    #[serde(default)]
11075    pub secret: Option<String>,
11076    /// Maximum retry attempts (default: 3).
11077    #[serde(default = "default_webhook_retries")]
11078    pub max_retries: u32,
11079    /// Timeout in seconds (default: 10).
11080    #[serde(default = "default_webhook_timeout")]
11081    pub timeout_secs: u64,
11082}
11083
11084fn default_webhook_retries() -> u32 {
11085    3
11086}
11087fn default_webhook_timeout() -> u64 {
11088    10
11089}
11090
11091// ===== Enterprise Process Chain Config Structs =====
11092
11093// ----- Source-to-Pay (S2C/S2P) -----
11094
11095/// Source-to-Pay configuration covering the entire sourcing lifecycle.
11096#[derive(Debug, Clone, Default, Serialize, Deserialize)]
11097pub struct SourceToPayConfig {
11098    /// Enable source-to-pay generation
11099    #[serde(default)]
11100    pub enabled: bool,
11101    /// Spend analysis configuration
11102    #[serde(default)]
11103    pub spend_analysis: SpendAnalysisConfig,
11104    /// Sourcing project configuration
11105    #[serde(default)]
11106    pub sourcing: SourcingConfig,
11107    /// Supplier qualification configuration
11108    #[serde(default)]
11109    pub qualification: QualificationConfig,
11110    /// RFx event configuration
11111    #[serde(default)]
11112    pub rfx: RfxConfig,
11113    /// Contract configuration
11114    #[serde(default)]
11115    pub contracts: ContractConfig,
11116    /// Catalog configuration
11117    #[serde(default)]
11118    pub catalog: CatalogConfig,
11119    /// Scorecard configuration
11120    #[serde(default)]
11121    pub scorecards: ScorecardConfig,
11122    /// P2P integration settings
11123    #[serde(default)]
11124    pub p2p_integration: P2PIntegrationConfig,
11125}
11126
11127/// Spend analysis configuration.
11128#[derive(Debug, Clone, Serialize, Deserialize)]
11129pub struct SpendAnalysisConfig {
11130    /// HHI threshold for triggering sourcing project
11131    #[serde(default = "default_hhi_threshold")]
11132    pub hhi_threshold: f64,
11133    /// Target spend coverage under contracts
11134    #[serde(default = "default_contract_coverage_target")]
11135    pub contract_coverage_target: f64,
11136}
11137
11138impl Default for SpendAnalysisConfig {
11139    fn default() -> Self {
11140        Self {
11141            hhi_threshold: default_hhi_threshold(),
11142            contract_coverage_target: default_contract_coverage_target(),
11143        }
11144    }
11145}
11146
11147fn default_hhi_threshold() -> f64 {
11148    2500.0
11149}
11150fn default_contract_coverage_target() -> f64 {
11151    0.80
11152}
11153
11154/// Sourcing project configuration.
11155#[derive(Debug, Clone, Serialize, Deserialize)]
11156pub struct SourcingConfig {
11157    /// Number of sourcing projects per year
11158    #[serde(default = "default_sourcing_projects_per_year")]
11159    pub projects_per_year: u32,
11160    /// Months before expiry to trigger renewal project
11161    #[serde(default = "default_renewal_horizon_months")]
11162    pub renewal_horizon_months: u32,
11163    /// Average project duration in months
11164    #[serde(default = "default_project_duration_months")]
11165    pub project_duration_months: u32,
11166}
11167
11168impl Default for SourcingConfig {
11169    fn default() -> Self {
11170        Self {
11171            projects_per_year: default_sourcing_projects_per_year(),
11172            renewal_horizon_months: default_renewal_horizon_months(),
11173            project_duration_months: default_project_duration_months(),
11174        }
11175    }
11176}
11177
11178fn default_sourcing_projects_per_year() -> u32 {
11179    10
11180}
11181fn default_renewal_horizon_months() -> u32 {
11182    3
11183}
11184fn default_project_duration_months() -> u32 {
11185    4
11186}
11187
11188/// Supplier qualification configuration.
11189#[derive(Debug, Clone, Serialize, Deserialize)]
11190pub struct QualificationConfig {
11191    /// Pass rate for qualification
11192    #[serde(default = "default_qualification_pass_rate")]
11193    pub pass_rate: f64,
11194    /// Qualification validity in days
11195    #[serde(default = "default_qualification_validity_days")]
11196    pub validity_days: u32,
11197    /// Financial stability weight
11198    #[serde(default = "default_financial_weight")]
11199    pub financial_weight: f64,
11200    /// Quality management weight
11201    #[serde(default = "default_quality_weight")]
11202    pub quality_weight: f64,
11203    /// Delivery performance weight
11204    #[serde(default = "default_delivery_weight")]
11205    pub delivery_weight: f64,
11206    /// Compliance weight
11207    #[serde(default = "default_compliance_weight")]
11208    pub compliance_weight: f64,
11209}
11210
11211impl Default for QualificationConfig {
11212    fn default() -> Self {
11213        Self {
11214            pass_rate: default_qualification_pass_rate(),
11215            validity_days: default_qualification_validity_days(),
11216            financial_weight: default_financial_weight(),
11217            quality_weight: default_quality_weight(),
11218            delivery_weight: default_delivery_weight(),
11219            compliance_weight: default_compliance_weight(),
11220        }
11221    }
11222}
11223
11224fn default_qualification_pass_rate() -> f64 {
11225    0.75
11226}
11227fn default_qualification_validity_days() -> u32 {
11228    365
11229}
11230fn default_financial_weight() -> f64 {
11231    0.25
11232}
11233fn default_quality_weight() -> f64 {
11234    0.30
11235}
11236fn default_delivery_weight() -> f64 {
11237    0.25
11238}
11239fn default_compliance_weight() -> f64 {
11240    0.20
11241}
11242
11243/// RFx event configuration.
11244#[derive(Debug, Clone, Serialize, Deserialize)]
11245pub struct RfxConfig {
11246    /// Spend threshold above which RFI is required before RFP
11247    #[serde(default = "default_rfi_threshold")]
11248    pub rfi_threshold: f64,
11249    /// Minimum vendors invited per RFx
11250    #[serde(default = "default_min_invited_vendors")]
11251    pub min_invited_vendors: u32,
11252    /// Maximum vendors invited per RFx
11253    #[serde(default = "default_max_invited_vendors")]
11254    pub max_invited_vendors: u32,
11255    /// Response rate (% of invited vendors that submit bids)
11256    #[serde(default = "default_response_rate")]
11257    pub response_rate: f64,
11258    /// Default price weight in evaluation
11259    #[serde(default = "default_price_weight")]
11260    pub default_price_weight: f64,
11261    /// Default quality weight in evaluation
11262    #[serde(default = "default_rfx_quality_weight")]
11263    pub default_quality_weight: f64,
11264    /// Default delivery weight in evaluation
11265    #[serde(default = "default_rfx_delivery_weight")]
11266    pub default_delivery_weight: f64,
11267}
11268
11269impl Default for RfxConfig {
11270    fn default() -> Self {
11271        Self {
11272            rfi_threshold: default_rfi_threshold(),
11273            min_invited_vendors: default_min_invited_vendors(),
11274            max_invited_vendors: default_max_invited_vendors(),
11275            response_rate: default_response_rate(),
11276            default_price_weight: default_price_weight(),
11277            default_quality_weight: default_rfx_quality_weight(),
11278            default_delivery_weight: default_rfx_delivery_weight(),
11279        }
11280    }
11281}
11282
11283fn default_rfi_threshold() -> f64 {
11284    100_000.0
11285}
11286fn default_min_invited_vendors() -> u32 {
11287    3
11288}
11289fn default_max_invited_vendors() -> u32 {
11290    8
11291}
11292fn default_response_rate() -> f64 {
11293    0.70
11294}
11295fn default_price_weight() -> f64 {
11296    0.40
11297}
11298fn default_rfx_quality_weight() -> f64 {
11299    0.35
11300}
11301fn default_rfx_delivery_weight() -> f64 {
11302    0.25
11303}
11304
11305/// Contract configuration.
11306#[derive(Debug, Clone, Serialize, Deserialize)]
11307pub struct ContractConfig {
11308    /// Minimum contract duration in months
11309    #[serde(default = "default_min_contract_months")]
11310    pub min_duration_months: u32,
11311    /// Maximum contract duration in months
11312    #[serde(default = "default_max_contract_months")]
11313    pub max_duration_months: u32,
11314    /// Auto-renewal rate
11315    #[serde(default = "default_auto_renewal_rate")]
11316    pub auto_renewal_rate: f64,
11317    /// Amendment rate (% of contracts with at least one amendment)
11318    #[serde(default = "default_amendment_rate")]
11319    pub amendment_rate: f64,
11320    /// Distribution of contract types
11321    #[serde(default)]
11322    pub type_distribution: ContractTypeDistribution,
11323}
11324
11325impl Default for ContractConfig {
11326    fn default() -> Self {
11327        Self {
11328            min_duration_months: default_min_contract_months(),
11329            max_duration_months: default_max_contract_months(),
11330            auto_renewal_rate: default_auto_renewal_rate(),
11331            amendment_rate: default_amendment_rate(),
11332            type_distribution: ContractTypeDistribution::default(),
11333        }
11334    }
11335}
11336
11337fn default_min_contract_months() -> u32 {
11338    12
11339}
11340fn default_max_contract_months() -> u32 {
11341    36
11342}
11343fn default_auto_renewal_rate() -> f64 {
11344    0.40
11345}
11346fn default_amendment_rate() -> f64 {
11347    0.20
11348}
11349
11350/// Distribution of contract types.
11351#[derive(Debug, Clone, Serialize, Deserialize)]
11352pub struct ContractTypeDistribution {
11353    /// Fixed price percentage
11354    #[serde(default = "default_fixed_price_pct")]
11355    pub fixed_price: f64,
11356    /// Blanket/framework percentage
11357    #[serde(default = "default_blanket_pct")]
11358    pub blanket: f64,
11359    /// Time and materials percentage
11360    #[serde(default = "default_time_materials_pct")]
11361    pub time_and_materials: f64,
11362    /// Service agreement percentage
11363    #[serde(default = "default_service_agreement_pct")]
11364    pub service_agreement: f64,
11365}
11366
11367impl Default for ContractTypeDistribution {
11368    fn default() -> Self {
11369        Self {
11370            fixed_price: default_fixed_price_pct(),
11371            blanket: default_blanket_pct(),
11372            time_and_materials: default_time_materials_pct(),
11373            service_agreement: default_service_agreement_pct(),
11374        }
11375    }
11376}
11377
11378fn default_fixed_price_pct() -> f64 {
11379    0.40
11380}
11381fn default_blanket_pct() -> f64 {
11382    0.30
11383}
11384fn default_time_materials_pct() -> f64 {
11385    0.15
11386}
11387fn default_service_agreement_pct() -> f64 {
11388    0.15
11389}
11390
11391/// Catalog configuration.
11392#[derive(Debug, Clone, Serialize, Deserialize)]
11393pub struct CatalogConfig {
11394    /// Percentage of catalog items marked as preferred
11395    #[serde(default = "default_preferred_vendor_flag_rate")]
11396    pub preferred_vendor_flag_rate: f64,
11397    /// Rate of materials with multiple sources in catalog
11398    #[serde(default = "default_multi_source_rate")]
11399    pub multi_source_rate: f64,
11400}
11401
11402impl Default for CatalogConfig {
11403    fn default() -> Self {
11404        Self {
11405            preferred_vendor_flag_rate: default_preferred_vendor_flag_rate(),
11406            multi_source_rate: default_multi_source_rate(),
11407        }
11408    }
11409}
11410
11411fn default_preferred_vendor_flag_rate() -> f64 {
11412    0.70
11413}
11414fn default_multi_source_rate() -> f64 {
11415    0.25
11416}
11417
11418/// Scorecard configuration.
11419#[derive(Debug, Clone, Serialize, Deserialize)]
11420pub struct ScorecardConfig {
11421    /// Scorecard review frequency (quarterly, monthly)
11422    #[serde(default = "default_scorecard_frequency")]
11423    pub frequency: String,
11424    /// On-time delivery weight in overall score
11425    #[serde(default = "default_otd_weight")]
11426    pub on_time_delivery_weight: f64,
11427    /// Quality weight in overall score
11428    #[serde(default = "default_quality_score_weight")]
11429    pub quality_weight: f64,
11430    /// Price competitiveness weight
11431    #[serde(default = "default_price_score_weight")]
11432    pub price_weight: f64,
11433    /// Responsiveness weight
11434    #[serde(default = "default_responsiveness_weight")]
11435    pub responsiveness_weight: f64,
11436    /// Grade A threshold (score >= this)
11437    #[serde(default = "default_grade_a_threshold")]
11438    pub grade_a_threshold: f64,
11439    /// Grade B threshold
11440    #[serde(default = "default_grade_b_threshold")]
11441    pub grade_b_threshold: f64,
11442    /// Grade C threshold
11443    #[serde(default = "default_grade_c_threshold")]
11444    pub grade_c_threshold: f64,
11445}
11446
11447impl Default for ScorecardConfig {
11448    fn default() -> Self {
11449        Self {
11450            frequency: default_scorecard_frequency(),
11451            on_time_delivery_weight: default_otd_weight(),
11452            quality_weight: default_quality_score_weight(),
11453            price_weight: default_price_score_weight(),
11454            responsiveness_weight: default_responsiveness_weight(),
11455            grade_a_threshold: default_grade_a_threshold(),
11456            grade_b_threshold: default_grade_b_threshold(),
11457            grade_c_threshold: default_grade_c_threshold(),
11458        }
11459    }
11460}
11461
11462fn default_scorecard_frequency() -> String {
11463    "quarterly".to_string()
11464}
11465fn default_otd_weight() -> f64 {
11466    0.30
11467}
11468fn default_quality_score_weight() -> f64 {
11469    0.30
11470}
11471fn default_price_score_weight() -> f64 {
11472    0.25
11473}
11474fn default_responsiveness_weight() -> f64 {
11475    0.15
11476}
11477fn default_grade_a_threshold() -> f64 {
11478    90.0
11479}
11480fn default_grade_b_threshold() -> f64 {
11481    75.0
11482}
11483fn default_grade_c_threshold() -> f64 {
11484    60.0
11485}
11486
11487/// P2P integration settings for contract enforcement.
11488#[derive(Debug, Clone, Serialize, Deserialize)]
11489pub struct P2PIntegrationConfig {
11490    /// Rate of off-contract (maverick) purchases
11491    #[serde(default = "default_off_contract_rate")]
11492    pub off_contract_rate: f64,
11493    /// Price tolerance for contract price validation
11494    #[serde(default = "default_price_tolerance")]
11495    pub price_tolerance: f64,
11496    /// Whether to enforce catalog ordering
11497    #[serde(default)]
11498    pub catalog_enforcement: bool,
11499}
11500
11501impl Default for P2PIntegrationConfig {
11502    fn default() -> Self {
11503        Self {
11504            off_contract_rate: default_off_contract_rate(),
11505            price_tolerance: default_price_tolerance(),
11506            catalog_enforcement: false,
11507        }
11508    }
11509}
11510
11511fn default_off_contract_rate() -> f64 {
11512    0.15
11513}
11514fn default_price_tolerance() -> f64 {
11515    0.02
11516}
11517
11518// ----- Financial Reporting -----
11519
11520/// Financial reporting configuration.
11521#[derive(Debug, Clone, Serialize, Deserialize)]
11522pub struct FinancialReportingConfig {
11523    /// Enable financial reporting generation
11524    #[serde(default)]
11525    pub enabled: bool,
11526    /// Generate balance sheet
11527    #[serde(default = "default_true")]
11528    pub generate_balance_sheet: bool,
11529    /// Generate income statement
11530    #[serde(default = "default_true")]
11531    pub generate_income_statement: bool,
11532    /// Generate cash flow statement
11533    #[serde(default = "default_true")]
11534    pub generate_cash_flow: bool,
11535    /// Generate changes in equity statement
11536    #[serde(default = "default_true")]
11537    pub generate_changes_in_equity: bool,
11538    /// Number of comparative periods
11539    #[serde(default = "default_comparative_periods")]
11540    pub comparative_periods: u32,
11541    /// Management KPIs configuration
11542    #[serde(default)]
11543    pub management_kpis: ManagementKpisConfig,
11544    /// Budget configuration
11545    #[serde(default)]
11546    pub budgets: BudgetConfig,
11547}
11548
11549impl Default for FinancialReportingConfig {
11550    fn default() -> Self {
11551        Self {
11552            enabled: false,
11553            generate_balance_sheet: true,
11554            generate_income_statement: true,
11555            generate_cash_flow: true,
11556            generate_changes_in_equity: true,
11557            comparative_periods: default_comparative_periods(),
11558            management_kpis: ManagementKpisConfig::default(),
11559            budgets: BudgetConfig::default(),
11560        }
11561    }
11562}
11563
11564fn default_comparative_periods() -> u32 {
11565    1
11566}
11567
11568/// Management KPIs configuration.
11569#[derive(Debug, Clone, Default, Serialize, Deserialize)]
11570pub struct ManagementKpisConfig {
11571    /// Enable KPI generation
11572    #[serde(default)]
11573    pub enabled: bool,
11574    /// KPI calculation frequency (monthly, quarterly)
11575    #[serde(default = "default_kpi_frequency")]
11576    pub frequency: String,
11577}
11578
11579fn default_kpi_frequency() -> String {
11580    "monthly".to_string()
11581}
11582
11583/// Budget configuration.
11584#[derive(Debug, Clone, Serialize, Deserialize)]
11585pub struct BudgetConfig {
11586    /// Enable budget generation
11587    #[serde(default)]
11588    pub enabled: bool,
11589    /// Expected revenue growth rate for budgeting
11590    #[serde(default = "default_revenue_growth_rate")]
11591    pub revenue_growth_rate: f64,
11592    /// Expected expense inflation rate
11593    #[serde(default = "default_expense_inflation_rate")]
11594    pub expense_inflation_rate: f64,
11595    /// Random noise to add to budget vs actual
11596    #[serde(default = "default_variance_noise")]
11597    pub variance_noise: f64,
11598}
11599
11600impl Default for BudgetConfig {
11601    fn default() -> Self {
11602        Self {
11603            enabled: false,
11604            revenue_growth_rate: default_revenue_growth_rate(),
11605            expense_inflation_rate: default_expense_inflation_rate(),
11606            variance_noise: default_variance_noise(),
11607        }
11608    }
11609}
11610
11611fn default_revenue_growth_rate() -> f64 {
11612    0.05
11613}
11614fn default_expense_inflation_rate() -> f64 {
11615    0.03
11616}
11617fn default_variance_noise() -> f64 {
11618    0.10
11619}
11620
11621// ----- HR Configuration -----
11622
11623/// HR (Hire-to-Retire) process configuration.
11624#[derive(Debug, Clone, Default, Serialize, Deserialize)]
11625pub struct HrConfig {
11626    /// Enable HR generation
11627    #[serde(default)]
11628    pub enabled: bool,
11629    /// Payroll configuration
11630    #[serde(default)]
11631    pub payroll: PayrollConfig,
11632    /// Time and attendance configuration
11633    #[serde(default)]
11634    pub time_attendance: TimeAttendanceConfig,
11635    /// Expense management configuration
11636    #[serde(default)]
11637    pub expenses: ExpenseConfig,
11638}
11639
11640/// Payroll configuration.
11641#[derive(Debug, Clone, Serialize, Deserialize)]
11642pub struct PayrollConfig {
11643    /// Enable payroll generation
11644    #[serde(default = "default_true")]
11645    pub enabled: bool,
11646    /// Pay frequency (monthly, biweekly, weekly)
11647    #[serde(default = "default_pay_frequency")]
11648    pub pay_frequency: String,
11649    /// Salary ranges by job level
11650    #[serde(default)]
11651    pub salary_ranges: PayrollSalaryRanges,
11652    /// Effective tax rates
11653    #[serde(default)]
11654    pub tax_rates: PayrollTaxRates,
11655    /// Benefits enrollment rate
11656    #[serde(default = "default_benefits_enrollment_rate")]
11657    pub benefits_enrollment_rate: f64,
11658    /// Retirement plan participation rate
11659    #[serde(default = "default_retirement_participation_rate")]
11660    pub retirement_participation_rate: f64,
11661}
11662
11663impl Default for PayrollConfig {
11664    fn default() -> Self {
11665        Self {
11666            enabled: true,
11667            pay_frequency: default_pay_frequency(),
11668            salary_ranges: PayrollSalaryRanges::default(),
11669            tax_rates: PayrollTaxRates::default(),
11670            benefits_enrollment_rate: default_benefits_enrollment_rate(),
11671            retirement_participation_rate: default_retirement_participation_rate(),
11672        }
11673    }
11674}
11675
11676fn default_pay_frequency() -> String {
11677    "monthly".to_string()
11678}
11679fn default_benefits_enrollment_rate() -> f64 {
11680    0.60
11681}
11682fn default_retirement_participation_rate() -> f64 {
11683    0.45
11684}
11685
11686/// Salary ranges by job level.
11687#[derive(Debug, Clone, Serialize, Deserialize)]
11688pub struct PayrollSalaryRanges {
11689    /// Staff level min/max
11690    #[serde(default = "default_staff_min")]
11691    pub staff_min: f64,
11692    #[serde(default = "default_staff_max")]
11693    pub staff_max: f64,
11694    /// Manager level min/max
11695    #[serde(default = "default_manager_min")]
11696    pub manager_min: f64,
11697    #[serde(default = "default_manager_max")]
11698    pub manager_max: f64,
11699    /// Director level min/max
11700    #[serde(default = "default_director_min")]
11701    pub director_min: f64,
11702    #[serde(default = "default_director_max")]
11703    pub director_max: f64,
11704    /// Executive level min/max
11705    #[serde(default = "default_executive_min")]
11706    pub executive_min: f64,
11707    #[serde(default = "default_executive_max")]
11708    pub executive_max: f64,
11709}
11710
11711impl Default for PayrollSalaryRanges {
11712    fn default() -> Self {
11713        Self {
11714            staff_min: default_staff_min(),
11715            staff_max: default_staff_max(),
11716            manager_min: default_manager_min(),
11717            manager_max: default_manager_max(),
11718            director_min: default_director_min(),
11719            director_max: default_director_max(),
11720            executive_min: default_executive_min(),
11721            executive_max: default_executive_max(),
11722        }
11723    }
11724}
11725
11726fn default_staff_min() -> f64 {
11727    50_000.0
11728}
11729fn default_staff_max() -> f64 {
11730    70_000.0
11731}
11732fn default_manager_min() -> f64 {
11733    80_000.0
11734}
11735fn default_manager_max() -> f64 {
11736    120_000.0
11737}
11738fn default_director_min() -> f64 {
11739    120_000.0
11740}
11741fn default_director_max() -> f64 {
11742    180_000.0
11743}
11744fn default_executive_min() -> f64 {
11745    180_000.0
11746}
11747fn default_executive_max() -> f64 {
11748    350_000.0
11749}
11750
11751/// Effective tax rates for payroll.
11752#[derive(Debug, Clone, Serialize, Deserialize)]
11753pub struct PayrollTaxRates {
11754    /// Federal effective tax rate
11755    #[serde(default = "default_federal_rate")]
11756    pub federal_effective: f64,
11757    /// State effective tax rate
11758    #[serde(default = "default_state_rate")]
11759    pub state_effective: f64,
11760    /// FICA/social security rate
11761    #[serde(default = "default_fica_rate")]
11762    pub fica: f64,
11763}
11764
11765impl Default for PayrollTaxRates {
11766    fn default() -> Self {
11767        Self {
11768            federal_effective: default_federal_rate(),
11769            state_effective: default_state_rate(),
11770            fica: default_fica_rate(),
11771        }
11772    }
11773}
11774
11775fn default_federal_rate() -> f64 {
11776    0.22
11777}
11778fn default_state_rate() -> f64 {
11779    0.05
11780}
11781fn default_fica_rate() -> f64 {
11782    0.0765
11783}
11784
11785/// Time and attendance configuration.
11786#[derive(Debug, Clone, Serialize, Deserialize)]
11787pub struct TimeAttendanceConfig {
11788    /// Enable time tracking
11789    #[serde(default = "default_true")]
11790    pub enabled: bool,
11791    /// Overtime rate (% of employees with overtime in a period)
11792    #[serde(default = "default_overtime_rate")]
11793    pub overtime_rate: f64,
11794}
11795
11796impl Default for TimeAttendanceConfig {
11797    fn default() -> Self {
11798        Self {
11799            enabled: true,
11800            overtime_rate: default_overtime_rate(),
11801        }
11802    }
11803}
11804
11805fn default_overtime_rate() -> f64 {
11806    0.10
11807}
11808
11809/// Expense management configuration.
11810#[derive(Debug, Clone, Serialize, Deserialize)]
11811pub struct ExpenseConfig {
11812    /// Enable expense report generation
11813    #[serde(default = "default_true")]
11814    pub enabled: bool,
11815    /// Rate of employees submitting expenses per month
11816    #[serde(default = "default_expense_submission_rate")]
11817    pub submission_rate: f64,
11818    /// Rate of policy violations
11819    #[serde(default = "default_policy_violation_rate")]
11820    pub policy_violation_rate: f64,
11821}
11822
11823impl Default for ExpenseConfig {
11824    fn default() -> Self {
11825        Self {
11826            enabled: true,
11827            submission_rate: default_expense_submission_rate(),
11828            policy_violation_rate: default_policy_violation_rate(),
11829        }
11830    }
11831}
11832
11833fn default_expense_submission_rate() -> f64 {
11834    0.30
11835}
11836fn default_policy_violation_rate() -> f64 {
11837    0.08
11838}
11839
11840// ----- Manufacturing Configuration -----
11841
11842/// Manufacturing process configuration (production orders, WIP, routing).
11843#[derive(Debug, Clone, Default, Serialize, Deserialize)]
11844pub struct ManufacturingProcessConfig {
11845    /// Enable manufacturing generation
11846    #[serde(default)]
11847    pub enabled: bool,
11848    /// Production order configuration
11849    #[serde(default)]
11850    pub production_orders: ProductionOrderConfig,
11851    /// Costing configuration
11852    #[serde(default)]
11853    pub costing: ManufacturingCostingConfig,
11854    /// Routing configuration
11855    #[serde(default)]
11856    pub routing: RoutingConfig,
11857}
11858
11859/// Production order configuration.
11860#[derive(Debug, Clone, Serialize, Deserialize)]
11861pub struct ProductionOrderConfig {
11862    /// Orders per month
11863    #[serde(default = "default_prod_orders_per_month")]
11864    pub orders_per_month: u32,
11865    /// Average batch size
11866    #[serde(default = "default_prod_avg_batch_size")]
11867    pub avg_batch_size: u32,
11868    /// Yield rate
11869    #[serde(default = "default_prod_yield_rate")]
11870    pub yield_rate: f64,
11871    /// Make-to-order rate (vs make-to-stock)
11872    #[serde(default = "default_prod_make_to_order_rate")]
11873    pub make_to_order_rate: f64,
11874    /// Rework rate
11875    #[serde(default = "default_prod_rework_rate")]
11876    pub rework_rate: f64,
11877}
11878
11879impl Default for ProductionOrderConfig {
11880    fn default() -> Self {
11881        Self {
11882            orders_per_month: default_prod_orders_per_month(),
11883            avg_batch_size: default_prod_avg_batch_size(),
11884            yield_rate: default_prod_yield_rate(),
11885            make_to_order_rate: default_prod_make_to_order_rate(),
11886            rework_rate: default_prod_rework_rate(),
11887        }
11888    }
11889}
11890
11891fn default_prod_orders_per_month() -> u32 {
11892    50
11893}
11894fn default_prod_avg_batch_size() -> u32 {
11895    100
11896}
11897fn default_prod_yield_rate() -> f64 {
11898    0.97
11899}
11900fn default_prod_make_to_order_rate() -> f64 {
11901    0.20
11902}
11903fn default_prod_rework_rate() -> f64 {
11904    0.03
11905}
11906
11907/// Manufacturing costing configuration.
11908#[derive(Debug, Clone, Serialize, Deserialize)]
11909pub struct ManufacturingCostingConfig {
11910    /// Labor rate per hour
11911    #[serde(default = "default_labor_rate")]
11912    pub labor_rate_per_hour: f64,
11913    /// Overhead application rate (multiplier on direct labor)
11914    #[serde(default = "default_overhead_rate")]
11915    pub overhead_rate: f64,
11916    /// Standard cost update frequency
11917    #[serde(default = "default_cost_update_frequency")]
11918    pub standard_cost_update_frequency: String,
11919}
11920
11921impl Default for ManufacturingCostingConfig {
11922    fn default() -> Self {
11923        Self {
11924            labor_rate_per_hour: default_labor_rate(),
11925            overhead_rate: default_overhead_rate(),
11926            standard_cost_update_frequency: default_cost_update_frequency(),
11927        }
11928    }
11929}
11930
11931fn default_labor_rate() -> f64 {
11932    35.0
11933}
11934fn default_overhead_rate() -> f64 {
11935    1.50
11936}
11937fn default_cost_update_frequency() -> String {
11938    "quarterly".to_string()
11939}
11940
11941/// Routing configuration for production operations.
11942#[derive(Debug, Clone, Serialize, Deserialize)]
11943pub struct RoutingConfig {
11944    /// Average number of operations per routing
11945    #[serde(default = "default_avg_operations")]
11946    pub avg_operations: u32,
11947    /// Average setup time in hours
11948    #[serde(default = "default_setup_time")]
11949    pub setup_time_hours: f64,
11950    /// Run time variation coefficient
11951    #[serde(default = "default_run_time_variation")]
11952    pub run_time_variation: f64,
11953}
11954
11955impl Default for RoutingConfig {
11956    fn default() -> Self {
11957        Self {
11958            avg_operations: default_avg_operations(),
11959            setup_time_hours: default_setup_time(),
11960            run_time_variation: default_run_time_variation(),
11961        }
11962    }
11963}
11964
11965fn default_avg_operations() -> u32 {
11966    4
11967}
11968fn default_setup_time() -> f64 {
11969    1.5
11970}
11971fn default_run_time_variation() -> f64 {
11972    0.15
11973}
11974
11975// ----- Sales Quote Configuration -----
11976
11977/// Sales quote (quote-to-order) pipeline configuration.
11978#[derive(Debug, Clone, Serialize, Deserialize)]
11979pub struct SalesQuoteConfig {
11980    /// Enable sales quote generation
11981    #[serde(default)]
11982    pub enabled: bool,
11983    /// Quotes per month
11984    #[serde(default = "default_quotes_per_month")]
11985    pub quotes_per_month: u32,
11986    /// Win rate (fraction of quotes that convert to orders)
11987    #[serde(default = "default_quote_win_rate")]
11988    pub win_rate: f64,
11989    /// Average quote validity in days
11990    #[serde(default = "default_quote_validity_days")]
11991    pub validity_days: u32,
11992}
11993
11994impl Default for SalesQuoteConfig {
11995    fn default() -> Self {
11996        Self {
11997            enabled: false,
11998            quotes_per_month: default_quotes_per_month(),
11999            win_rate: default_quote_win_rate(),
12000            validity_days: default_quote_validity_days(),
12001        }
12002    }
12003}
12004
12005fn default_quotes_per_month() -> u32 {
12006    30
12007}
12008fn default_quote_win_rate() -> f64 {
12009    0.35
12010}
12011fn default_quote_validity_days() -> u32 {
12012    30
12013}
12014
12015// =============================================================================
12016// Tax Accounting Configuration
12017// =============================================================================
12018
12019/// Tax accounting configuration.
12020///
12021/// Controls generation of tax-related data including VAT/GST, sales tax,
12022/// withholding tax, tax provisions, and payroll tax across multiple jurisdictions.
12023#[derive(Debug, Clone, Serialize, Deserialize)]
12024pub struct TaxConfig {
12025    /// Whether tax generation is enabled.
12026    #[serde(default)]
12027    pub enabled: bool,
12028    /// Tax jurisdiction configuration.
12029    #[serde(default)]
12030    pub jurisdictions: TaxJurisdictionConfig,
12031    /// VAT/GST configuration.
12032    #[serde(default)]
12033    pub vat_gst: VatGstConfig,
12034    /// Sales tax configuration.
12035    #[serde(default)]
12036    pub sales_tax: SalesTaxConfig,
12037    /// Withholding tax configuration.
12038    #[serde(default)]
12039    pub withholding: WithholdingTaxSchemaConfig,
12040    /// Tax provision configuration.
12041    #[serde(default)]
12042    pub provisions: TaxProvisionSchemaConfig,
12043    /// Payroll tax configuration.
12044    #[serde(default)]
12045    pub payroll_tax: PayrollTaxSchemaConfig,
12046    /// Anomaly injection rate for tax data (0.0 to 1.0).
12047    #[serde(default = "default_tax_anomaly_rate")]
12048    pub anomaly_rate: f64,
12049}
12050
12051fn default_tax_anomaly_rate() -> f64 {
12052    0.03
12053}
12054
12055impl Default for TaxConfig {
12056    fn default() -> Self {
12057        Self {
12058            enabled: false,
12059            jurisdictions: TaxJurisdictionConfig::default(),
12060            vat_gst: VatGstConfig::default(),
12061            sales_tax: SalesTaxConfig::default(),
12062            withholding: WithholdingTaxSchemaConfig::default(),
12063            provisions: TaxProvisionSchemaConfig::default(),
12064            payroll_tax: PayrollTaxSchemaConfig::default(),
12065            anomaly_rate: default_tax_anomaly_rate(),
12066        }
12067    }
12068}
12069
12070/// Tax jurisdiction configuration.
12071///
12072/// Specifies which countries and subnational jurisdictions to include
12073/// when generating tax data.
12074#[derive(Debug, Clone, Default, Serialize, Deserialize)]
12075pub struct TaxJurisdictionConfig {
12076    /// List of country codes to include (e.g., ["US", "DE", "GB"]).
12077    #[serde(default)]
12078    pub countries: Vec<String>,
12079    /// Whether to include subnational jurisdictions (e.g., US states, Canadian provinces).
12080    #[serde(default)]
12081    pub include_subnational: bool,
12082}
12083
12084/// VAT/GST configuration.
12085///
12086/// Controls generation of Value Added Tax / Goods and Services Tax data,
12087/// including standard and reduced rates, exempt categories, and reverse charge.
12088#[derive(Debug, Clone, Serialize, Deserialize)]
12089pub struct VatGstConfig {
12090    /// Whether VAT/GST generation is enabled.
12091    #[serde(default)]
12092    pub enabled: bool,
12093    /// Standard VAT/GST rates by country code (e.g., {"DE": 0.19, "GB": 0.20}).
12094    #[serde(default)]
12095    pub standard_rates: std::collections::HashMap<String, f64>,
12096    /// Reduced VAT/GST rates by country code (e.g., {"DE": 0.07, "GB": 0.05}).
12097    #[serde(default)]
12098    pub reduced_rates: std::collections::HashMap<String, f64>,
12099    /// Categories exempt from VAT/GST (e.g., ["financial_services", "healthcare"]).
12100    #[serde(default)]
12101    pub exempt_categories: Vec<String>,
12102    /// Whether to apply reverse charge mechanism for cross-border B2B transactions.
12103    #[serde(default = "default_true")]
12104    pub reverse_charge: bool,
12105}
12106
12107impl Default for VatGstConfig {
12108    fn default() -> Self {
12109        Self {
12110            enabled: false,
12111            standard_rates: std::collections::HashMap::new(),
12112            reduced_rates: std::collections::HashMap::new(),
12113            exempt_categories: Vec::new(),
12114            reverse_charge: true,
12115        }
12116    }
12117}
12118
12119/// Sales tax configuration.
12120///
12121/// Controls generation of US-style sales tax data including nexus determination.
12122#[derive(Debug, Clone, Default, Serialize, Deserialize)]
12123pub struct SalesTaxConfig {
12124    /// Whether sales tax generation is enabled.
12125    #[serde(default)]
12126    pub enabled: bool,
12127    /// US states where the company has nexus (e.g., ["CA", "NY", "TX"]).
12128    #[serde(default)]
12129    pub nexus_states: Vec<String>,
12130}
12131
12132/// Withholding tax configuration.
12133///
12134/// Controls generation of withholding tax data for cross-border payments,
12135/// including treaty network and rate overrides.
12136#[derive(Debug, Clone, Serialize, Deserialize)]
12137pub struct WithholdingTaxSchemaConfig {
12138    /// Whether withholding tax generation is enabled.
12139    #[serde(default)]
12140    pub enabled: bool,
12141    /// Whether to simulate a treaty network with reduced rates.
12142    #[serde(default = "default_true")]
12143    pub treaty_network: bool,
12144    /// Default withholding tax rate for non-treaty countries (0.0 to 1.0).
12145    #[serde(default = "default_withholding_rate")]
12146    pub default_rate: f64,
12147    /// Reduced withholding tax rate for treaty countries (0.0 to 1.0).
12148    #[serde(default = "default_treaty_reduced_rate")]
12149    pub treaty_reduced_rate: f64,
12150}
12151
12152fn default_withholding_rate() -> f64 {
12153    0.30
12154}
12155
12156fn default_treaty_reduced_rate() -> f64 {
12157    0.15
12158}
12159
12160impl Default for WithholdingTaxSchemaConfig {
12161    fn default() -> Self {
12162        Self {
12163            enabled: false,
12164            treaty_network: true,
12165            default_rate: default_withholding_rate(),
12166            treaty_reduced_rate: default_treaty_reduced_rate(),
12167        }
12168    }
12169}
12170
12171/// Tax provision configuration.
12172///
12173/// Controls generation of tax provision data including statutory rates
12174/// and uncertain tax positions (ASC 740 / IAS 12).
12175#[derive(Debug, Clone, Serialize, Deserialize)]
12176pub struct TaxProvisionSchemaConfig {
12177    /// Whether tax provision generation is enabled.
12178    /// Defaults to true when tax is enabled, as provisions are typically required.
12179    #[serde(default = "default_true")]
12180    pub enabled: bool,
12181    /// Statutory corporate tax rate (0.0 to 1.0).
12182    #[serde(default = "default_statutory_rate")]
12183    pub statutory_rate: f64,
12184    /// Whether to generate uncertain tax positions (FIN 48 / IFRIC 23).
12185    #[serde(default = "default_true")]
12186    pub uncertain_positions: bool,
12187}
12188
12189fn default_statutory_rate() -> f64 {
12190    0.21
12191}
12192
12193impl Default for TaxProvisionSchemaConfig {
12194    fn default() -> Self {
12195        Self {
12196            enabled: true,
12197            statutory_rate: default_statutory_rate(),
12198            uncertain_positions: true,
12199        }
12200    }
12201}
12202
12203/// Payroll tax configuration.
12204///
12205/// Controls generation of payroll tax data (employer/employee contributions,
12206/// social security, Medicare, etc.).
12207#[derive(Debug, Clone, Default, Serialize, Deserialize)]
12208pub struct PayrollTaxSchemaConfig {
12209    /// Whether payroll tax generation is enabled.
12210    #[serde(default)]
12211    pub enabled: bool,
12212}
12213
12214// ---------------------------------------------------------------------------
12215// Treasury & Cash Management Configuration
12216// ---------------------------------------------------------------------------
12217
12218/// Treasury and cash management configuration.
12219///
12220/// Controls generation of cash positions, forecasts, pooling, hedging
12221/// instruments (ASC 815 / IFRS 9), debt instruments with covenants,
12222/// bank guarantees, and intercompany netting runs.
12223#[derive(Debug, Clone, Serialize, Deserialize)]
12224pub struct TreasuryConfig {
12225    /// Whether treasury generation is enabled.
12226    #[serde(default)]
12227    pub enabled: bool,
12228    /// Cash positioning configuration.
12229    #[serde(default)]
12230    pub cash_positioning: CashPositioningConfig,
12231    /// Cash forecasting configuration.
12232    #[serde(default)]
12233    pub cash_forecasting: CashForecastingConfig,
12234    /// Cash pooling configuration.
12235    #[serde(default)]
12236    pub cash_pooling: CashPoolingConfig,
12237    /// Hedging configuration (FX forwards, IR swaps, etc.).
12238    #[serde(default)]
12239    pub hedging: HedgingSchemaConfig,
12240    /// Debt instrument and covenant configuration.
12241    #[serde(default)]
12242    pub debt: DebtSchemaConfig,
12243    /// Intercompany netting configuration.
12244    #[serde(default)]
12245    pub netting: NettingSchemaConfig,
12246    /// Bank guarantee / letter of credit configuration.
12247    #[serde(default)]
12248    pub bank_guarantees: BankGuaranteeSchemaConfig,
12249    /// Anomaly injection rate for treasury data (0.0 to 1.0).
12250    #[serde(default = "default_treasury_anomaly_rate")]
12251    pub anomaly_rate: f64,
12252}
12253
12254fn default_treasury_anomaly_rate() -> f64 {
12255    0.02
12256}
12257
12258impl Default for TreasuryConfig {
12259    fn default() -> Self {
12260        Self {
12261            enabled: false,
12262            cash_positioning: CashPositioningConfig::default(),
12263            cash_forecasting: CashForecastingConfig::default(),
12264            cash_pooling: CashPoolingConfig::default(),
12265            hedging: HedgingSchemaConfig::default(),
12266            debt: DebtSchemaConfig::default(),
12267            netting: NettingSchemaConfig::default(),
12268            bank_guarantees: BankGuaranteeSchemaConfig::default(),
12269            anomaly_rate: default_treasury_anomaly_rate(),
12270        }
12271    }
12272}
12273
12274/// Cash positioning configuration.
12275///
12276/// Controls daily cash position generation per entity/bank account.
12277#[derive(Debug, Clone, Serialize, Deserialize)]
12278pub struct CashPositioningConfig {
12279    /// Whether cash positioning is enabled.
12280    #[serde(default = "default_true")]
12281    pub enabled: bool,
12282    /// Position generation frequency.
12283    #[serde(default = "default_cash_frequency")]
12284    pub frequency: String,
12285    /// Minimum cash balance policy threshold.
12286    #[serde(default = "default_minimum_balance_policy")]
12287    pub minimum_balance_policy: f64,
12288}
12289
12290fn default_cash_frequency() -> String {
12291    "daily".to_string()
12292}
12293
12294fn default_minimum_balance_policy() -> f64 {
12295    100_000.0
12296}
12297
12298impl Default for CashPositioningConfig {
12299    fn default() -> Self {
12300        Self {
12301            enabled: true,
12302            frequency: default_cash_frequency(),
12303            minimum_balance_policy: default_minimum_balance_policy(),
12304        }
12305    }
12306}
12307
12308/// Cash forecasting configuration.
12309///
12310/// Controls forward-looking cash forecast generation with probability-weighted items.
12311#[derive(Debug, Clone, Serialize, Deserialize)]
12312pub struct CashForecastingConfig {
12313    /// Whether cash forecasting is enabled.
12314    #[serde(default = "default_true")]
12315    pub enabled: bool,
12316    /// Number of days to forecast into the future.
12317    #[serde(default = "default_horizon_days")]
12318    pub horizon_days: u32,
12319    /// AR collection probability curve type ("aging" or "flat").
12320    #[serde(default = "default_ar_probability_curve")]
12321    pub ar_collection_probability_curve: String,
12322    /// Confidence interval for the forecast (0.0 to 1.0).
12323    #[serde(default = "default_confidence_interval")]
12324    pub confidence_interval: f64,
12325}
12326
12327fn default_horizon_days() -> u32 {
12328    90
12329}
12330
12331fn default_ar_probability_curve() -> String {
12332    "aging".to_string()
12333}
12334
12335fn default_confidence_interval() -> f64 {
12336    0.90
12337}
12338
12339impl Default for CashForecastingConfig {
12340    fn default() -> Self {
12341        Self {
12342            enabled: true,
12343            horizon_days: default_horizon_days(),
12344            ar_collection_probability_curve: default_ar_probability_curve(),
12345            confidence_interval: default_confidence_interval(),
12346        }
12347    }
12348}
12349
12350/// Cash pooling configuration.
12351///
12352/// Controls cash pool structure generation (physical, notional, zero-balancing).
12353#[derive(Debug, Clone, Serialize, Deserialize)]
12354pub struct CashPoolingConfig {
12355    /// Whether cash pooling is enabled.
12356    #[serde(default)]
12357    pub enabled: bool,
12358    /// Pool type: "physical_pooling", "notional_pooling", or "zero_balancing".
12359    #[serde(default = "default_pool_type")]
12360    pub pool_type: String,
12361    /// Time of day when sweeps occur (HH:MM format).
12362    #[serde(default = "default_sweep_time")]
12363    pub sweep_time: String,
12364}
12365
12366fn default_pool_type() -> String {
12367    "zero_balancing".to_string()
12368}
12369
12370fn default_sweep_time() -> String {
12371    "16:00".to_string()
12372}
12373
12374impl Default for CashPoolingConfig {
12375    fn default() -> Self {
12376        Self {
12377            enabled: false,
12378            pool_type: default_pool_type(),
12379            sweep_time: default_sweep_time(),
12380        }
12381    }
12382}
12383
12384/// Hedging configuration.
12385///
12386/// Controls generation of hedging instruments and hedge relationship designations
12387/// under ASC 815 / IFRS 9.
12388#[derive(Debug, Clone, Serialize, Deserialize)]
12389pub struct HedgingSchemaConfig {
12390    /// Whether hedging generation is enabled.
12391    #[serde(default)]
12392    pub enabled: bool,
12393    /// Target hedge ratio (0.0 to 1.0). Proportion of FX exposure to hedge.
12394    #[serde(default = "default_hedge_ratio")]
12395    pub hedge_ratio: f64,
12396    /// Types of instruments to generate (e.g., ["fx_forward", "interest_rate_swap"]).
12397    #[serde(default = "default_hedge_instruments")]
12398    pub instruments: Vec<String>,
12399    /// Whether to designate formal hedge accounting relationships.
12400    #[serde(default = "default_true")]
12401    pub hedge_accounting: bool,
12402    /// Effectiveness testing method: "dollar_offset", "regression", or "critical_terms".
12403    #[serde(default = "default_effectiveness_method")]
12404    pub effectiveness_method: String,
12405}
12406
12407fn default_hedge_ratio() -> f64 {
12408    0.75
12409}
12410
12411fn default_hedge_instruments() -> Vec<String> {
12412    vec!["fx_forward".to_string(), "interest_rate_swap".to_string()]
12413}
12414
12415fn default_effectiveness_method() -> String {
12416    "regression".to_string()
12417}
12418
12419impl Default for HedgingSchemaConfig {
12420    fn default() -> Self {
12421        Self {
12422            enabled: false,
12423            hedge_ratio: default_hedge_ratio(),
12424            instruments: default_hedge_instruments(),
12425            hedge_accounting: true,
12426            effectiveness_method: default_effectiveness_method(),
12427        }
12428    }
12429}
12430
12431/// Debt instrument configuration.
12432///
12433/// Controls generation of debt instruments (term loans, revolving credit, bonds)
12434/// with amortization schedules and financial covenants.
12435#[derive(Debug, Clone, Default, Serialize, Deserialize)]
12436pub struct DebtSchemaConfig {
12437    /// Whether debt instrument generation is enabled.
12438    #[serde(default)]
12439    pub enabled: bool,
12440    /// Debt instrument definitions.
12441    #[serde(default)]
12442    pub instruments: Vec<DebtInstrumentDef>,
12443    /// Covenant definitions.
12444    #[serde(default)]
12445    pub covenants: Vec<CovenantDef>,
12446}
12447
12448/// Definition of a debt instrument in configuration.
12449#[derive(Debug, Clone, Serialize, Deserialize)]
12450pub struct DebtInstrumentDef {
12451    /// Instrument type: "term_loan", "revolving_credit", "bond", "commercial_paper", "bridge_loan".
12452    #[serde(rename = "type")]
12453    pub instrument_type: String,
12454    /// Principal amount (for term loans, bonds).
12455    #[serde(default)]
12456    pub principal: Option<f64>,
12457    /// Interest rate (annual, as decimal fraction).
12458    #[serde(default)]
12459    pub rate: Option<f64>,
12460    /// Maturity in months.
12461    #[serde(default)]
12462    pub maturity_months: Option<u32>,
12463    /// Facility limit (for revolving credit).
12464    #[serde(default)]
12465    pub facility: Option<f64>,
12466}
12467
12468/// Definition of a debt covenant in configuration.
12469#[derive(Debug, Clone, Serialize, Deserialize)]
12470pub struct CovenantDef {
12471    /// Covenant type: "debt_to_equity", "interest_coverage", "current_ratio",
12472    /// "net_worth", "debt_to_ebitda", "fixed_charge_coverage".
12473    #[serde(rename = "type")]
12474    pub covenant_type: String,
12475    /// Covenant threshold value.
12476    pub threshold: f64,
12477}
12478
12479/// Intercompany netting configuration.
12480///
12481/// Controls generation of multilateral netting runs.
12482#[derive(Debug, Clone, Serialize, Deserialize)]
12483pub struct NettingSchemaConfig {
12484    /// Whether netting generation is enabled.
12485    #[serde(default)]
12486    pub enabled: bool,
12487    /// Netting cycle: "daily", "weekly", or "monthly".
12488    #[serde(default = "default_netting_cycle")]
12489    pub cycle: String,
12490}
12491
12492fn default_netting_cycle() -> String {
12493    "monthly".to_string()
12494}
12495
12496impl Default for NettingSchemaConfig {
12497    fn default() -> Self {
12498        Self {
12499            enabled: false,
12500            cycle: default_netting_cycle(),
12501        }
12502    }
12503}
12504
12505/// Bank guarantee and letter of credit configuration.
12506///
12507/// Controls generation of bank guarantees, standby LCs, and performance bonds.
12508#[derive(Debug, Clone, Serialize, Deserialize)]
12509pub struct BankGuaranteeSchemaConfig {
12510    /// Whether bank guarantee generation is enabled.
12511    #[serde(default)]
12512    pub enabled: bool,
12513    /// Number of guarantees to generate.
12514    #[serde(default = "default_guarantee_count")]
12515    pub count: u32,
12516}
12517
12518fn default_guarantee_count() -> u32 {
12519    5
12520}
12521
12522impl Default for BankGuaranteeSchemaConfig {
12523    fn default() -> Self {
12524        Self {
12525            enabled: false,
12526            count: default_guarantee_count(),
12527        }
12528    }
12529}
12530
12531// ===========================================================================
12532// Project Accounting Configuration
12533// ===========================================================================
12534
12535/// Project accounting configuration.
12536///
12537/// Controls generation of project cost lines, revenue recognition,
12538/// milestones, change orders, retainage, and earned value metrics.
12539#[derive(Debug, Clone, Serialize, Deserialize)]
12540pub struct ProjectAccountingConfig {
12541    /// Whether project accounting is enabled.
12542    #[serde(default)]
12543    pub enabled: bool,
12544    /// Number of projects to generate.
12545    #[serde(default = "default_project_count")]
12546    pub project_count: u32,
12547    /// Distribution of project types (capital, internal, customer, r_and_d, maintenance, technology).
12548    #[serde(default)]
12549    pub project_types: ProjectTypeDistribution,
12550    /// WBS structure configuration.
12551    #[serde(default)]
12552    pub wbs: WbsSchemaConfig,
12553    /// Cost allocation rates (what % of source documents get project-tagged).
12554    #[serde(default)]
12555    pub cost_allocation: CostAllocationConfig,
12556    /// Revenue recognition configuration for project accounting.
12557    #[serde(default)]
12558    pub revenue_recognition: ProjectRevenueRecognitionConfig,
12559    /// Milestone configuration.
12560    #[serde(default)]
12561    pub milestones: MilestoneSchemaConfig,
12562    /// Change order configuration.
12563    #[serde(default)]
12564    pub change_orders: ChangeOrderSchemaConfig,
12565    /// Retainage configuration.
12566    #[serde(default)]
12567    pub retainage: RetainageSchemaConfig,
12568    /// Earned value management configuration.
12569    #[serde(default)]
12570    pub earned_value: EarnedValueSchemaConfig,
12571    /// Anomaly injection rate for project accounting data (0.0 to 1.0).
12572    #[serde(default = "default_project_anomaly_rate")]
12573    pub anomaly_rate: f64,
12574}
12575
12576fn default_project_count() -> u32 {
12577    10
12578}
12579
12580fn default_project_anomaly_rate() -> f64 {
12581    0.03
12582}
12583
12584impl Default for ProjectAccountingConfig {
12585    fn default() -> Self {
12586        Self {
12587            enabled: false,
12588            project_count: default_project_count(),
12589            project_types: ProjectTypeDistribution::default(),
12590            wbs: WbsSchemaConfig::default(),
12591            cost_allocation: CostAllocationConfig::default(),
12592            revenue_recognition: ProjectRevenueRecognitionConfig::default(),
12593            milestones: MilestoneSchemaConfig::default(),
12594            change_orders: ChangeOrderSchemaConfig::default(),
12595            retainage: RetainageSchemaConfig::default(),
12596            earned_value: EarnedValueSchemaConfig::default(),
12597            anomaly_rate: default_project_anomaly_rate(),
12598        }
12599    }
12600}
12601
12602/// Distribution of project types by weight.
12603#[derive(Debug, Clone, Serialize, Deserialize)]
12604pub struct ProjectTypeDistribution {
12605    /// Weight for capital projects (default 0.25).
12606    #[serde(default = "default_capital_weight")]
12607    pub capital: f64,
12608    /// Weight for internal projects (default 0.20).
12609    #[serde(default = "default_internal_weight")]
12610    pub internal: f64,
12611    /// Weight for customer projects (default 0.30).
12612    #[serde(default = "default_customer_weight")]
12613    pub customer: f64,
12614    /// Weight for R&D projects (default 0.10).
12615    #[serde(default = "default_rnd_weight")]
12616    pub r_and_d: f64,
12617    /// Weight for maintenance projects (default 0.10).
12618    #[serde(default = "default_maintenance_weight")]
12619    pub maintenance: f64,
12620    /// Weight for technology projects (default 0.05).
12621    #[serde(default = "default_technology_weight")]
12622    pub technology: f64,
12623}
12624
12625fn default_capital_weight() -> f64 {
12626    0.25
12627}
12628fn default_internal_weight() -> f64 {
12629    0.20
12630}
12631fn default_customer_weight() -> f64 {
12632    0.30
12633}
12634fn default_rnd_weight() -> f64 {
12635    0.10
12636}
12637fn default_maintenance_weight() -> f64 {
12638    0.10
12639}
12640fn default_technology_weight() -> f64 {
12641    0.05
12642}
12643
12644impl Default for ProjectTypeDistribution {
12645    fn default() -> Self {
12646        Self {
12647            capital: default_capital_weight(),
12648            internal: default_internal_weight(),
12649            customer: default_customer_weight(),
12650            r_and_d: default_rnd_weight(),
12651            maintenance: default_maintenance_weight(),
12652            technology: default_technology_weight(),
12653        }
12654    }
12655}
12656
12657/// WBS structure configuration.
12658#[derive(Debug, Clone, Serialize, Deserialize)]
12659pub struct WbsSchemaConfig {
12660    /// Maximum depth of WBS hierarchy (default 3).
12661    #[serde(default = "default_wbs_max_depth")]
12662    pub max_depth: u32,
12663    /// Minimum elements per level-1 WBS (default 2).
12664    #[serde(default = "default_wbs_min_elements")]
12665    pub min_elements_per_level: u32,
12666    /// Maximum elements per level-1 WBS (default 6).
12667    #[serde(default = "default_wbs_max_elements")]
12668    pub max_elements_per_level: u32,
12669}
12670
12671fn default_wbs_max_depth() -> u32 {
12672    3
12673}
12674fn default_wbs_min_elements() -> u32 {
12675    2
12676}
12677fn default_wbs_max_elements() -> u32 {
12678    6
12679}
12680
12681impl Default for WbsSchemaConfig {
12682    fn default() -> Self {
12683        Self {
12684            max_depth: default_wbs_max_depth(),
12685            min_elements_per_level: default_wbs_min_elements(),
12686            max_elements_per_level: default_wbs_max_elements(),
12687        }
12688    }
12689}
12690
12691/// Cost allocation rates — what fraction of each document type gets linked to a project.
12692#[derive(Debug, Clone, Serialize, Deserialize)]
12693pub struct CostAllocationConfig {
12694    /// Fraction of time entries assigned to projects (0.0 to 1.0).
12695    #[serde(default = "default_time_entry_rate")]
12696    pub time_entry_project_rate: f64,
12697    /// Fraction of expense reports assigned to projects (0.0 to 1.0).
12698    #[serde(default = "default_expense_rate")]
12699    pub expense_project_rate: f64,
12700    /// Fraction of purchase orders assigned to projects (0.0 to 1.0).
12701    #[serde(default = "default_po_rate")]
12702    pub purchase_order_project_rate: f64,
12703    /// Fraction of vendor invoices assigned to projects (0.0 to 1.0).
12704    #[serde(default = "default_vi_rate")]
12705    pub vendor_invoice_project_rate: f64,
12706}
12707
12708fn default_time_entry_rate() -> f64 {
12709    0.60
12710}
12711fn default_expense_rate() -> f64 {
12712    0.30
12713}
12714fn default_po_rate() -> f64 {
12715    0.40
12716}
12717fn default_vi_rate() -> f64 {
12718    0.35
12719}
12720
12721impl Default for CostAllocationConfig {
12722    fn default() -> Self {
12723        Self {
12724            time_entry_project_rate: default_time_entry_rate(),
12725            expense_project_rate: default_expense_rate(),
12726            purchase_order_project_rate: default_po_rate(),
12727            vendor_invoice_project_rate: default_vi_rate(),
12728        }
12729    }
12730}
12731
12732/// Revenue recognition configuration for project accounting.
12733#[derive(Debug, Clone, Serialize, Deserialize)]
12734pub struct ProjectRevenueRecognitionConfig {
12735    /// Whether revenue recognition is enabled for customer projects.
12736    #[serde(default = "default_true")]
12737    pub enabled: bool,
12738    /// Default method: "percentage_of_completion", "completed_contract", "milestone_based".
12739    #[serde(default = "default_revenue_method")]
12740    pub method: String,
12741    /// Default completion measure: "cost_to_cost", "labor_hours", "physical_completion".
12742    #[serde(default = "default_completion_measure")]
12743    pub completion_measure: String,
12744    /// Average contract value for customer projects.
12745    #[serde(default = "default_avg_contract_value")]
12746    pub avg_contract_value: f64,
12747}
12748
12749fn default_revenue_method() -> String {
12750    "percentage_of_completion".to_string()
12751}
12752fn default_completion_measure() -> String {
12753    "cost_to_cost".to_string()
12754}
12755fn default_avg_contract_value() -> f64 {
12756    500_000.0
12757}
12758
12759impl Default for ProjectRevenueRecognitionConfig {
12760    fn default() -> Self {
12761        Self {
12762            enabled: true,
12763            method: default_revenue_method(),
12764            completion_measure: default_completion_measure(),
12765            avg_contract_value: default_avg_contract_value(),
12766        }
12767    }
12768}
12769
12770/// Milestone configuration.
12771#[derive(Debug, Clone, Serialize, Deserialize)]
12772pub struct MilestoneSchemaConfig {
12773    /// Whether milestone generation is enabled.
12774    #[serde(default = "default_true")]
12775    pub enabled: bool,
12776    /// Average number of milestones per project.
12777    #[serde(default = "default_milestones_per_project")]
12778    pub avg_per_project: u32,
12779    /// Fraction of milestones that are payment milestones (0.0 to 1.0).
12780    #[serde(default = "default_payment_milestone_rate")]
12781    pub payment_milestone_rate: f64,
12782}
12783
12784fn default_milestones_per_project() -> u32 {
12785    4
12786}
12787fn default_payment_milestone_rate() -> f64 {
12788    0.50
12789}
12790
12791impl Default for MilestoneSchemaConfig {
12792    fn default() -> Self {
12793        Self {
12794            enabled: true,
12795            avg_per_project: default_milestones_per_project(),
12796            payment_milestone_rate: default_payment_milestone_rate(),
12797        }
12798    }
12799}
12800
12801/// Change order configuration.
12802#[derive(Debug, Clone, Serialize, Deserialize)]
12803pub struct ChangeOrderSchemaConfig {
12804    /// Whether change order generation is enabled.
12805    #[serde(default = "default_true")]
12806    pub enabled: bool,
12807    /// Probability that a project will have at least one change order (0.0 to 1.0).
12808    #[serde(default = "default_change_order_probability")]
12809    pub probability: f64,
12810    /// Maximum change orders per project.
12811    #[serde(default = "default_max_change_orders")]
12812    pub max_per_project: u32,
12813    /// Approval rate for change orders (0.0 to 1.0).
12814    #[serde(default = "default_change_order_approval_rate")]
12815    pub approval_rate: f64,
12816}
12817
12818fn default_change_order_probability() -> f64 {
12819    0.40
12820}
12821fn default_max_change_orders() -> u32 {
12822    3
12823}
12824fn default_change_order_approval_rate() -> f64 {
12825    0.75
12826}
12827
12828impl Default for ChangeOrderSchemaConfig {
12829    fn default() -> Self {
12830        Self {
12831            enabled: true,
12832            probability: default_change_order_probability(),
12833            max_per_project: default_max_change_orders(),
12834            approval_rate: default_change_order_approval_rate(),
12835        }
12836    }
12837}
12838
12839/// Retainage configuration.
12840#[derive(Debug, Clone, Serialize, Deserialize)]
12841pub struct RetainageSchemaConfig {
12842    /// Whether retainage is enabled.
12843    #[serde(default)]
12844    pub enabled: bool,
12845    /// Default retainage percentage (0.0 to 1.0, e.g., 0.10 for 10%).
12846    #[serde(default = "default_retainage_pct")]
12847    pub default_percentage: f64,
12848}
12849
12850fn default_retainage_pct() -> f64 {
12851    0.10
12852}
12853
12854impl Default for RetainageSchemaConfig {
12855    fn default() -> Self {
12856        Self {
12857            enabled: false,
12858            default_percentage: default_retainage_pct(),
12859        }
12860    }
12861}
12862
12863/// Earned value management (EVM) configuration.
12864#[derive(Debug, Clone, Serialize, Deserialize)]
12865pub struct EarnedValueSchemaConfig {
12866    /// Whether EVM metrics are generated.
12867    #[serde(default = "default_true")]
12868    pub enabled: bool,
12869    /// Measurement frequency: "weekly", "biweekly", "monthly".
12870    #[serde(default = "default_evm_frequency")]
12871    pub frequency: String,
12872}
12873
12874fn default_evm_frequency() -> String {
12875    "monthly".to_string()
12876}
12877
12878impl Default for EarnedValueSchemaConfig {
12879    fn default() -> Self {
12880        Self {
12881            enabled: true,
12882            frequency: default_evm_frequency(),
12883        }
12884    }
12885}
12886
12887// =============================================================================
12888// ESG / Sustainability Configuration
12889// =============================================================================
12890
12891/// Top-level ESG / sustainability reporting configuration.
12892#[derive(Debug, Clone, Serialize, Deserialize)]
12893pub struct EsgConfig {
12894    /// Whether ESG generation is enabled.
12895    #[serde(default)]
12896    pub enabled: bool,
12897    /// Environmental metrics (emissions, energy, water, waste).
12898    #[serde(default)]
12899    pub environmental: EnvironmentalConfig,
12900    /// Social metrics (diversity, pay equity, safety).
12901    #[serde(default)]
12902    pub social: SocialConfig,
12903    /// Governance metrics (board composition, ethics, compliance).
12904    #[serde(default)]
12905    pub governance: GovernanceSchemaConfig,
12906    /// Supply-chain ESG assessment settings.
12907    #[serde(default)]
12908    pub supply_chain_esg: SupplyChainEsgConfig,
12909    /// ESG reporting / disclosure framework settings.
12910    #[serde(default)]
12911    pub reporting: EsgReportingConfig,
12912    /// Climate scenario analysis settings.
12913    #[serde(default)]
12914    pub climate_scenarios: ClimateScenarioConfig,
12915    /// Anomaly injection rate for ESG data (0.0 to 1.0).
12916    #[serde(default = "default_esg_anomaly_rate")]
12917    pub anomaly_rate: f64,
12918}
12919
12920fn default_esg_anomaly_rate() -> f64 {
12921    0.02
12922}
12923
12924impl Default for EsgConfig {
12925    fn default() -> Self {
12926        Self {
12927            enabled: false,
12928            environmental: EnvironmentalConfig::default(),
12929            social: SocialConfig::default(),
12930            governance: GovernanceSchemaConfig::default(),
12931            supply_chain_esg: SupplyChainEsgConfig::default(),
12932            reporting: EsgReportingConfig::default(),
12933            climate_scenarios: ClimateScenarioConfig::default(),
12934            anomaly_rate: default_esg_anomaly_rate(),
12935        }
12936    }
12937}
12938
12939/// Country pack configuration.
12940///
12941/// Controls where to load additional country packs and per-country overrides.
12942/// When omitted, only the built-in packs (_default, US, DE, GB) are used.
12943#[derive(Debug, Clone, Serialize, Deserialize, Default)]
12944pub struct CountryPacksSchemaConfig {
12945    /// Optional directory containing additional `*.json` country packs.
12946    #[serde(default)]
12947    pub external_dir: Option<PathBuf>,
12948    /// Per-country overrides applied after loading.
12949    /// Keys are ISO 3166-1 alpha-2 codes; values are partial JSON objects
12950    /// that are deep-merged on top of the loaded pack.
12951    #[serde(default)]
12952    pub overrides: std::collections::HashMap<String, serde_json::Value>,
12953}
12954
12955/// Environmental metrics configuration.
12956#[derive(Debug, Clone, Serialize, Deserialize)]
12957pub struct EnvironmentalConfig {
12958    /// Whether environmental metrics are generated.
12959    #[serde(default = "default_true")]
12960    pub enabled: bool,
12961    /// Scope 1 (direct) emission generation settings.
12962    #[serde(default)]
12963    pub scope1: EmissionScopeConfig,
12964    /// Scope 2 (purchased energy) emission generation settings.
12965    #[serde(default)]
12966    pub scope2: EmissionScopeConfig,
12967    /// Scope 3 (value chain) emission generation settings.
12968    #[serde(default)]
12969    pub scope3: Scope3Config,
12970    /// Energy consumption tracking settings.
12971    #[serde(default)]
12972    pub energy: EnergySchemaConfig,
12973    /// Water usage tracking settings.
12974    #[serde(default)]
12975    pub water: WaterSchemaConfig,
12976    /// Waste management tracking settings.
12977    #[serde(default)]
12978    pub waste: WasteSchemaConfig,
12979}
12980
12981impl Default for EnvironmentalConfig {
12982    fn default() -> Self {
12983        Self {
12984            enabled: true,
12985            scope1: EmissionScopeConfig::default(),
12986            scope2: EmissionScopeConfig::default(),
12987            scope3: Scope3Config::default(),
12988            energy: EnergySchemaConfig::default(),
12989            water: WaterSchemaConfig::default(),
12990            waste: WasteSchemaConfig::default(),
12991        }
12992    }
12993}
12994
12995/// Configuration for a single emission scope (Scope 1 or 2).
12996#[derive(Debug, Clone, Serialize, Deserialize)]
12997pub struct EmissionScopeConfig {
12998    /// Whether this scope is enabled.
12999    #[serde(default = "default_true")]
13000    pub enabled: bool,
13001    /// Emission factor region (e.g., "US", "EU", "global").
13002    #[serde(default = "default_emission_region")]
13003    pub factor_region: String,
13004}
13005
13006fn default_emission_region() -> String {
13007    "US".to_string()
13008}
13009
13010impl Default for EmissionScopeConfig {
13011    fn default() -> Self {
13012        Self {
13013            enabled: true,
13014            factor_region: default_emission_region(),
13015        }
13016    }
13017}
13018
13019/// Scope 3 (value chain) emission configuration.
13020#[derive(Debug, Clone, Serialize, Deserialize)]
13021pub struct Scope3Config {
13022    /// Whether Scope 3 emissions are generated.
13023    #[serde(default = "default_true")]
13024    pub enabled: bool,
13025    /// Categories to include (e.g., "purchased_goods", "business_travel", "commuting").
13026    #[serde(default = "default_scope3_categories")]
13027    pub categories: Vec<String>,
13028    /// Spend-based emission intensity (kg CO2e per USD).
13029    #[serde(default = "default_spend_intensity")]
13030    pub default_spend_intensity_kg_per_usd: f64,
13031}
13032
13033fn default_scope3_categories() -> Vec<String> {
13034    vec![
13035        "purchased_goods".to_string(),
13036        "business_travel".to_string(),
13037        "employee_commuting".to_string(),
13038    ]
13039}
13040
13041fn default_spend_intensity() -> f64 {
13042    0.5
13043}
13044
13045impl Default for Scope3Config {
13046    fn default() -> Self {
13047        Self {
13048            enabled: true,
13049            categories: default_scope3_categories(),
13050            default_spend_intensity_kg_per_usd: default_spend_intensity(),
13051        }
13052    }
13053}
13054
13055/// Energy consumption configuration.
13056#[derive(Debug, Clone, Serialize, Deserialize)]
13057pub struct EnergySchemaConfig {
13058    /// Whether energy consumption tracking is enabled.
13059    #[serde(default = "default_true")]
13060    pub enabled: bool,
13061    /// Number of facilities to generate.
13062    #[serde(default = "default_facility_count")]
13063    pub facility_count: u32,
13064    /// Target percentage of energy from renewable sources (0.0 to 1.0).
13065    #[serde(default = "default_renewable_target")]
13066    pub renewable_target: f64,
13067}
13068
13069fn default_facility_count() -> u32 {
13070    5
13071}
13072
13073fn default_renewable_target() -> f64 {
13074    0.30
13075}
13076
13077impl Default for EnergySchemaConfig {
13078    fn default() -> Self {
13079        Self {
13080            enabled: true,
13081            facility_count: default_facility_count(),
13082            renewable_target: default_renewable_target(),
13083        }
13084    }
13085}
13086
13087/// Water usage configuration.
13088#[derive(Debug, Clone, Serialize, Deserialize)]
13089pub struct WaterSchemaConfig {
13090    /// Whether water usage tracking is enabled.
13091    #[serde(default = "default_true")]
13092    pub enabled: bool,
13093    /// Number of facilities with water tracking.
13094    #[serde(default = "default_water_facility_count")]
13095    pub facility_count: u32,
13096}
13097
13098fn default_water_facility_count() -> u32 {
13099    3
13100}
13101
13102impl Default for WaterSchemaConfig {
13103    fn default() -> Self {
13104        Self {
13105            enabled: true,
13106            facility_count: default_water_facility_count(),
13107        }
13108    }
13109}
13110
13111/// Waste management configuration.
13112#[derive(Debug, Clone, Serialize, Deserialize)]
13113pub struct WasteSchemaConfig {
13114    /// Whether waste tracking is enabled.
13115    #[serde(default = "default_true")]
13116    pub enabled: bool,
13117    /// Target diversion rate (0.0 to 1.0).
13118    #[serde(default = "default_diversion_target")]
13119    pub diversion_target: f64,
13120}
13121
13122fn default_diversion_target() -> f64 {
13123    0.50
13124}
13125
13126impl Default for WasteSchemaConfig {
13127    fn default() -> Self {
13128        Self {
13129            enabled: true,
13130            diversion_target: default_diversion_target(),
13131        }
13132    }
13133}
13134
13135/// Social metrics configuration.
13136#[derive(Debug, Clone, Serialize, Deserialize)]
13137pub struct SocialConfig {
13138    /// Whether social metrics are generated.
13139    #[serde(default = "default_true")]
13140    pub enabled: bool,
13141    /// Workforce diversity tracking settings.
13142    #[serde(default)]
13143    pub diversity: DiversitySchemaConfig,
13144    /// Pay equity analysis settings.
13145    #[serde(default)]
13146    pub pay_equity: PayEquitySchemaConfig,
13147    /// Safety incident and metrics settings.
13148    #[serde(default)]
13149    pub safety: SafetySchemaConfig,
13150}
13151
13152impl Default for SocialConfig {
13153    fn default() -> Self {
13154        Self {
13155            enabled: true,
13156            diversity: DiversitySchemaConfig::default(),
13157            pay_equity: PayEquitySchemaConfig::default(),
13158            safety: SafetySchemaConfig::default(),
13159        }
13160    }
13161}
13162
13163/// Workforce diversity configuration.
13164#[derive(Debug, Clone, Serialize, Deserialize)]
13165pub struct DiversitySchemaConfig {
13166    /// Whether diversity metrics are generated.
13167    #[serde(default = "default_true")]
13168    pub enabled: bool,
13169    /// Dimensions to track (e.g., "gender", "ethnicity", "age_group").
13170    #[serde(default = "default_diversity_dimensions")]
13171    pub dimensions: Vec<String>,
13172}
13173
13174fn default_diversity_dimensions() -> Vec<String> {
13175    vec![
13176        "gender".to_string(),
13177        "ethnicity".to_string(),
13178        "age_group".to_string(),
13179    ]
13180}
13181
13182impl Default for DiversitySchemaConfig {
13183    fn default() -> Self {
13184        Self {
13185            enabled: true,
13186            dimensions: default_diversity_dimensions(),
13187        }
13188    }
13189}
13190
13191/// Pay equity analysis configuration.
13192#[derive(Debug, Clone, Serialize, Deserialize)]
13193pub struct PayEquitySchemaConfig {
13194    /// Whether pay equity analysis is generated.
13195    #[serde(default = "default_true")]
13196    pub enabled: bool,
13197    /// Target pay gap threshold for flagging (e.g., 0.05 = 5% gap).
13198    #[serde(default = "default_pay_gap_threshold")]
13199    pub gap_threshold: f64,
13200}
13201
13202fn default_pay_gap_threshold() -> f64 {
13203    0.05
13204}
13205
13206impl Default for PayEquitySchemaConfig {
13207    fn default() -> Self {
13208        Self {
13209            enabled: true,
13210            gap_threshold: default_pay_gap_threshold(),
13211        }
13212    }
13213}
13214
13215/// Safety metrics configuration.
13216#[derive(Debug, Clone, Serialize, Deserialize)]
13217pub struct SafetySchemaConfig {
13218    /// Whether safety metrics are generated.
13219    #[serde(default = "default_true")]
13220    pub enabled: bool,
13221    /// Average annual recordable incidents per 200,000 hours.
13222    #[serde(default = "default_trir_target")]
13223    pub target_trir: f64,
13224    /// Number of safety incidents to generate.
13225    #[serde(default = "default_incident_count")]
13226    pub incident_count: u32,
13227}
13228
13229fn default_trir_target() -> f64 {
13230    2.5
13231}
13232
13233fn default_incident_count() -> u32 {
13234    20
13235}
13236
13237impl Default for SafetySchemaConfig {
13238    fn default() -> Self {
13239        Self {
13240            enabled: true,
13241            target_trir: default_trir_target(),
13242            incident_count: default_incident_count(),
13243        }
13244    }
13245}
13246
13247/// Governance metrics configuration.
13248#[derive(Debug, Clone, Serialize, Deserialize)]
13249pub struct GovernanceSchemaConfig {
13250    /// Whether governance metrics are generated.
13251    #[serde(default = "default_true")]
13252    pub enabled: bool,
13253    /// Number of board members.
13254    #[serde(default = "default_board_size")]
13255    pub board_size: u32,
13256    /// Target independent director ratio (0.0 to 1.0).
13257    #[serde(default = "default_independence_target")]
13258    pub independence_target: f64,
13259}
13260
13261fn default_board_size() -> u32 {
13262    11
13263}
13264
13265fn default_independence_target() -> f64 {
13266    0.67
13267}
13268
13269impl Default for GovernanceSchemaConfig {
13270    fn default() -> Self {
13271        Self {
13272            enabled: true,
13273            board_size: default_board_size(),
13274            independence_target: default_independence_target(),
13275        }
13276    }
13277}
13278
13279/// Supply-chain ESG assessment configuration.
13280#[derive(Debug, Clone, Serialize, Deserialize)]
13281pub struct SupplyChainEsgConfig {
13282    /// Whether supply chain ESG assessments are generated.
13283    #[serde(default = "default_true")]
13284    pub enabled: bool,
13285    /// Proportion of vendors to assess (0.0 to 1.0).
13286    #[serde(default = "default_assessment_coverage")]
13287    pub assessment_coverage: f64,
13288    /// High-risk country codes for automatic flagging.
13289    #[serde(default = "default_high_risk_countries")]
13290    pub high_risk_countries: Vec<String>,
13291}
13292
13293fn default_assessment_coverage() -> f64 {
13294    0.80
13295}
13296
13297fn default_high_risk_countries() -> Vec<String> {
13298    vec!["CN".to_string(), "BD".to_string(), "MM".to_string()]
13299}
13300
13301impl Default for SupplyChainEsgConfig {
13302    fn default() -> Self {
13303        Self {
13304            enabled: true,
13305            assessment_coverage: default_assessment_coverage(),
13306            high_risk_countries: default_high_risk_countries(),
13307        }
13308    }
13309}
13310
13311/// ESG reporting / disclosure framework configuration.
13312#[derive(Debug, Clone, Serialize, Deserialize)]
13313pub struct EsgReportingConfig {
13314    /// Whether ESG disclosures are generated.
13315    #[serde(default = "default_true")]
13316    pub enabled: bool,
13317    /// Frameworks to generate disclosures for.
13318    #[serde(default = "default_esg_frameworks")]
13319    pub frameworks: Vec<String>,
13320    /// Whether materiality assessment is performed.
13321    #[serde(default = "default_true")]
13322    pub materiality_assessment: bool,
13323    /// Materiality threshold for impact dimension (0.0 to 1.0).
13324    #[serde(default = "default_materiality_threshold")]
13325    pub impact_threshold: f64,
13326    /// Materiality threshold for financial dimension (0.0 to 1.0).
13327    #[serde(default = "default_materiality_threshold")]
13328    pub financial_threshold: f64,
13329}
13330
13331fn default_esg_frameworks() -> Vec<String> {
13332    vec!["GRI".to_string(), "ESRS".to_string()]
13333}
13334
13335fn default_materiality_threshold() -> f64 {
13336    0.6
13337}
13338
13339impl Default for EsgReportingConfig {
13340    fn default() -> Self {
13341        Self {
13342            enabled: true,
13343            frameworks: default_esg_frameworks(),
13344            materiality_assessment: true,
13345            impact_threshold: default_materiality_threshold(),
13346            financial_threshold: default_materiality_threshold(),
13347        }
13348    }
13349}
13350
13351/// Climate scenario analysis configuration.
13352#[derive(Debug, Clone, Serialize, Deserialize)]
13353pub struct ClimateScenarioConfig {
13354    /// Whether climate scenario analysis is generated.
13355    #[serde(default)]
13356    pub enabled: bool,
13357    /// Scenarios to model (e.g., "net_zero_2050", "stated_policies", "current_trajectory").
13358    #[serde(default = "default_climate_scenarios")]
13359    pub scenarios: Vec<String>,
13360    /// Time horizons in years to project.
13361    #[serde(default = "default_time_horizons")]
13362    pub time_horizons: Vec<u32>,
13363}
13364
13365fn default_climate_scenarios() -> Vec<String> {
13366    vec![
13367        "net_zero_2050".to_string(),
13368        "stated_policies".to_string(),
13369        "current_trajectory".to_string(),
13370    ]
13371}
13372
13373fn default_time_horizons() -> Vec<u32> {
13374    vec![5, 10, 30]
13375}
13376
13377impl Default for ClimateScenarioConfig {
13378    fn default() -> Self {
13379        Self {
13380            enabled: false,
13381            scenarios: default_climate_scenarios(),
13382            time_horizons: default_time_horizons(),
13383        }
13384    }
13385}
13386
13387// ===== Counterfactual Simulation Scenarios =====
13388
13389/// Configuration for counterfactual simulation scenarios.
13390#[derive(Debug, Clone, Serialize, Deserialize, Default)]
13391pub struct ScenariosConfig {
13392    /// Whether scenario generation is enabled.
13393    #[serde(default)]
13394    pub enabled: bool,
13395    /// List of scenario definitions.
13396    #[serde(default)]
13397    pub scenarios: Vec<ScenarioSchemaConfig>,
13398    /// Causal model configuration.
13399    #[serde(default)]
13400    pub causal_model: CausalModelSchemaConfig,
13401    /// Default settings applied to all scenarios.
13402    #[serde(default)]
13403    pub defaults: ScenarioDefaultsConfig,
13404    /// Generate counterfactual (original, mutated) JE pairs for ML training.
13405    /// When true, the orchestrator produces paired clean/anomalous journal entries.
13406    #[serde(default)]
13407    pub generate_counterfactuals: bool,
13408}
13409
13410/// A single scenario definition in the config.
13411#[derive(Debug, Clone, Serialize, Deserialize)]
13412pub struct ScenarioSchemaConfig {
13413    /// Scenario name (must be unique).
13414    pub name: String,
13415    /// Human-readable description.
13416    #[serde(default)]
13417    pub description: String,
13418    /// Tags for categorization.
13419    #[serde(default)]
13420    pub tags: Vec<String>,
13421    /// Base scenario name (None = default config).
13422    pub base: Option<String>,
13423    /// IFRS 9-style probability weight.
13424    pub probability_weight: Option<f64>,
13425    /// List of interventions to apply.
13426    #[serde(default)]
13427    pub interventions: Vec<InterventionSchemaConfig>,
13428    /// Constraint overrides for this scenario.
13429    #[serde(default)]
13430    pub constraints: ScenarioConstraintsSchemaConfig,
13431    /// Output configuration for this scenario.
13432    #[serde(default)]
13433    pub output: ScenarioOutputSchemaConfig,
13434    /// Arbitrary metadata.
13435    #[serde(default)]
13436    pub metadata: std::collections::HashMap<String, String>,
13437}
13438
13439/// An intervention definition in the config.
13440#[derive(Debug, Clone, Serialize, Deserialize)]
13441pub struct InterventionSchemaConfig {
13442    /// Intervention type and parameters (flattened tagged enum).
13443    #[serde(flatten)]
13444    pub intervention_type: serde_json::Value,
13445    /// Timing configuration.
13446    #[serde(default)]
13447    pub timing: InterventionTimingSchemaConfig,
13448    /// Human-readable label.
13449    pub label: Option<String>,
13450    /// Priority for conflict resolution (higher wins).
13451    #[serde(default)]
13452    pub priority: u32,
13453}
13454
13455/// Timing configuration for an intervention.
13456#[derive(Debug, Clone, Serialize, Deserialize)]
13457pub struct InterventionTimingSchemaConfig {
13458    /// Month offset from start (1-indexed).
13459    #[serde(default = "default_start_month")]
13460    pub start_month: u32,
13461    /// Duration in months.
13462    pub duration_months: Option<u32>,
13463    /// Onset type: "sudden", "gradual", "oscillating", "custom".
13464    #[serde(default = "default_onset")]
13465    pub onset: String,
13466    /// Ramp period in months.
13467    pub ramp_months: Option<u32>,
13468}
13469
13470fn default_start_month() -> u32 {
13471    1
13472}
13473
13474fn default_onset() -> String {
13475    "sudden".to_string()
13476}
13477
13478impl Default for InterventionTimingSchemaConfig {
13479    fn default() -> Self {
13480        Self {
13481            start_month: 1,
13482            duration_months: None,
13483            onset: "sudden".to_string(),
13484            ramp_months: None,
13485        }
13486    }
13487}
13488
13489/// Scenario constraint overrides.
13490#[derive(Debug, Clone, Serialize, Deserialize)]
13491pub struct ScenarioConstraintsSchemaConfig {
13492    #[serde(default = "default_true")]
13493    pub preserve_accounting_identity: bool,
13494    #[serde(default = "default_true")]
13495    pub preserve_document_chains: bool,
13496    #[serde(default = "default_true")]
13497    pub preserve_period_close: bool,
13498    #[serde(default = "default_true")]
13499    pub preserve_balance_coherence: bool,
13500    #[serde(default)]
13501    pub custom: Vec<CustomConstraintSchemaConfig>,
13502}
13503
13504impl Default for ScenarioConstraintsSchemaConfig {
13505    fn default() -> Self {
13506        Self {
13507            preserve_accounting_identity: true,
13508            preserve_document_chains: true,
13509            preserve_period_close: true,
13510            preserve_balance_coherence: true,
13511            custom: Vec::new(),
13512        }
13513    }
13514}
13515
13516/// Custom constraint in config.
13517#[derive(Debug, Clone, Serialize, Deserialize)]
13518pub struct CustomConstraintSchemaConfig {
13519    pub config_path: String,
13520    pub min: Option<f64>,
13521    pub max: Option<f64>,
13522    #[serde(default)]
13523    pub description: String,
13524}
13525
13526/// Output configuration for a scenario.
13527#[derive(Debug, Clone, Serialize, Deserialize)]
13528pub struct ScenarioOutputSchemaConfig {
13529    #[serde(default = "default_true")]
13530    pub paired: bool,
13531    #[serde(default = "default_diff_formats_schema")]
13532    pub diff_formats: Vec<String>,
13533    #[serde(default)]
13534    pub diff_scope: Vec<String>,
13535}
13536
13537fn default_diff_formats_schema() -> Vec<String> {
13538    vec!["summary".to_string(), "aggregate".to_string()]
13539}
13540
13541impl Default for ScenarioOutputSchemaConfig {
13542    fn default() -> Self {
13543        Self {
13544            paired: true,
13545            diff_formats: default_diff_formats_schema(),
13546            diff_scope: Vec::new(),
13547        }
13548    }
13549}
13550
13551/// Causal model configuration.
13552#[derive(Debug, Clone, Serialize, Deserialize)]
13553pub struct CausalModelSchemaConfig {
13554    /// Preset name: "default", "minimal", or "custom".
13555    #[serde(default = "default_causal_preset")]
13556    pub preset: String,
13557    /// Custom nodes (merged with preset).
13558    #[serde(default)]
13559    pub nodes: Vec<serde_json::Value>,
13560    /// Custom edges (merged with preset).
13561    #[serde(default)]
13562    pub edges: Vec<serde_json::Value>,
13563}
13564
13565fn default_causal_preset() -> String {
13566    "default".to_string()
13567}
13568
13569impl Default for CausalModelSchemaConfig {
13570    fn default() -> Self {
13571        Self {
13572            preset: "default".to_string(),
13573            nodes: Vec::new(),
13574            edges: Vec::new(),
13575        }
13576    }
13577}
13578
13579/// Default settings applied to all scenarios.
13580#[derive(Debug, Clone, Serialize, Deserialize, Default)]
13581pub struct ScenarioDefaultsConfig {
13582    #[serde(default)]
13583    pub constraints: ScenarioConstraintsSchemaConfig,
13584    #[serde(default)]
13585    pub output: ScenarioOutputSchemaConfig,
13586}
13587
13588// =====================================================================
13589// Compliance Regulations Framework Configuration
13590// =====================================================================
13591
13592/// Top-level configuration for the compliance regulations framework.
13593///
13594/// Controls standards registry, jurisdiction profiles, temporal versioning,
13595/// audit procedure templates, compliance graph integration, and output settings.
13596///
13597/// # Example
13598///
13599/// ```yaml
13600/// compliance_regulations:
13601///   enabled: true
13602///   jurisdictions: [US, DE, GB]
13603///   reference_date: "2025-06-30"
13604///   standards_selection:
13605///     categories: [accounting, auditing, regulatory]
13606///     include: ["IFRS-16", "ASC-606"]
13607///   audit_procedures:
13608///     enabled: true
13609///     procedures_per_standard: 3
13610///   findings:
13611///     enabled: true
13612///     finding_rate: 0.05
13613///   filings:
13614///     enabled: true
13615///   graph:
13616///     enabled: true
13617///     include_compliance_nodes: true
13618///     include_compliance_edges: true
13619/// ```
13620#[derive(Debug, Clone, Default, Serialize, Deserialize)]
13621pub struct ComplianceRegulationsConfig {
13622    /// Master switch for the compliance regulations framework.
13623    #[serde(default)]
13624    pub enabled: bool,
13625    /// Jurisdictions to generate compliance data for (ISO 3166-1 alpha-2 codes).
13626    /// If empty, inferred from company countries in the config.
13627    #[serde(default)]
13628    pub jurisdictions: Vec<String>,
13629    /// Reference date for temporal standard resolution (YYYY-MM-DD).
13630    /// Defaults to the global start_date if not set.
13631    #[serde(default)]
13632    pub reference_date: Option<String>,
13633    /// Standards selection filters.
13634    #[serde(default)]
13635    pub standards_selection: StandardsSelectionConfig,
13636    /// Audit procedure generation settings.
13637    #[serde(default)]
13638    pub audit_procedures: AuditProcedureGenConfig,
13639    /// Compliance finding generation settings.
13640    #[serde(default)]
13641    pub findings: ComplianceFindingGenConfig,
13642    /// Regulatory filing generation settings.
13643    #[serde(default)]
13644    pub filings: ComplianceFilingGenConfig,
13645    /// Compliance graph integration settings.
13646    #[serde(default)]
13647    pub graph: ComplianceGraphConfig,
13648    /// Output settings for compliance-specific files.
13649    #[serde(default)]
13650    pub output: ComplianceOutputConfig,
13651    /// v3.3.0: legal-document generation (engagement letters,
13652    /// management reps, legal opinions, regulatory filings, board
13653    /// resolutions). Requires `compliance_regulations.enabled = true`
13654    /// AND `legal_documents.enabled = true` to take effect.
13655    #[serde(default)]
13656    pub legal_documents: LegalDocumentsConfig,
13657}
13658
13659/// Legal-document generation settings (v3.3.0+).
13660///
13661/// Wires `LegalDocumentGenerator` into the orchestrator. Generates one
13662/// batch per audit engagement when enabled.
13663#[derive(Debug, Clone, Serialize, Deserialize)]
13664pub struct LegalDocumentsConfig {
13665    /// Master switch.
13666    #[serde(default)]
13667    pub enabled: bool,
13668    /// Probability of including a legal-opinion document in an engagement.
13669    #[serde(default = "default_legal_opinion_probability")]
13670    pub legal_opinion_probability: f64,
13671}
13672
13673fn default_legal_opinion_probability() -> f64 {
13674    0.40
13675}
13676
13677impl Default for LegalDocumentsConfig {
13678    fn default() -> Self {
13679        Self {
13680            enabled: false,
13681            legal_opinion_probability: default_legal_opinion_probability(),
13682        }
13683    }
13684}
13685
13686/// Filters which standards are included in the generation.
13687#[derive(Debug, Clone, Default, Serialize, Deserialize)]
13688pub struct StandardsSelectionConfig {
13689    /// Standard categories to include (accounting, auditing, regulatory, tax, esg).
13690    /// Empty = all categories.
13691    #[serde(default)]
13692    pub categories: Vec<String>,
13693    /// Explicit standard IDs to include (e.g., ["IFRS-16", "ASC-606"]).
13694    /// When non-empty, only these standards (plus mandatory ones for selected jurisdictions) are used.
13695    #[serde(default)]
13696    pub include: Vec<String>,
13697    /// Standard IDs to exclude.
13698    #[serde(default)]
13699    pub exclude: Vec<String>,
13700    /// Include superseded standards in the output (for historical analysis).
13701    #[serde(default)]
13702    pub include_superseded: bool,
13703}
13704
13705/// Configuration for audit procedure template generation.
13706#[derive(Debug, Clone, Serialize, Deserialize)]
13707pub struct AuditProcedureGenConfig {
13708    /// Whether audit procedure generation is enabled.
13709    #[serde(default)]
13710    pub enabled: bool,
13711    /// Number of procedures to generate per applicable standard.
13712    #[serde(default = "default_procedures_per_standard")]
13713    pub procedures_per_standard: usize,
13714    /// Sampling methodology: "statistical", "non_statistical", "mixed".
13715    #[serde(default = "default_sampling_method")]
13716    pub sampling_method: String,
13717    /// Confidence level for statistical sampling (0.0-1.0).
13718    #[serde(default = "default_confidence_level")]
13719    pub confidence_level: f64,
13720    /// Tolerable misstatement rate for sampling (0.0-1.0).
13721    #[serde(default = "default_tolerable_misstatement")]
13722    pub tolerable_misstatement: f64,
13723}
13724
13725fn default_procedures_per_standard() -> usize {
13726    3
13727}
13728
13729fn default_sampling_method() -> String {
13730    "statistical".to_string()
13731}
13732
13733fn default_confidence_level() -> f64 {
13734    0.95
13735}
13736
13737fn default_tolerable_misstatement() -> f64 {
13738    0.05
13739}
13740
13741impl Default for AuditProcedureGenConfig {
13742    fn default() -> Self {
13743        Self {
13744            enabled: false,
13745            procedures_per_standard: default_procedures_per_standard(),
13746            sampling_method: default_sampling_method(),
13747            confidence_level: default_confidence_level(),
13748            tolerable_misstatement: default_tolerable_misstatement(),
13749        }
13750    }
13751}
13752
13753/// Configuration for compliance finding generation.
13754#[derive(Debug, Clone, Serialize, Deserialize)]
13755pub struct ComplianceFindingGenConfig {
13756    /// Whether finding generation is enabled.
13757    #[serde(default)]
13758    pub enabled: bool,
13759    /// Rate of findings per audit procedure (0.0-1.0).
13760    #[serde(default = "default_finding_rate")]
13761    pub finding_rate: f64,
13762    /// Rate of material weakness findings among all findings (0.0-1.0).
13763    #[serde(default = "default_cr_material_weakness_rate")]
13764    pub material_weakness_rate: f64,
13765    /// Rate of significant deficiency findings among all findings (0.0-1.0).
13766    #[serde(default = "default_cr_significant_deficiency_rate")]
13767    pub significant_deficiency_rate: f64,
13768    /// Whether to generate remediation plans for findings.
13769    #[serde(default = "default_true")]
13770    pub generate_remediation: bool,
13771}
13772
13773fn default_finding_rate() -> f64 {
13774    0.05
13775}
13776
13777fn default_cr_material_weakness_rate() -> f64 {
13778    0.02
13779}
13780
13781fn default_cr_significant_deficiency_rate() -> f64 {
13782    0.08
13783}
13784
13785impl Default for ComplianceFindingGenConfig {
13786    fn default() -> Self {
13787        Self {
13788            enabled: false,
13789            finding_rate: default_finding_rate(),
13790            material_weakness_rate: default_cr_material_weakness_rate(),
13791            significant_deficiency_rate: default_cr_significant_deficiency_rate(),
13792            generate_remediation: true,
13793        }
13794    }
13795}
13796
13797/// Configuration for regulatory filing generation.
13798#[derive(Debug, Clone, Serialize, Deserialize)]
13799pub struct ComplianceFilingGenConfig {
13800    /// Whether filing generation is enabled.
13801    #[serde(default)]
13802    pub enabled: bool,
13803    /// Filing types to include (e.g., ["10-K", "10-Q", "Jahresabschluss"]).
13804    /// Empty = all applicable filings for the selected jurisdictions.
13805    #[serde(default)]
13806    pub filing_types: Vec<String>,
13807    /// Generate filing status progression (draft → filed → accepted).
13808    #[serde(default = "default_true")]
13809    pub generate_status_progression: bool,
13810}
13811
13812impl Default for ComplianceFilingGenConfig {
13813    fn default() -> Self {
13814        Self {
13815            enabled: false,
13816            filing_types: Vec::new(),
13817            generate_status_progression: true,
13818        }
13819    }
13820}
13821
13822/// Configuration for compliance graph integration.
13823#[derive(Debug, Clone, Serialize, Deserialize)]
13824pub struct ComplianceGraphConfig {
13825    /// Whether compliance graph integration is enabled.
13826    #[serde(default)]
13827    pub enabled: bool,
13828    /// Include compliance nodes (Standard, Regulation, Jurisdiction, etc.).
13829    #[serde(default = "default_true")]
13830    pub include_compliance_nodes: bool,
13831    /// Include compliance edges (MapsToStandard, TestsControl, etc.).
13832    #[serde(default = "default_true")]
13833    pub include_compliance_edges: bool,
13834    /// Include cross-reference edges between standards.
13835    #[serde(default = "default_true")]
13836    pub include_cross_references: bool,
13837    /// Include temporal supersession edges.
13838    #[serde(default)]
13839    pub include_supersession_edges: bool,
13840    /// Include edges linking standards to the GL account types they govern.
13841    #[serde(default = "default_true")]
13842    pub include_account_links: bool,
13843    /// Include edges linking standards to the internal controls that implement them.
13844    #[serde(default = "default_true")]
13845    pub include_control_links: bool,
13846    /// Include edges linking filings and jurisdictions to the originating company.
13847    #[serde(default = "default_true")]
13848    pub include_company_links: bool,
13849}
13850
13851impl Default for ComplianceGraphConfig {
13852    fn default() -> Self {
13853        Self {
13854            enabled: false,
13855            include_compliance_nodes: true,
13856            include_compliance_edges: true,
13857            include_cross_references: true,
13858            include_supersession_edges: false,
13859            include_account_links: true,
13860            include_control_links: true,
13861            include_company_links: true,
13862        }
13863    }
13864}
13865
13866/// Output settings for compliance-specific data files.
13867#[derive(Debug, Clone, Serialize, Deserialize)]
13868pub struct ComplianceOutputConfig {
13869    /// Export the standards registry catalog.
13870    #[serde(default = "default_true")]
13871    pub export_registry: bool,
13872    /// Export jurisdiction profiles.
13873    #[serde(default = "default_true")]
13874    pub export_jurisdictions: bool,
13875    /// Export cross-reference map.
13876    #[serde(default = "default_true")]
13877    pub export_cross_references: bool,
13878    /// Export temporal version history.
13879    #[serde(default)]
13880    pub export_version_history: bool,
13881}
13882
13883impl Default for ComplianceOutputConfig {
13884    fn default() -> Self {
13885        Self {
13886            export_registry: true,
13887            export_jurisdictions: true,
13888            export_cross_references: true,
13889            export_version_history: false,
13890        }
13891    }
13892}
13893
13894#[cfg(test)]
13895#[allow(clippy::unwrap_used)]
13896mod tests {
13897    use super::*;
13898    use crate::presets::demo_preset;
13899
13900    // ==========================================================================
13901    // Serialization/Deserialization Tests
13902    // ==========================================================================
13903
13904    #[test]
13905    fn test_config_yaml_roundtrip() {
13906        let config = demo_preset();
13907        let yaml = serde_yaml::to_string(&config).expect("Failed to serialize to YAML");
13908        let deserialized: GeneratorConfig =
13909            serde_yaml::from_str(&yaml).expect("Failed to deserialize from YAML");
13910
13911        assert_eq!(
13912            config.global.period_months,
13913            deserialized.global.period_months
13914        );
13915        assert_eq!(config.global.industry, deserialized.global.industry);
13916        assert_eq!(config.companies.len(), deserialized.companies.len());
13917        assert_eq!(config.companies[0].code, deserialized.companies[0].code);
13918    }
13919
13920    #[test]
13921    fn test_config_json_roundtrip() {
13922        // Create a config without infinity values (JSON can't serialize f64::INFINITY)
13923        let mut config = demo_preset();
13924        // Replace infinity with a large but finite value for JSON compatibility
13925        config.master_data.employees.approval_limits.executive = 1e12;
13926
13927        let json = serde_json::to_string(&config).expect("Failed to serialize to JSON");
13928        let deserialized: GeneratorConfig =
13929            serde_json::from_str(&json).expect("Failed to deserialize from JSON");
13930
13931        assert_eq!(
13932            config.global.period_months,
13933            deserialized.global.period_months
13934        );
13935        assert_eq!(config.global.industry, deserialized.global.industry);
13936        assert_eq!(config.companies.len(), deserialized.companies.len());
13937    }
13938
13939    #[test]
13940    fn test_transaction_volume_serialization() {
13941        // Test various transaction volumes serialize correctly
13942        let volumes = vec![
13943            (TransactionVolume::TenK, "ten_k"),
13944            (TransactionVolume::HundredK, "hundred_k"),
13945            (TransactionVolume::OneM, "one_m"),
13946            (TransactionVolume::TenM, "ten_m"),
13947            (TransactionVolume::HundredM, "hundred_m"),
13948        ];
13949
13950        for (volume, expected_key) in volumes {
13951            let json = serde_json::to_string(&volume).expect("Failed to serialize");
13952            assert!(
13953                json.contains(expected_key),
13954                "Expected {} in JSON: {}",
13955                expected_key,
13956                json
13957            );
13958        }
13959    }
13960
13961    #[test]
13962    fn test_transaction_volume_custom_serialization() {
13963        let volume = TransactionVolume::Custom(12345);
13964        let json = serde_json::to_string(&volume).expect("Failed to serialize");
13965        let deserialized: TransactionVolume =
13966            serde_json::from_str(&json).expect("Failed to deserialize");
13967        assert_eq!(deserialized.count(), 12345);
13968    }
13969
13970    #[test]
13971    fn test_output_mode_serialization() {
13972        let modes = vec![
13973            OutputMode::Streaming,
13974            OutputMode::FlatFile,
13975            OutputMode::Both,
13976        ];
13977
13978        for mode in modes {
13979            let json = serde_json::to_string(&mode).expect("Failed to serialize");
13980            let deserialized: OutputMode =
13981                serde_json::from_str(&json).expect("Failed to deserialize");
13982            assert!(format!("{:?}", mode) == format!("{:?}", deserialized));
13983        }
13984    }
13985
13986    #[test]
13987    fn test_file_format_serialization() {
13988        let formats = vec![
13989            FileFormat::Csv,
13990            FileFormat::Parquet,
13991            FileFormat::Json,
13992            FileFormat::JsonLines,
13993        ];
13994
13995        for format in formats {
13996            let json = serde_json::to_string(&format).expect("Failed to serialize");
13997            let deserialized: FileFormat =
13998                serde_json::from_str(&json).expect("Failed to deserialize");
13999            assert!(format!("{:?}", format) == format!("{:?}", deserialized));
14000        }
14001    }
14002
14003    #[test]
14004    fn test_compression_algorithm_serialization() {
14005        let algos = vec![
14006            CompressionAlgorithm::Gzip,
14007            CompressionAlgorithm::Zstd,
14008            CompressionAlgorithm::Lz4,
14009            CompressionAlgorithm::Snappy,
14010        ];
14011
14012        for algo in algos {
14013            let json = serde_json::to_string(&algo).expect("Failed to serialize");
14014            let deserialized: CompressionAlgorithm =
14015                serde_json::from_str(&json).expect("Failed to deserialize");
14016            assert!(format!("{:?}", algo) == format!("{:?}", deserialized));
14017        }
14018    }
14019
14020    #[test]
14021    fn test_transfer_pricing_method_serialization() {
14022        let methods = vec![
14023            TransferPricingMethod::CostPlus,
14024            TransferPricingMethod::ComparableUncontrolled,
14025            TransferPricingMethod::ResalePrice,
14026            TransferPricingMethod::TransactionalNetMargin,
14027            TransferPricingMethod::ProfitSplit,
14028        ];
14029
14030        for method in methods {
14031            let json = serde_json::to_string(&method).expect("Failed to serialize");
14032            let deserialized: TransferPricingMethod =
14033                serde_json::from_str(&json).expect("Failed to deserialize");
14034            assert!(format!("{:?}", method) == format!("{:?}", deserialized));
14035        }
14036    }
14037
14038    #[test]
14039    fn test_benford_exemption_serialization() {
14040        let exemptions = vec![
14041            BenfordExemption::Recurring,
14042            BenfordExemption::Payroll,
14043            BenfordExemption::FixedFees,
14044            BenfordExemption::RoundAmounts,
14045        ];
14046
14047        for exemption in exemptions {
14048            let json = serde_json::to_string(&exemption).expect("Failed to serialize");
14049            let deserialized: BenfordExemption =
14050                serde_json::from_str(&json).expect("Failed to deserialize");
14051            assert!(format!("{:?}", exemption) == format!("{:?}", deserialized));
14052        }
14053    }
14054
14055    // ==========================================================================
14056    // Default Value Tests
14057    // ==========================================================================
14058
14059    #[test]
14060    fn test_global_config_defaults() {
14061        let yaml = r#"
14062            industry: manufacturing
14063            start_date: "2024-01-01"
14064            period_months: 6
14065        "#;
14066        let config: GlobalConfig = serde_yaml::from_str(yaml).expect("Failed to parse");
14067        assert_eq!(config.group_currency, "USD");
14068        assert!(config.parallel);
14069        assert_eq!(config.worker_threads, 0);
14070        assert_eq!(config.memory_limit_mb, 0);
14071    }
14072
14073    #[test]
14074    fn test_fraud_config_defaults() {
14075        let config = FraudConfig::default();
14076        assert!(!config.enabled);
14077        assert_eq!(config.fraud_rate, 0.005);
14078        assert!(!config.clustering_enabled);
14079    }
14080
14081    #[test]
14082    fn test_internal_controls_config_defaults() {
14083        let config = InternalControlsConfig::default();
14084        assert!(!config.enabled);
14085        assert_eq!(config.exception_rate, 0.02);
14086        assert_eq!(config.sod_violation_rate, 0.01);
14087        assert!(config.export_control_master_data);
14088        assert_eq!(config.sox_materiality_threshold, 10000.0);
14089        // COSO fields
14090        assert!(config.coso_enabled);
14091        assert!(!config.include_entity_level_controls);
14092        assert_eq!(config.target_maturity_level, "mixed");
14093    }
14094
14095    #[test]
14096    fn test_output_config_defaults() {
14097        let config = OutputConfig::default();
14098        assert!(matches!(config.mode, OutputMode::FlatFile));
14099        assert_eq!(config.formats, vec![FileFormat::Parquet]);
14100        assert!(config.compression.enabled);
14101        assert!(matches!(
14102            config.compression.algorithm,
14103            CompressionAlgorithm::Zstd
14104        ));
14105        assert!(config.include_acdoca);
14106        assert!(!config.include_bseg);
14107        assert!(config.partition_by_period);
14108        assert!(!config.partition_by_company);
14109    }
14110
14111    #[test]
14112    fn test_approval_config_defaults() {
14113        let config = ApprovalConfig::default();
14114        assert!(!config.enabled);
14115        assert_eq!(config.auto_approve_threshold, 1000.0);
14116        assert_eq!(config.rejection_rate, 0.02);
14117        assert_eq!(config.revision_rate, 0.05);
14118        assert_eq!(config.average_approval_delay_hours, 4.0);
14119        assert_eq!(config.thresholds.len(), 4);
14120    }
14121
14122    #[test]
14123    fn test_p2p_flow_config_defaults() {
14124        let config = P2PFlowConfig::default();
14125        assert!(config.enabled);
14126        assert_eq!(config.three_way_match_rate, 0.95);
14127        assert_eq!(config.partial_delivery_rate, 0.15);
14128        assert_eq!(config.average_po_to_gr_days, 14);
14129    }
14130
14131    #[test]
14132    fn test_o2c_flow_config_defaults() {
14133        let config = O2CFlowConfig::default();
14134        assert!(config.enabled);
14135        assert_eq!(config.credit_check_failure_rate, 0.02);
14136        assert_eq!(config.return_rate, 0.03);
14137        assert_eq!(config.bad_debt_rate, 0.01);
14138    }
14139
14140    #[test]
14141    fn test_balance_config_defaults() {
14142        let config = BalanceConfig::default();
14143        assert!(!config.generate_opening_balances);
14144        assert!(config.generate_trial_balances);
14145        assert_eq!(config.target_gross_margin, 0.35);
14146        assert!(config.validate_balance_equation);
14147        assert!(config.reconcile_subledgers);
14148    }
14149
14150    // ==========================================================================
14151    // Partial Config Deserialization Tests
14152    // ==========================================================================
14153
14154    #[test]
14155    fn test_partial_config_with_defaults() {
14156        // Minimal config that should use all defaults
14157        let yaml = r#"
14158            global:
14159              industry: manufacturing
14160              start_date: "2024-01-01"
14161              period_months: 3
14162            companies:
14163              - code: "TEST"
14164                name: "Test Company"
14165                currency: "USD"
14166                country: "US"
14167                annual_transaction_volume: ten_k
14168            chart_of_accounts:
14169              complexity: small
14170            output:
14171              output_directory: "./output"
14172        "#;
14173
14174        let config: GeneratorConfig = serde_yaml::from_str(yaml).expect("Failed to parse");
14175        assert_eq!(config.global.period_months, 3);
14176        assert_eq!(config.companies.len(), 1);
14177        assert!(!config.fraud.enabled); // Default
14178        assert!(!config.internal_controls.enabled); // Default
14179    }
14180
14181    #[test]
14182    fn test_config_with_fraud_enabled() {
14183        let yaml = r#"
14184            global:
14185              industry: retail
14186              start_date: "2024-01-01"
14187              period_months: 12
14188            companies:
14189              - code: "RETAIL"
14190                name: "Retail Co"
14191                currency: "USD"
14192                country: "US"
14193                annual_transaction_volume: hundred_k
14194            chart_of_accounts:
14195              complexity: medium
14196            output:
14197              output_directory: "./output"
14198            fraud:
14199              enabled: true
14200              fraud_rate: 0.05
14201              clustering_enabled: true
14202        "#;
14203
14204        let config: GeneratorConfig = serde_yaml::from_str(yaml).expect("Failed to parse");
14205        assert!(config.fraud.enabled);
14206        assert_eq!(config.fraud.fraud_rate, 0.05);
14207        assert!(config.fraud.clustering_enabled);
14208    }
14209
14210    #[test]
14211    fn test_config_with_multiple_companies() {
14212        let yaml = r#"
14213            global:
14214              industry: manufacturing
14215              start_date: "2024-01-01"
14216              period_months: 6
14217            companies:
14218              - code: "HQ"
14219                name: "Headquarters"
14220                currency: "USD"
14221                country: "US"
14222                annual_transaction_volume: hundred_k
14223                volume_weight: 1.0
14224              - code: "EU"
14225                name: "European Subsidiary"
14226                currency: "EUR"
14227                country: "DE"
14228                annual_transaction_volume: hundred_k
14229                volume_weight: 0.5
14230              - code: "APAC"
14231                name: "Asia Pacific"
14232                currency: "JPY"
14233                country: "JP"
14234                annual_transaction_volume: ten_k
14235                volume_weight: 0.3
14236            chart_of_accounts:
14237              complexity: large
14238            output:
14239              output_directory: "./output"
14240        "#;
14241
14242        let config: GeneratorConfig = serde_yaml::from_str(yaml).expect("Failed to parse");
14243        assert_eq!(config.companies.len(), 3);
14244        assert_eq!(config.companies[0].code, "HQ");
14245        assert_eq!(config.companies[1].currency, "EUR");
14246        assert_eq!(config.companies[2].volume_weight, 0.3);
14247    }
14248
14249    #[test]
14250    fn test_intercompany_config() {
14251        let yaml = r#"
14252            enabled: true
14253            ic_transaction_rate: 0.20
14254            transfer_pricing_method: cost_plus
14255            markup_percent: 0.08
14256            generate_matched_pairs: true
14257            generate_eliminations: true
14258        "#;
14259
14260        let config: IntercompanyConfig = serde_yaml::from_str(yaml).expect("Failed to parse");
14261        assert!(config.enabled);
14262        assert_eq!(config.ic_transaction_rate, 0.20);
14263        assert!(matches!(
14264            config.transfer_pricing_method,
14265            TransferPricingMethod::CostPlus
14266        ));
14267        assert_eq!(config.markup_percent, 0.08);
14268        assert!(config.generate_eliminations);
14269    }
14270
14271    // ==========================================================================
14272    // Company Config Tests
14273    // ==========================================================================
14274
14275    #[test]
14276    fn test_company_config_defaults() {
14277        let yaml = r#"
14278            code: "TEST"
14279            name: "Test Company"
14280            currency: "USD"
14281            country: "US"
14282            annual_transaction_volume: ten_k
14283        "#;
14284
14285        let config: CompanyConfig = serde_yaml::from_str(yaml).expect("Failed to parse");
14286        assert_eq!(config.fiscal_year_variant, "K4"); // Default
14287        assert_eq!(config.volume_weight, 1.0); // Default
14288    }
14289
14290    // ==========================================================================
14291    // Chart of Accounts Config Tests
14292    // ==========================================================================
14293
14294    #[test]
14295    fn test_coa_config_defaults() {
14296        let yaml = r#"
14297            complexity: medium
14298        "#;
14299
14300        let config: ChartOfAccountsConfig = serde_yaml::from_str(yaml).expect("Failed to parse");
14301        assert!(config.industry_specific); // Default true
14302        assert!(config.custom_accounts.is_none());
14303        assert_eq!(config.min_hierarchy_depth, 2); // Default
14304        assert_eq!(config.max_hierarchy_depth, 5); // Default
14305    }
14306
14307    // ==========================================================================
14308    // Accounting Standards Config Tests
14309    // ==========================================================================
14310
14311    #[test]
14312    fn test_accounting_standards_config_defaults() {
14313        let config = AccountingStandardsConfig::default();
14314        assert!(!config.enabled);
14315        assert!(config.framework.is_none());
14316        assert!(!config.revenue_recognition.enabled);
14317        assert!(!config.leases.enabled);
14318        assert!(!config.fair_value.enabled);
14319        assert!(!config.impairment.enabled);
14320        assert!(!config.generate_differences);
14321    }
14322
14323    #[test]
14324    fn test_accounting_standards_config_yaml() {
14325        let yaml = r#"
14326            enabled: true
14327            framework: ifrs
14328            revenue_recognition:
14329              enabled: true
14330              generate_contracts: true
14331              avg_obligations_per_contract: 2.5
14332              variable_consideration_rate: 0.20
14333              over_time_recognition_rate: 0.35
14334              contract_count: 150
14335            leases:
14336              enabled: true
14337              lease_count: 75
14338              finance_lease_percent: 0.25
14339              avg_lease_term_months: 48
14340            generate_differences: true
14341        "#;
14342
14343        let config: AccountingStandardsConfig =
14344            serde_yaml::from_str(yaml).expect("Failed to parse");
14345        assert!(config.enabled);
14346        assert!(matches!(
14347            config.framework,
14348            Some(AccountingFrameworkConfig::Ifrs)
14349        ));
14350        assert!(config.revenue_recognition.enabled);
14351        assert_eq!(config.revenue_recognition.contract_count, 150);
14352        assert_eq!(config.revenue_recognition.avg_obligations_per_contract, 2.5);
14353        assert!(config.leases.enabled);
14354        assert_eq!(config.leases.lease_count, 75);
14355        assert_eq!(config.leases.finance_lease_percent, 0.25);
14356        assert!(config.generate_differences);
14357    }
14358
14359    #[test]
14360    fn test_accounting_framework_serialization() {
14361        let frameworks = [
14362            AccountingFrameworkConfig::UsGaap,
14363            AccountingFrameworkConfig::Ifrs,
14364            AccountingFrameworkConfig::DualReporting,
14365            AccountingFrameworkConfig::FrenchGaap,
14366            AccountingFrameworkConfig::GermanGaap,
14367        ];
14368
14369        for framework in frameworks {
14370            let json = serde_json::to_string(&framework).expect("Failed to serialize");
14371            let deserialized: AccountingFrameworkConfig =
14372                serde_json::from_str(&json).expect("Failed to deserialize");
14373            assert!(format!("{:?}", framework) == format!("{:?}", deserialized));
14374        }
14375    }
14376
14377    #[test]
14378    fn test_revenue_recognition_config_defaults() {
14379        let config = RevenueRecognitionConfig::default();
14380        assert!(!config.enabled);
14381        assert!(config.generate_contracts);
14382        assert_eq!(config.avg_obligations_per_contract, 2.0);
14383        assert_eq!(config.variable_consideration_rate, 0.15);
14384        assert_eq!(config.over_time_recognition_rate, 0.30);
14385        assert_eq!(config.contract_count, 100);
14386    }
14387
14388    #[test]
14389    fn test_lease_accounting_config_defaults() {
14390        let config = LeaseAccountingConfig::default();
14391        assert!(!config.enabled);
14392        assert_eq!(config.lease_count, 50);
14393        assert_eq!(config.finance_lease_percent, 0.30);
14394        assert_eq!(config.avg_lease_term_months, 60);
14395        assert!(config.generate_amortization);
14396        assert_eq!(config.real_estate_percent, 0.40);
14397    }
14398
14399    #[test]
14400    fn test_fair_value_config_defaults() {
14401        let config = FairValueConfig::default();
14402        assert!(!config.enabled);
14403        assert_eq!(config.measurement_count, 25);
14404        assert_eq!(config.level1_percent, 0.40);
14405        assert_eq!(config.level2_percent, 0.35);
14406        assert_eq!(config.level3_percent, 0.25);
14407        assert!(!config.include_sensitivity_analysis);
14408    }
14409
14410    #[test]
14411    fn test_impairment_config_defaults() {
14412        let config = ImpairmentConfig::default();
14413        assert!(!config.enabled);
14414        assert_eq!(config.test_count, 15);
14415        assert_eq!(config.impairment_rate, 0.10);
14416        assert!(config.generate_projections);
14417        assert!(!config.include_goodwill);
14418    }
14419
14420    // ==========================================================================
14421    // Audit Standards Config Tests
14422    // ==========================================================================
14423
14424    #[test]
14425    fn test_audit_standards_config_defaults() {
14426        let config = AuditStandardsConfig::default();
14427        assert!(!config.enabled);
14428        assert!(!config.isa_compliance.enabled);
14429        assert!(!config.analytical_procedures.enabled);
14430        assert!(!config.confirmations.enabled);
14431        assert!(!config.opinion.enabled);
14432        assert!(!config.generate_audit_trail);
14433        assert!(!config.sox.enabled);
14434        assert!(!config.pcaob.enabled);
14435    }
14436
14437    #[test]
14438    fn test_audit_standards_config_yaml() {
14439        let yaml = r#"
14440            enabled: true
14441            isa_compliance:
14442              enabled: true
14443              compliance_level: comprehensive
14444              generate_isa_mappings: true
14445              include_pcaob: true
14446              framework: dual
14447            analytical_procedures:
14448              enabled: true
14449              procedures_per_account: 5
14450              variance_probability: 0.25
14451            confirmations:
14452              enabled: true
14453              confirmation_count: 75
14454              positive_response_rate: 0.90
14455              exception_rate: 0.08
14456            opinion:
14457              enabled: true
14458              generate_kam: true
14459              average_kam_count: 4
14460            sox:
14461              enabled: true
14462              generate_302_certifications: true
14463              generate_404_assessments: true
14464              material_weakness_rate: 0.03
14465            pcaob:
14466              enabled: true
14467              is_pcaob_audit: true
14468              include_icfr_opinion: true
14469            generate_audit_trail: true
14470        "#;
14471
14472        let config: AuditStandardsConfig = serde_yaml::from_str(yaml).expect("Failed to parse");
14473        assert!(config.enabled);
14474        assert!(config.isa_compliance.enabled);
14475        assert_eq!(config.isa_compliance.compliance_level, "comprehensive");
14476        assert!(config.isa_compliance.include_pcaob);
14477        assert_eq!(config.isa_compliance.framework, "dual");
14478        assert!(config.analytical_procedures.enabled);
14479        assert_eq!(config.analytical_procedures.procedures_per_account, 5);
14480        assert!(config.confirmations.enabled);
14481        assert_eq!(config.confirmations.confirmation_count, 75);
14482        assert!(config.opinion.enabled);
14483        assert_eq!(config.opinion.average_kam_count, 4);
14484        assert!(config.sox.enabled);
14485        assert!(config.sox.generate_302_certifications);
14486        assert_eq!(config.sox.material_weakness_rate, 0.03);
14487        assert!(config.pcaob.enabled);
14488        assert!(config.pcaob.is_pcaob_audit);
14489        assert!(config.pcaob.include_icfr_opinion);
14490        assert!(config.generate_audit_trail);
14491    }
14492
14493    #[test]
14494    fn test_isa_compliance_config_defaults() {
14495        let config = IsaComplianceConfig::default();
14496        assert!(!config.enabled);
14497        assert_eq!(config.compliance_level, "standard");
14498        assert!(config.generate_isa_mappings);
14499        assert!(config.generate_coverage_summary);
14500        assert!(!config.include_pcaob);
14501        assert_eq!(config.framework, "isa");
14502    }
14503
14504    #[test]
14505    fn test_sox_compliance_config_defaults() {
14506        let config = SoxComplianceConfig::default();
14507        assert!(!config.enabled);
14508        assert!(config.generate_302_certifications);
14509        assert!(config.generate_404_assessments);
14510        assert_eq!(config.materiality_threshold, 10000.0);
14511        assert_eq!(config.material_weakness_rate, 0.02);
14512        assert_eq!(config.significant_deficiency_rate, 0.08);
14513    }
14514
14515    #[test]
14516    fn test_pcaob_config_defaults() {
14517        let config = PcaobConfig::default();
14518        assert!(!config.enabled);
14519        assert!(!config.is_pcaob_audit);
14520        assert!(config.generate_cam);
14521        assert!(!config.include_icfr_opinion);
14522        assert!(!config.generate_standard_mappings);
14523    }
14524
14525    #[test]
14526    fn test_config_with_standards_enabled() {
14527        let yaml = r#"
14528            global:
14529              industry: financial_services
14530              start_date: "2024-01-01"
14531              period_months: 12
14532            companies:
14533              - code: "BANK"
14534                name: "Test Bank"
14535                currency: "USD"
14536                country: "US"
14537                annual_transaction_volume: hundred_k
14538            chart_of_accounts:
14539              complexity: large
14540            output:
14541              output_directory: "./output"
14542            accounting_standards:
14543              enabled: true
14544              framework: us_gaap
14545              revenue_recognition:
14546                enabled: true
14547              leases:
14548                enabled: true
14549            audit_standards:
14550              enabled: true
14551              isa_compliance:
14552                enabled: true
14553              sox:
14554                enabled: true
14555        "#;
14556
14557        let config: GeneratorConfig = serde_yaml::from_str(yaml).expect("Failed to parse");
14558        assert!(config.accounting_standards.enabled);
14559        assert!(matches!(
14560            config.accounting_standards.framework,
14561            Some(AccountingFrameworkConfig::UsGaap)
14562        ));
14563        assert!(config.accounting_standards.revenue_recognition.enabled);
14564        assert!(config.accounting_standards.leases.enabled);
14565        assert!(config.audit_standards.enabled);
14566        assert!(config.audit_standards.isa_compliance.enabled);
14567        assert!(config.audit_standards.sox.enabled);
14568    }
14569
14570    // ==========================================================================
14571    // Industry-Specific Config Tests
14572    // ==========================================================================
14573
14574    #[test]
14575    fn test_industry_specific_config_defaults() {
14576        let config = IndustrySpecificConfig::default();
14577        assert!(!config.enabled);
14578        assert!(!config.manufacturing.enabled);
14579        assert!(!config.retail.enabled);
14580        assert!(!config.healthcare.enabled);
14581        assert!(!config.technology.enabled);
14582        assert!(!config.financial_services.enabled);
14583        assert!(!config.professional_services.enabled);
14584    }
14585
14586    #[test]
14587    fn test_manufacturing_config_defaults() {
14588        let config = ManufacturingConfig::default();
14589        assert!(!config.enabled);
14590        assert_eq!(config.bom_depth, 4);
14591        assert!(!config.just_in_time);
14592        assert_eq!(config.supplier_tiers, 2);
14593        assert_eq!(config.target_yield_rate, 0.97);
14594        assert_eq!(config.scrap_alert_threshold, 0.03);
14595    }
14596
14597    #[test]
14598    fn test_retail_config_defaults() {
14599        let config = RetailConfig::default();
14600        assert!(!config.enabled);
14601        assert_eq!(config.avg_daily_transactions, 500);
14602        assert!(config.loss_prevention);
14603        assert_eq!(config.shrinkage_rate, 0.015);
14604    }
14605
14606    #[test]
14607    fn test_healthcare_config_defaults() {
14608        let config = HealthcareConfig::default();
14609        assert!(!config.enabled);
14610        assert_eq!(config.facility_type, "hospital");
14611        assert_eq!(config.avg_daily_encounters, 150);
14612        assert!(config.compliance.hipaa);
14613        assert!(config.compliance.stark_law);
14614        assert!(config.coding_systems.icd10);
14615        assert!(config.coding_systems.cpt);
14616    }
14617
14618    #[test]
14619    fn test_technology_config_defaults() {
14620        let config = TechnologyConfig::default();
14621        assert!(!config.enabled);
14622        assert_eq!(config.revenue_model, "saas");
14623        assert_eq!(config.subscription_revenue_pct, 0.60);
14624        assert!(config.rd_capitalization.enabled);
14625    }
14626
14627    #[test]
14628    fn test_config_with_industry_specific() {
14629        let yaml = r#"
14630            global:
14631              industry: healthcare
14632              start_date: "2024-01-01"
14633              period_months: 12
14634            companies:
14635              - code: "HOSP"
14636                name: "Test Hospital"
14637                currency: "USD"
14638                country: "US"
14639                annual_transaction_volume: hundred_k
14640            chart_of_accounts:
14641              complexity: medium
14642            output:
14643              output_directory: "./output"
14644            industry_specific:
14645              enabled: true
14646              healthcare:
14647                enabled: true
14648                facility_type: hospital
14649                payer_mix:
14650                  medicare: 0.45
14651                  medicaid: 0.15
14652                  commercial: 0.35
14653                  self_pay: 0.05
14654                coding_systems:
14655                  icd10: true
14656                  cpt: true
14657                  drg: true
14658                compliance:
14659                  hipaa: true
14660                  stark_law: true
14661                anomaly_rates:
14662                  upcoding: 0.03
14663                  unbundling: 0.02
14664        "#;
14665
14666        let config: GeneratorConfig = serde_yaml::from_str(yaml).expect("Failed to parse");
14667        assert!(config.industry_specific.enabled);
14668        assert!(config.industry_specific.healthcare.enabled);
14669        assert_eq!(
14670            config.industry_specific.healthcare.facility_type,
14671            "hospital"
14672        );
14673        assert_eq!(config.industry_specific.healthcare.payer_mix.medicare, 0.45);
14674        assert_eq!(config.industry_specific.healthcare.payer_mix.self_pay, 0.05);
14675        assert!(config.industry_specific.healthcare.coding_systems.icd10);
14676        assert!(config.industry_specific.healthcare.compliance.hipaa);
14677        assert_eq!(
14678            config.industry_specific.healthcare.anomaly_rates.upcoding,
14679            0.03
14680        );
14681    }
14682
14683    #[test]
14684    fn test_config_with_manufacturing_specific() {
14685        let yaml = r#"
14686            global:
14687              industry: manufacturing
14688              start_date: "2024-01-01"
14689              period_months: 12
14690            companies:
14691              - code: "MFG"
14692                name: "Test Manufacturing"
14693                currency: "USD"
14694                country: "US"
14695                annual_transaction_volume: hundred_k
14696            chart_of_accounts:
14697              complexity: medium
14698            output:
14699              output_directory: "./output"
14700            industry_specific:
14701              enabled: true
14702              manufacturing:
14703                enabled: true
14704                bom_depth: 5
14705                just_in_time: true
14706                supplier_tiers: 3
14707                target_yield_rate: 0.98
14708                anomaly_rates:
14709                  yield_manipulation: 0.02
14710                  phantom_production: 0.01
14711        "#;
14712
14713        let config: GeneratorConfig = serde_yaml::from_str(yaml).expect("Failed to parse");
14714        assert!(config.industry_specific.enabled);
14715        assert!(config.industry_specific.manufacturing.enabled);
14716        assert_eq!(config.industry_specific.manufacturing.bom_depth, 5);
14717        assert!(config.industry_specific.manufacturing.just_in_time);
14718        assert_eq!(config.industry_specific.manufacturing.supplier_tiers, 3);
14719        assert_eq!(
14720            config.industry_specific.manufacturing.target_yield_rate,
14721            0.98
14722        );
14723        assert_eq!(
14724            config
14725                .industry_specific
14726                .manufacturing
14727                .anomaly_rates
14728                .yield_manipulation,
14729            0.02
14730        );
14731    }
14732
14733    // ==========================================================================
14734    // Tax Configuration Tests
14735    // ==========================================================================
14736
14737    #[test]
14738    fn test_tax_config_defaults() {
14739        let tax = TaxConfig::default();
14740        assert!(!tax.enabled);
14741        assert!(tax.jurisdictions.countries.is_empty());
14742        assert!(!tax.jurisdictions.include_subnational);
14743        assert!(!tax.vat_gst.enabled);
14744        assert!(tax.vat_gst.standard_rates.is_empty());
14745        assert!(tax.vat_gst.reduced_rates.is_empty());
14746        assert!(tax.vat_gst.exempt_categories.is_empty());
14747        assert!(tax.vat_gst.reverse_charge);
14748        assert!(!tax.sales_tax.enabled);
14749        assert!(tax.sales_tax.nexus_states.is_empty());
14750        assert!(!tax.withholding.enabled);
14751        assert!(tax.withholding.treaty_network);
14752        assert_eq!(tax.withholding.default_rate, 0.30);
14753        assert_eq!(tax.withholding.treaty_reduced_rate, 0.15);
14754        assert!(tax.provisions.enabled);
14755        assert_eq!(tax.provisions.statutory_rate, 0.21);
14756        assert!(tax.provisions.uncertain_positions);
14757        assert!(!tax.payroll_tax.enabled);
14758        assert_eq!(tax.anomaly_rate, 0.03);
14759    }
14760
14761    #[test]
14762    fn test_tax_config_from_yaml() {
14763        let yaml = r#"
14764            global:
14765              seed: 42
14766              start_date: "2024-01-01"
14767              period_months: 12
14768              industry: retail
14769            companies:
14770              - code: C001
14771                name: Test Corp
14772                currency: USD
14773                country: US
14774                annual_transaction_volume: ten_k
14775            chart_of_accounts:
14776              complexity: small
14777            output:
14778              output_directory: ./output
14779            tax:
14780              enabled: true
14781              anomaly_rate: 0.05
14782              jurisdictions:
14783                countries: ["US", "DE", "GB"]
14784                include_subnational: true
14785              vat_gst:
14786                enabled: true
14787                standard_rates:
14788                  DE: 0.19
14789                  GB: 0.20
14790                reduced_rates:
14791                  DE: 0.07
14792                  GB: 0.05
14793                exempt_categories:
14794                  - financial_services
14795                  - healthcare
14796                reverse_charge: false
14797              sales_tax:
14798                enabled: true
14799                nexus_states: ["CA", "NY", "TX"]
14800              withholding:
14801                enabled: true
14802                treaty_network: false
14803                default_rate: 0.25
14804                treaty_reduced_rate: 0.10
14805              provisions:
14806                enabled: false
14807                statutory_rate: 0.28
14808                uncertain_positions: false
14809              payroll_tax:
14810                enabled: true
14811        "#;
14812
14813        let config: GeneratorConfig = serde_yaml::from_str(yaml).expect("Failed to parse");
14814        assert!(config.tax.enabled);
14815        assert_eq!(config.tax.anomaly_rate, 0.05);
14816
14817        // Jurisdictions
14818        assert_eq!(config.tax.jurisdictions.countries.len(), 3);
14819        assert!(config
14820            .tax
14821            .jurisdictions
14822            .countries
14823            .contains(&"DE".to_string()));
14824        assert!(config.tax.jurisdictions.include_subnational);
14825
14826        // VAT/GST
14827        assert!(config.tax.vat_gst.enabled);
14828        assert_eq!(config.tax.vat_gst.standard_rates.get("DE"), Some(&0.19));
14829        assert_eq!(config.tax.vat_gst.standard_rates.get("GB"), Some(&0.20));
14830        assert_eq!(config.tax.vat_gst.reduced_rates.get("DE"), Some(&0.07));
14831        assert_eq!(config.tax.vat_gst.exempt_categories.len(), 2);
14832        assert!(!config.tax.vat_gst.reverse_charge);
14833
14834        // Sales tax
14835        assert!(config.tax.sales_tax.enabled);
14836        assert_eq!(config.tax.sales_tax.nexus_states.len(), 3);
14837        assert!(config
14838            .tax
14839            .sales_tax
14840            .nexus_states
14841            .contains(&"CA".to_string()));
14842
14843        // Withholding
14844        assert!(config.tax.withholding.enabled);
14845        assert!(!config.tax.withholding.treaty_network);
14846        assert_eq!(config.tax.withholding.default_rate, 0.25);
14847        assert_eq!(config.tax.withholding.treaty_reduced_rate, 0.10);
14848
14849        // Provisions
14850        assert!(!config.tax.provisions.enabled);
14851        assert_eq!(config.tax.provisions.statutory_rate, 0.28);
14852        assert!(!config.tax.provisions.uncertain_positions);
14853
14854        // Payroll tax
14855        assert!(config.tax.payroll_tax.enabled);
14856    }
14857
14858    #[test]
14859    fn test_generator_config_with_tax_default() {
14860        let yaml = r#"
14861            global:
14862              seed: 42
14863              start_date: "2024-01-01"
14864              period_months: 12
14865              industry: retail
14866            companies:
14867              - code: C001
14868                name: Test Corp
14869                currency: USD
14870                country: US
14871                annual_transaction_volume: ten_k
14872            chart_of_accounts:
14873              complexity: small
14874            output:
14875              output_directory: ./output
14876        "#;
14877
14878        let config: GeneratorConfig =
14879            serde_yaml::from_str(yaml).expect("Failed to parse config without tax section");
14880        // Tax should be present with defaults when not specified in YAML
14881        assert!(!config.tax.enabled);
14882        assert!(config.tax.jurisdictions.countries.is_empty());
14883        assert_eq!(config.tax.anomaly_rate, 0.03);
14884        assert!(config.tax.provisions.enabled); // provisions default to enabled=true
14885        assert_eq!(config.tax.provisions.statutory_rate, 0.21);
14886    }
14887
14888    // ==========================================================================
14889    // SessionSchemaConfig Tests
14890    // ==========================================================================
14891
14892    #[test]
14893    fn test_session_config_default_disabled() {
14894        let yaml = "{}";
14895        let config: SessionSchemaConfig =
14896            serde_yaml::from_str(yaml).expect("Failed to parse empty session config");
14897        assert!(!config.enabled);
14898        assert!(config.checkpoint_path.is_none());
14899        assert!(config.per_period_output);
14900        assert!(config.consolidated_output);
14901    }
14902
14903    #[test]
14904    fn test_config_backward_compatible_without_session() {
14905        let yaml = r#"
14906            global:
14907              seed: 42
14908              start_date: "2024-01-01"
14909              period_months: 12
14910              industry: retail
14911            companies:
14912              - code: C001
14913                name: Test Corp
14914                currency: USD
14915                country: US
14916                annual_transaction_volume: ten_k
14917            chart_of_accounts:
14918              complexity: small
14919            output:
14920              output_directory: ./output
14921        "#;
14922
14923        let config: GeneratorConfig =
14924            serde_yaml::from_str(yaml).expect("Failed to parse config without session");
14925        // Session should default to disabled
14926        assert!(!config.session.enabled);
14927        assert!(config.session.per_period_output);
14928        assert!(config.session.consolidated_output);
14929        // fiscal_year_months should be None
14930        assert!(config.global.fiscal_year_months.is_none());
14931    }
14932
14933    #[test]
14934    fn test_fiscal_year_months_parsed() {
14935        let yaml = r#"
14936            global:
14937              seed: 42
14938              start_date: "2024-01-01"
14939              period_months: 24
14940              industry: retail
14941              fiscal_year_months: 12
14942            companies:
14943              - code: C001
14944                name: Test Corp
14945                currency: USD
14946                country: US
14947                annual_transaction_volume: ten_k
14948            chart_of_accounts:
14949              complexity: small
14950            output:
14951              output_directory: ./output
14952            session:
14953              enabled: true
14954              checkpoint_path: /tmp/checkpoints
14955              per_period_output: true
14956              consolidated_output: false
14957        "#;
14958
14959        let config: GeneratorConfig =
14960            serde_yaml::from_str(yaml).expect("Failed to parse config with fiscal_year_months");
14961        assert_eq!(config.global.fiscal_year_months, Some(12));
14962        assert!(config.session.enabled);
14963        assert_eq!(
14964            config.session.checkpoint_path,
14965            Some("/tmp/checkpoints".to_string())
14966        );
14967        assert!(config.session.per_period_output);
14968        assert!(!config.session.consolidated_output);
14969    }
14970}