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
264fn default_llm_provider() -> String {
265    "mock".to_string()
266}
267
268fn default_llm_model_name() -> String {
269    "gpt-4o-mini".to_string()
270}
271
272fn default_llm_batch_size() -> usize {
273    50
274}
275
276impl Default for LlmSchemaConfig {
277    fn default() -> Self {
278        Self {
279            enabled: false,
280            provider: default_llm_provider(),
281            model: default_llm_model_name(),
282            max_vendor_enrichments: default_llm_batch_size(),
283        }
284    }
285}
286
287/// Diffusion model configuration.
288///
289/// Controls statistical diffusion-based data enhancement that generates samples
290/// matching target distribution properties (means, standard deviations, correlations).
291#[derive(Debug, Clone, Serialize, Deserialize)]
292pub struct DiffusionSchemaConfig {
293    /// Whether diffusion enhancement is enabled.
294    #[serde(default)]
295    pub enabled: bool,
296    /// Number of diffusion steps (higher = better quality, slower).
297    #[serde(default = "default_diffusion_steps")]
298    pub n_steps: usize,
299    /// Noise schedule type: "linear", "cosine", "sigmoid".
300    #[serde(default = "default_diffusion_schedule")]
301    pub schedule: String,
302    /// Number of sample rows to generate for demonstration.
303    #[serde(default = "default_diffusion_sample_size")]
304    pub sample_size: usize,
305    /// Backend type: "statistical" (default), "neural", "hybrid".
306    #[serde(default = "default_diffusion_backend")]
307    pub backend: String,
308    /// Neural diffusion backend configuration (used when backend is "neural" or "hybrid").
309    #[serde(default)]
310    pub neural: NeuralDiffusionSchemaConfig,
311}
312
313fn default_diffusion_steps() -> usize {
314    100
315}
316
317fn default_diffusion_schedule() -> String {
318    "linear".to_string()
319}
320
321fn default_diffusion_sample_size() -> usize {
322    100
323}
324
325fn default_diffusion_backend() -> String {
326    "statistical".to_string()
327}
328
329impl Default for DiffusionSchemaConfig {
330    fn default() -> Self {
331        Self {
332            enabled: false,
333            n_steps: default_diffusion_steps(),
334            schedule: default_diffusion_schedule(),
335            sample_size: default_diffusion_sample_size(),
336            backend: default_diffusion_backend(),
337            neural: NeuralDiffusionSchemaConfig::default(),
338        }
339    }
340}
341
342/// Neural diffusion backend configuration.
343///
344/// Controls the `candle`-based neural score network that learns joint distributions
345/// from training data for the neural and hybrid diffusion backends.
346#[derive(Debug, Clone, Serialize, Deserialize)]
347pub struct NeuralDiffusionSchemaConfig {
348    /// Hidden layer dimensions for the score network MLP.
349    #[serde(default = "default_neural_hidden_dims")]
350    pub hidden_dims: Vec<usize>,
351    /// Dimensionality of the timestep embedding.
352    #[serde(default = "default_neural_timestep_embed_dim")]
353    pub timestep_embed_dim: usize,
354    /// Learning rate for training.
355    #[serde(default = "default_neural_learning_rate")]
356    pub learning_rate: f64,
357    /// Number of training epochs.
358    #[serde(default = "default_neural_training_epochs")]
359    pub training_epochs: usize,
360    /// Training batch size.
361    #[serde(default = "default_neural_batch_size")]
362    pub batch_size: usize,
363    /// Blend weight for hybrid mode (0.0 = all statistical, 1.0 = all neural).
364    #[serde(default = "default_neural_hybrid_weight")]
365    pub hybrid_weight: f64,
366    /// Hybrid blending strategy: "weighted_average", "column_select", "threshold".
367    #[serde(default = "default_neural_hybrid_strategy")]
368    pub hybrid_strategy: String,
369    /// Columns to apply neural generation to (empty = all numeric columns).
370    #[serde(default)]
371    pub neural_columns: Vec<String>,
372}
373
374fn default_neural_hidden_dims() -> Vec<usize> {
375    vec![256, 256, 128]
376}
377
378fn default_neural_timestep_embed_dim() -> usize {
379    64
380}
381
382fn default_neural_learning_rate() -> f64 {
383    0.001
384}
385
386fn default_neural_training_epochs() -> usize {
387    100
388}
389
390fn default_neural_batch_size() -> usize {
391    64
392}
393
394fn default_neural_hybrid_weight() -> f64 {
395    0.5
396}
397
398fn default_neural_hybrid_strategy() -> String {
399    "weighted_average".to_string()
400}
401
402impl Default for NeuralDiffusionSchemaConfig {
403    fn default() -> Self {
404        Self {
405            hidden_dims: default_neural_hidden_dims(),
406            timestep_embed_dim: default_neural_timestep_embed_dim(),
407            learning_rate: default_neural_learning_rate(),
408            training_epochs: default_neural_training_epochs(),
409            batch_size: default_neural_batch_size(),
410            hybrid_weight: default_neural_hybrid_weight(),
411            hybrid_strategy: default_neural_hybrid_strategy(),
412            neural_columns: Vec::new(),
413        }
414    }
415}
416
417/// Causal generation configuration.
418///
419/// Controls structural causal model (SCM) based data generation that respects
420/// causal relationships between variables, supports do-calculus interventions,
421/// and enables counterfactual scenarios.
422#[derive(Debug, Clone, Serialize, Deserialize)]
423pub struct CausalSchemaConfig {
424    /// Whether causal generation is enabled.
425    #[serde(default)]
426    pub enabled: bool,
427    /// Built-in template to use: "fraud_detection", "revenue_cycle", or "custom".
428    #[serde(default = "default_causal_template")]
429    pub template: String,
430    /// Number of causal samples to generate.
431    #[serde(default = "default_causal_sample_size")]
432    pub sample_size: usize,
433    /// Whether to run causal validation on the output.
434    #[serde(default = "default_true")]
435    pub validate: bool,
436}
437
438fn default_causal_template() -> String {
439    "fraud_detection".to_string()
440}
441
442fn default_causal_sample_size() -> usize {
443    500
444}
445
446impl Default for CausalSchemaConfig {
447    fn default() -> Self {
448        Self {
449            enabled: false,
450            template: default_causal_template(),
451            sample_size: default_causal_sample_size(),
452            validate: true,
453        }
454    }
455}
456
457/// Graph export configuration for accounting network and ML training exports.
458///
459/// This section enables exporting generated data as graphs for:
460/// - Network reconstruction algorithms
461/// - Graph neural network training
462/// - Neo4j graph database import
463#[derive(Debug, Clone, Serialize, Deserialize)]
464pub struct GraphExportConfig {
465    /// Enable graph export.
466    #[serde(default)]
467    pub enabled: bool,
468
469    /// Graph types to generate.
470    #[serde(default = "default_graph_types")]
471    pub graph_types: Vec<GraphTypeConfig>,
472
473    /// Export formats to generate.
474    #[serde(default = "default_graph_formats")]
475    pub formats: Vec<GraphExportFormat>,
476
477    /// Train split ratio for ML datasets.
478    #[serde(default = "default_train_ratio")]
479    pub train_ratio: f64,
480
481    /// Validation split ratio for ML datasets.
482    #[serde(default = "default_val_ratio")]
483    pub validation_ratio: f64,
484
485    /// Random seed for train/val/test splits.
486    #[serde(default)]
487    pub split_seed: Option<u64>,
488
489    /// Output subdirectory for graph exports (relative to output directory).
490    #[serde(default = "default_graph_subdir")]
491    pub output_subdirectory: String,
492
493    /// Multi-layer hypergraph export settings for RustGraph integration.
494    #[serde(default)]
495    pub hypergraph: HypergraphExportSettings,
496
497    /// DGL-specific export settings.
498    #[serde(default)]
499    pub dgl: DglExportConfig,
500}
501
502fn default_graph_types() -> Vec<GraphTypeConfig> {
503    vec![GraphTypeConfig::default()]
504}
505
506fn default_graph_formats() -> Vec<GraphExportFormat> {
507    vec![GraphExportFormat::PytorchGeometric]
508}
509
510fn default_train_ratio() -> f64 {
511    0.7
512}
513
514fn default_val_ratio() -> f64 {
515    0.15
516}
517
518fn default_graph_subdir() -> String {
519    "graphs".to_string()
520}
521
522impl Default for GraphExportConfig {
523    fn default() -> Self {
524        Self {
525            enabled: false,
526            graph_types: default_graph_types(),
527            formats: default_graph_formats(),
528            train_ratio: 0.7,
529            validation_ratio: 0.15,
530            split_seed: None,
531            output_subdirectory: "graphs".to_string(),
532            hypergraph: HypergraphExportSettings::default(),
533            dgl: DglExportConfig::default(),
534        }
535    }
536}
537
538/// DGL-specific export settings.
539#[derive(Debug, Clone, Default, Serialize, Deserialize)]
540pub struct DglExportConfig {
541    /// Export as a heterogeneous graph (distinct node/edge types).
542    ///
543    /// When `true` the DGL exporter produces a `HeteroData` object with typed
544    /// node and edge stores rather than a single homogeneous graph.
545    /// Set to `true` in `graph_export.dgl.heterogeneous: true` in YAML.
546    #[serde(default)]
547    pub heterogeneous: bool,
548}
549
550// Default derived: heterogeneous = false (bool default)
551
552/// Settings for the multi-layer hypergraph export (RustGraph integration).
553///
554/// Produces a 3-layer hypergraph:
555/// - Layer 1: Governance & Controls (COSO, SOX, internal controls, organizational)
556/// - Layer 2: Process Events (P2P/O2C document flows, OCPM events)
557/// - Layer 3: Accounting Network (GL accounts, journal entries as hyperedges)
558#[derive(Debug, Clone, Serialize, Deserialize)]
559pub struct HypergraphExportSettings {
560    /// Enable hypergraph export.
561    #[serde(default)]
562    pub enabled: bool,
563
564    /// Maximum total nodes across all layers (default 50000).
565    #[serde(default = "default_hypergraph_max_nodes")]
566    pub max_nodes: usize,
567
568    /// Aggregation strategy when node budget is exceeded.
569    #[serde(default = "default_aggregation_strategy")]
570    pub aggregation_strategy: String,
571
572    /// Layer 1 (Governance & Controls) settings.
573    #[serde(default)]
574    pub governance_layer: GovernanceLayerSettings,
575
576    /// Layer 2 (Process Events) settings.
577    #[serde(default)]
578    pub process_layer: ProcessLayerSettings,
579
580    /// Layer 3 (Accounting Network) settings.
581    #[serde(default)]
582    pub accounting_layer: AccountingLayerSettings,
583
584    /// Cross-layer edge generation settings.
585    #[serde(default)]
586    pub cross_layer: CrossLayerSettings,
587
588    /// Output subdirectory for hypergraph files (relative to graph output directory).
589    #[serde(default = "default_hypergraph_subdir")]
590    pub output_subdirectory: String,
591
592    /// Output format: "native" (default) for internal field names, "unified" for RustGraph format.
593    #[serde(default = "default_hypergraph_format")]
594    pub output_format: String,
595
596    /// Optional URL for streaming unified JSONL to a RustGraph ingest endpoint.
597    #[serde(default)]
598    pub stream_target: Option<String>,
599
600    /// Batch size for streaming (number of JSONL lines per HTTP POST). Default: 1000.
601    #[serde(default = "default_stream_batch_size")]
602    pub stream_batch_size: usize,
603}
604
605fn default_hypergraph_max_nodes() -> usize {
606    50_000
607}
608
609fn default_aggregation_strategy() -> String {
610    "pool_by_counterparty".to_string()
611}
612
613fn default_hypergraph_subdir() -> String {
614    "hypergraph".to_string()
615}
616
617fn default_hypergraph_format() -> String {
618    "native".to_string()
619}
620
621fn default_stream_batch_size() -> usize {
622    1000
623}
624
625impl Default for HypergraphExportSettings {
626    fn default() -> Self {
627        Self {
628            enabled: false,
629            max_nodes: 50_000,
630            aggregation_strategy: "pool_by_counterparty".to_string(),
631            governance_layer: GovernanceLayerSettings::default(),
632            process_layer: ProcessLayerSettings::default(),
633            accounting_layer: AccountingLayerSettings::default(),
634            cross_layer: CrossLayerSettings::default(),
635            output_subdirectory: "hypergraph".to_string(),
636            output_format: "native".to_string(),
637            stream_target: None,
638            stream_batch_size: 1000,
639        }
640    }
641}
642
643/// Layer 1: Governance & Controls layer settings.
644#[derive(Debug, Clone, Serialize, Deserialize)]
645pub struct GovernanceLayerSettings {
646    /// Include COSO framework nodes (5 components + 17 principles).
647    #[serde(default = "default_true")]
648    pub include_coso: bool,
649    /// Include internal control nodes.
650    #[serde(default = "default_true")]
651    pub include_controls: bool,
652    /// Include SOX assertion nodes.
653    #[serde(default = "default_true")]
654    pub include_sox: bool,
655    /// Include vendor master data nodes.
656    #[serde(default = "default_true")]
657    pub include_vendors: bool,
658    /// Include customer master data nodes.
659    #[serde(default = "default_true")]
660    pub include_customers: bool,
661    /// Include employee/organizational nodes.
662    #[serde(default = "default_true")]
663    pub include_employees: bool,
664}
665
666impl Default for GovernanceLayerSettings {
667    fn default() -> Self {
668        Self {
669            include_coso: true,
670            include_controls: true,
671            include_sox: true,
672            include_vendors: true,
673            include_customers: true,
674            include_employees: true,
675        }
676    }
677}
678
679/// Layer 2: Process Events layer settings.
680#[derive(Debug, Clone, Serialize, Deserialize)]
681pub struct ProcessLayerSettings {
682    /// Include P2P (Procure-to-Pay) document flow nodes.
683    #[serde(default = "default_true")]
684    pub include_p2p: bool,
685    /// Include O2C (Order-to-Cash) document flow nodes.
686    #[serde(default = "default_true")]
687    pub include_o2c: bool,
688    /// Include S2C (Source-to-Contract) document flow nodes.
689    #[serde(default = "default_true")]
690    pub include_s2c: bool,
691    /// Include H2R (Hire-to-Retire) document flow nodes.
692    #[serde(default = "default_true")]
693    pub include_h2r: bool,
694    /// Include MFG (Manufacturing) document flow nodes.
695    #[serde(default = "default_true")]
696    pub include_mfg: bool,
697    /// Include BANK (Banking) document flow nodes.
698    #[serde(default = "default_true")]
699    pub include_bank: bool,
700    /// Include AUDIT document flow nodes.
701    #[serde(default = "default_true")]
702    pub include_audit: bool,
703    /// Include R2R (Record-to-Report) document flow nodes (bank recon + period close).
704    #[serde(default = "default_true")]
705    pub include_r2r: bool,
706    /// Export OCPM events as hyperedges.
707    #[serde(default = "default_true")]
708    pub events_as_hyperedges: bool,
709    /// Threshold: if a counterparty has more documents than this, aggregate into pool nodes.
710    #[serde(default = "default_docs_per_counterparty_threshold")]
711    pub docs_per_counterparty_threshold: usize,
712}
713
714fn default_docs_per_counterparty_threshold() -> usize {
715    20
716}
717
718impl Default for ProcessLayerSettings {
719    fn default() -> Self {
720        Self {
721            include_p2p: true,
722            include_o2c: true,
723            include_s2c: true,
724            include_h2r: true,
725            include_mfg: true,
726            include_bank: true,
727            include_audit: true,
728            include_r2r: true,
729            events_as_hyperedges: true,
730            docs_per_counterparty_threshold: 20,
731        }
732    }
733}
734
735/// Layer 3: Accounting Network layer settings.
736#[derive(Debug, Clone, Serialize, Deserialize)]
737pub struct AccountingLayerSettings {
738    /// Include GL account nodes.
739    #[serde(default = "default_true")]
740    pub include_accounts: bool,
741    /// Export journal entries as hyperedges (debit+credit accounts as participants).
742    #[serde(default = "default_true")]
743    pub je_as_hyperedges: bool,
744}
745
746impl Default for AccountingLayerSettings {
747    fn default() -> Self {
748        Self {
749            include_accounts: true,
750            je_as_hyperedges: true,
751        }
752    }
753}
754
755/// Cross-layer edge generation settings.
756#[derive(Debug, Clone, Serialize, Deserialize)]
757pub struct CrossLayerSettings {
758    /// Generate cross-layer edges (Control→Account, Vendor→PO, etc.).
759    #[serde(default = "default_true")]
760    pub enabled: bool,
761}
762
763impl Default for CrossLayerSettings {
764    fn default() -> Self {
765        Self { enabled: true }
766    }
767}
768
769/// Configuration for a specific graph type to export.
770#[derive(Debug, Clone, Serialize, Deserialize)]
771pub struct GraphTypeConfig {
772    /// Name identifier for this graph configuration.
773    #[serde(default = "default_graph_name")]
774    pub name: String,
775
776    /// Whether to aggregate parallel edges between the same nodes.
777    #[serde(default)]
778    pub aggregate_edges: bool,
779
780    /// Minimum edge weight to include (filters out small transactions).
781    #[serde(default)]
782    pub min_edge_weight: f64,
783
784    /// Whether to include document nodes (creates hub-and-spoke structure).
785    #[serde(default)]
786    pub include_document_nodes: bool,
787}
788
789fn default_graph_name() -> String {
790    "accounting_network".to_string()
791}
792
793impl Default for GraphTypeConfig {
794    fn default() -> Self {
795        Self {
796            name: "accounting_network".to_string(),
797            aggregate_edges: false,
798            min_edge_weight: 0.0,
799            include_document_nodes: false,
800        }
801    }
802}
803
804/// Export format for graph data.
805#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
806#[serde(rename_all = "snake_case")]
807pub enum GraphExportFormat {
808    /// PyTorch Geometric format (.npy files + metadata.json).
809    PytorchGeometric,
810    /// Neo4j format (CSV files + Cypher import scripts).
811    Neo4j,
812    /// Deep Graph Library format.
813    Dgl,
814    /// RustGraph/RustAssureTwin JSON format.
815    RustGraph,
816    /// RustGraph multi-layer hypergraph format (nodes.jsonl + edges.jsonl + hyperedges.jsonl).
817    RustGraphHypergraph,
818}
819
820/// Scenario configuration for metadata, tagging, and ML training setup.
821///
822/// This section enables tracking the purpose and characteristics of a generation run.
823#[derive(Debug, Clone, Default, Serialize, Deserialize)]
824pub struct ScenarioConfig {
825    /// Tags for categorizing and filtering datasets.
826    /// Examples: "fraud_detection", "retail", "month_end_stress", "ml_training"
827    #[serde(default)]
828    pub tags: Vec<String>,
829
830    /// Data quality profile preset.
831    /// - "clean": Minimal data quality issues (0.1% missing, 0.05% typos)
832    /// - "noisy": Moderate issues (5% missing, 2% typos, 1% duplicates)
833    /// - "legacy": Heavy issues simulating legacy system data (10% missing, 5% typos)
834    #[serde(default)]
835    pub profile: Option<String>,
836
837    /// Human-readable description of the scenario purpose.
838    #[serde(default)]
839    pub description: Option<String>,
840
841    /// Whether this run is for ML training (enables balanced labeling).
842    #[serde(default)]
843    pub ml_training: bool,
844
845    /// Target anomaly class balance for ML training.
846    /// If set, anomalies will be injected to achieve this ratio.
847    #[serde(default)]
848    pub target_anomaly_ratio: Option<f64>,
849
850    /// Custom metadata key-value pairs.
851    #[serde(default)]
852    pub metadata: std::collections::HashMap<String, String>,
853}
854
855/// Temporal drift configuration for simulating distribution changes over time.
856///
857/// This enables generation of data that shows realistic temporal evolution,
858/// useful for training drift detection models and testing temporal robustness.
859#[derive(Debug, Clone, Serialize, Deserialize)]
860pub struct TemporalDriftConfig {
861    /// Enable temporal drift simulation.
862    #[serde(default)]
863    pub enabled: bool,
864
865    /// Amount mean drift per period (e.g., 0.02 = 2% mean shift per month).
866    /// Simulates gradual inflation or business growth.
867    #[serde(default = "default_amount_drift")]
868    pub amount_mean_drift: f64,
869
870    /// Amount variance drift per period (e.g., 0.01 = 1% variance increase per month).
871    /// Simulates increasing volatility over time.
872    #[serde(default)]
873    pub amount_variance_drift: f64,
874
875    /// Anomaly rate drift per period (e.g., 0.001 = 0.1% increase per month).
876    /// Simulates increasing fraud attempts or degrading controls.
877    #[serde(default)]
878    pub anomaly_rate_drift: f64,
879
880    /// Concept drift rate - how quickly feature distributions change (0.0-1.0).
881    /// Higher values cause more rapid distribution shifts.
882    #[serde(default = "default_concept_drift")]
883    pub concept_drift_rate: f64,
884
885    /// Sudden drift events - probability of a sudden distribution shift in any period.
886    #[serde(default)]
887    pub sudden_drift_probability: f64,
888
889    /// Magnitude of sudden drift events when they occur (multiplier).
890    #[serde(default = "default_sudden_drift_magnitude")]
891    pub sudden_drift_magnitude: f64,
892
893    /// Seasonal drift - enable cyclic patterns that repeat annually.
894    #[serde(default)]
895    pub seasonal_drift: bool,
896
897    /// Drift start period (0 = from beginning). Use to simulate stable baseline before drift.
898    #[serde(default)]
899    pub drift_start_period: u32,
900
901    /// Drift type: "gradual", "sudden", "recurring", "mixed"
902    #[serde(default = "default_drift_type")]
903    pub drift_type: DriftType,
904}
905
906fn default_amount_drift() -> f64 {
907    0.02
908}
909
910fn default_concept_drift() -> f64 {
911    0.01
912}
913
914fn default_sudden_drift_magnitude() -> f64 {
915    2.0
916}
917
918fn default_drift_type() -> DriftType {
919    DriftType::Gradual
920}
921
922impl Default for TemporalDriftConfig {
923    fn default() -> Self {
924        Self {
925            enabled: false,
926            amount_mean_drift: 0.02,
927            amount_variance_drift: 0.0,
928            anomaly_rate_drift: 0.0,
929            concept_drift_rate: 0.01,
930            sudden_drift_probability: 0.0,
931            sudden_drift_magnitude: 2.0,
932            seasonal_drift: false,
933            drift_start_period: 0,
934            drift_type: DriftType::Gradual,
935        }
936    }
937}
938
939impl TemporalDriftConfig {
940    /// Convert to core DriftConfig for use in generators.
941    pub fn to_core_config(&self) -> datasynth_core::distributions::DriftConfig {
942        datasynth_core::distributions::DriftConfig {
943            enabled: self.enabled,
944            amount_mean_drift: self.amount_mean_drift,
945            amount_variance_drift: self.amount_variance_drift,
946            anomaly_rate_drift: self.anomaly_rate_drift,
947            concept_drift_rate: self.concept_drift_rate,
948            sudden_drift_probability: self.sudden_drift_probability,
949            sudden_drift_magnitude: self.sudden_drift_magnitude,
950            seasonal_drift: self.seasonal_drift,
951            drift_start_period: self.drift_start_period,
952            drift_type: match self.drift_type {
953                DriftType::Gradual => datasynth_core::distributions::DriftType::Gradual,
954                DriftType::Sudden => datasynth_core::distributions::DriftType::Sudden,
955                DriftType::Recurring => datasynth_core::distributions::DriftType::Recurring,
956                DriftType::Mixed => datasynth_core::distributions::DriftType::Mixed,
957            },
958            regime_changes: Vec::new(),
959            economic_cycle: Default::default(),
960            parameter_drifts: Vec::new(),
961        }
962    }
963}
964
965/// Types of temporal drift patterns.
966#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
967#[serde(rename_all = "snake_case")]
968pub enum DriftType {
969    /// Gradual, continuous drift over time (like inflation).
970    #[default]
971    Gradual,
972    /// Sudden, point-in-time shifts (like policy changes).
973    Sudden,
974    /// Recurring patterns that cycle (like seasonal variations).
975    Recurring,
976    /// Combination of gradual background drift with occasional sudden shifts.
977    Mixed,
978}
979
980// ============================================================================
981// Streaming Output API Configuration (Phase 2)
982// ============================================================================
983
984/// Configuration for streaming output API.
985#[derive(Debug, Clone, Serialize, Deserialize)]
986pub struct StreamingSchemaConfig {
987    /// Enable streaming output.
988    #[serde(default)]
989    pub enabled: bool,
990    /// Target events per second (0 = unlimited, default 0).
991    #[serde(default)]
992    pub events_per_second: f64,
993    /// Token bucket burst size (default 100).
994    #[serde(default = "default_burst_size")]
995    pub burst_size: u32,
996    /// Buffer size for streaming (number of items).
997    #[serde(default = "default_buffer_size")]
998    pub buffer_size: usize,
999    /// Enable progress reporting.
1000    #[serde(default = "default_true")]
1001    pub enable_progress: bool,
1002    /// Progress reporting interval (number of items).
1003    #[serde(default = "default_progress_interval")]
1004    pub progress_interval: u64,
1005    /// Backpressure strategy.
1006    #[serde(default)]
1007    pub backpressure: BackpressureSchemaStrategy,
1008}
1009
1010fn default_buffer_size() -> usize {
1011    1000
1012}
1013
1014fn default_progress_interval() -> u64 {
1015    100
1016}
1017
1018impl Default for StreamingSchemaConfig {
1019    fn default() -> Self {
1020        Self {
1021            enabled: false,
1022            events_per_second: 0.0,
1023            burst_size: 100,
1024            buffer_size: 1000,
1025            enable_progress: true,
1026            progress_interval: 100,
1027            backpressure: BackpressureSchemaStrategy::Block,
1028        }
1029    }
1030}
1031
1032/// Backpressure strategy for streaming output.
1033#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
1034#[serde(rename_all = "snake_case")]
1035pub enum BackpressureSchemaStrategy {
1036    /// Block until space is available in the buffer.
1037    #[default]
1038    Block,
1039    /// Drop oldest items when buffer is full.
1040    DropOldest,
1041    /// Drop newest items when buffer is full.
1042    DropNewest,
1043    /// Buffer overflow items up to a limit, then block.
1044    Buffer,
1045}
1046
1047// ============================================================================
1048// Rate Limiting Configuration (Phase 5)
1049// ============================================================================
1050
1051/// Configuration for rate limiting.
1052#[derive(Debug, Clone, Serialize, Deserialize)]
1053pub struct RateLimitSchemaConfig {
1054    /// Enable rate limiting.
1055    #[serde(default)]
1056    pub enabled: bool,
1057    /// Entities per second limit.
1058    #[serde(default = "default_entities_per_second")]
1059    pub entities_per_second: f64,
1060    /// Burst size (number of tokens in bucket).
1061    #[serde(default = "default_burst_size")]
1062    pub burst_size: u32,
1063    /// Backpressure strategy for rate limiting.
1064    #[serde(default)]
1065    pub backpressure: RateLimitBackpressureSchema,
1066}
1067
1068fn default_entities_per_second() -> f64 {
1069    1000.0
1070}
1071
1072fn default_burst_size() -> u32 {
1073    100
1074}
1075
1076impl Default for RateLimitSchemaConfig {
1077    fn default() -> Self {
1078        Self {
1079            enabled: false,
1080            entities_per_second: 1000.0,
1081            burst_size: 100,
1082            backpressure: RateLimitBackpressureSchema::Block,
1083        }
1084    }
1085}
1086
1087/// Backpressure strategy for rate limiting.
1088#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
1089#[serde(rename_all = "snake_case")]
1090pub enum RateLimitBackpressureSchema {
1091    /// Block until rate allows.
1092    #[default]
1093    Block,
1094    /// Drop items that exceed rate.
1095    Drop,
1096    /// Buffer items and process when rate allows.
1097    Buffer,
1098}
1099
1100// ============================================================================
1101// Temporal Attribute Generation Configuration (Phase 3)
1102// ============================================================================
1103
1104/// Configuration for temporal attribute generation.
1105#[derive(Debug, Clone, Serialize, Deserialize)]
1106pub struct TemporalAttributeSchemaConfig {
1107    /// Enable temporal attribute generation.
1108    #[serde(default)]
1109    pub enabled: bool,
1110    /// Valid time configuration.
1111    #[serde(default)]
1112    pub valid_time: ValidTimeSchemaConfig,
1113    /// Transaction time configuration.
1114    #[serde(default)]
1115    pub transaction_time: TransactionTimeSchemaConfig,
1116    /// Generate version chains for entities.
1117    #[serde(default)]
1118    pub generate_version_chains: bool,
1119    /// Average number of versions per entity.
1120    #[serde(default = "default_avg_versions")]
1121    pub avg_versions_per_entity: f64,
1122}
1123
1124fn default_avg_versions() -> f64 {
1125    1.5
1126}
1127
1128impl Default for TemporalAttributeSchemaConfig {
1129    fn default() -> Self {
1130        Self {
1131            enabled: false,
1132            valid_time: ValidTimeSchemaConfig::default(),
1133            transaction_time: TransactionTimeSchemaConfig::default(),
1134            generate_version_chains: false,
1135            avg_versions_per_entity: 1.5,
1136        }
1137    }
1138}
1139
1140/// Configuration for valid time (business time) generation.
1141#[derive(Debug, Clone, Serialize, Deserialize)]
1142pub struct ValidTimeSchemaConfig {
1143    /// Probability that valid_to is set (entity has ended validity).
1144    #[serde(default = "default_closed_probability")]
1145    pub closed_probability: f64,
1146    /// Average validity duration in days.
1147    #[serde(default = "default_avg_validity_days")]
1148    pub avg_validity_days: u32,
1149    /// Standard deviation of validity duration in days.
1150    #[serde(default = "default_validity_stddev")]
1151    pub validity_stddev_days: u32,
1152}
1153
1154fn default_closed_probability() -> f64 {
1155    0.1
1156}
1157
1158fn default_avg_validity_days() -> u32 {
1159    365
1160}
1161
1162fn default_validity_stddev() -> u32 {
1163    90
1164}
1165
1166impl Default for ValidTimeSchemaConfig {
1167    fn default() -> Self {
1168        Self {
1169            closed_probability: 0.1,
1170            avg_validity_days: 365,
1171            validity_stddev_days: 90,
1172        }
1173    }
1174}
1175
1176/// Configuration for transaction time (system time) generation.
1177#[derive(Debug, Clone, Serialize, Deserialize)]
1178pub struct TransactionTimeSchemaConfig {
1179    /// Average recording delay in seconds (0 = immediate).
1180    #[serde(default)]
1181    pub avg_recording_delay_seconds: u32,
1182    /// Allow backdating (recording time before valid time).
1183    #[serde(default)]
1184    pub allow_backdating: bool,
1185    /// Probability of backdating if allowed.
1186    #[serde(default = "default_backdating_probability")]
1187    pub backdating_probability: f64,
1188    /// Maximum backdate days.
1189    #[serde(default = "default_max_backdate_days")]
1190    pub max_backdate_days: u32,
1191}
1192
1193fn default_backdating_probability() -> f64 {
1194    0.01
1195}
1196
1197fn default_max_backdate_days() -> u32 {
1198    30
1199}
1200
1201impl Default for TransactionTimeSchemaConfig {
1202    fn default() -> Self {
1203        Self {
1204            avg_recording_delay_seconds: 0,
1205            allow_backdating: false,
1206            backdating_probability: 0.01,
1207            max_backdate_days: 30,
1208        }
1209    }
1210}
1211
1212// ============================================================================
1213// Relationship Generation Configuration (Phase 4)
1214// ============================================================================
1215
1216/// Configuration for relationship generation.
1217#[derive(Debug, Clone, Serialize, Deserialize)]
1218pub struct RelationshipSchemaConfig {
1219    /// Relationship type definitions.
1220    #[serde(default)]
1221    pub relationship_types: Vec<RelationshipTypeSchemaConfig>,
1222    /// Allow orphan entities (entities with no relationships).
1223    #[serde(default = "default_true")]
1224    pub allow_orphans: bool,
1225    /// Probability of creating an orphan entity.
1226    #[serde(default = "default_orphan_probability")]
1227    pub orphan_probability: f64,
1228    /// Allow circular relationships.
1229    #[serde(default)]
1230    pub allow_circular: bool,
1231    /// Maximum depth for circular relationship detection.
1232    #[serde(default = "default_max_circular_depth")]
1233    pub max_circular_depth: u32,
1234}
1235
1236fn default_orphan_probability() -> f64 {
1237    0.01
1238}
1239
1240fn default_max_circular_depth() -> u32 {
1241    3
1242}
1243
1244impl Default for RelationshipSchemaConfig {
1245    fn default() -> Self {
1246        Self {
1247            relationship_types: Vec::new(),
1248            allow_orphans: true,
1249            orphan_probability: 0.01,
1250            allow_circular: false,
1251            max_circular_depth: 3,
1252        }
1253    }
1254}
1255
1256/// Configuration for a specific relationship type.
1257#[derive(Debug, Clone, Serialize, Deserialize)]
1258pub struct RelationshipTypeSchemaConfig {
1259    /// Name of the relationship type (e.g., "debits", "credits", "created").
1260    pub name: String,
1261    /// Source entity type (e.g., "journal_entry").
1262    pub source_type: String,
1263    /// Target entity type (e.g., "account").
1264    pub target_type: String,
1265    /// Cardinality rule for this relationship.
1266    #[serde(default)]
1267    pub cardinality: CardinalitySchemaRule,
1268    /// Weight for this relationship in random selection.
1269    #[serde(default = "default_relationship_weight")]
1270    pub weight: f64,
1271    /// Whether this relationship is required.
1272    #[serde(default)]
1273    pub required: bool,
1274    /// Whether this relationship is directed.
1275    #[serde(default = "default_true")]
1276    pub directed: bool,
1277}
1278
1279fn default_relationship_weight() -> f64 {
1280    1.0
1281}
1282
1283impl Default for RelationshipTypeSchemaConfig {
1284    fn default() -> Self {
1285        Self {
1286            name: String::new(),
1287            source_type: String::new(),
1288            target_type: String::new(),
1289            cardinality: CardinalitySchemaRule::default(),
1290            weight: 1.0,
1291            required: false,
1292            directed: true,
1293        }
1294    }
1295}
1296
1297/// Cardinality rule for relationships in schema config.
1298#[derive(Debug, Clone, Serialize, Deserialize)]
1299#[serde(rename_all = "snake_case")]
1300pub enum CardinalitySchemaRule {
1301    /// One source to one target.
1302    OneToOne,
1303    /// One source to many targets.
1304    OneToMany {
1305        /// Minimum number of targets.
1306        min: u32,
1307        /// Maximum number of targets.
1308        max: u32,
1309    },
1310    /// Many sources to one target.
1311    ManyToOne {
1312        /// Minimum number of sources.
1313        min: u32,
1314        /// Maximum number of sources.
1315        max: u32,
1316    },
1317    /// Many sources to many targets.
1318    ManyToMany {
1319        /// Minimum targets per source.
1320        min_per_source: u32,
1321        /// Maximum targets per source.
1322        max_per_source: u32,
1323    },
1324}
1325
1326impl Default for CardinalitySchemaRule {
1327    fn default() -> Self {
1328        Self::OneToMany { min: 1, max: 5 }
1329    }
1330}
1331
1332/// Global configuration settings.
1333#[derive(Debug, Clone, Serialize, Deserialize)]
1334pub struct GlobalConfig {
1335    /// Random seed for reproducibility
1336    pub seed: Option<u64>,
1337    /// Industry sector
1338    pub industry: IndustrySector,
1339    /// Simulation start date (YYYY-MM-DD)
1340    pub start_date: String,
1341    /// Simulation period in months
1342    pub period_months: u32,
1343    /// Base currency for group reporting
1344    #[serde(default = "default_currency")]
1345    pub group_currency: String,
1346    /// Presentation currency for consolidated financial statements (ISO 4217).
1347    /// If not set, defaults to `group_currency`.
1348    #[serde(default)]
1349    pub presentation_currency: Option<String>,
1350    /// Enable parallel generation
1351    #[serde(default = "default_true")]
1352    pub parallel: bool,
1353    /// Number of worker threads (0 = auto-detect)
1354    #[serde(default)]
1355    pub worker_threads: usize,
1356    /// Memory limit in MB (0 = unlimited)
1357    #[serde(default)]
1358    pub memory_limit_mb: usize,
1359    /// Fiscal year length in months (defaults to 12 if not set).
1360    /// Used by session-based generation to split the total period into fiscal years.
1361    #[serde(default)]
1362    pub fiscal_year_months: Option<u32>,
1363}
1364
1365fn default_currency() -> String {
1366    "USD".to_string()
1367}
1368fn default_true() -> bool {
1369    true
1370}
1371
1372/// Configuration for generation session behavior.
1373///
1374/// When enabled, the generation pipeline splits the total period into fiscal years
1375/// and generates data period-by-period, carrying forward balance state.
1376#[derive(Debug, Clone, Serialize, Deserialize)]
1377pub struct SessionSchemaConfig {
1378    /// Whether session-based (period-by-period) generation is enabled.
1379    #[serde(default)]
1380    pub enabled: bool,
1381    /// Optional path for saving/loading session checkpoint files.
1382    #[serde(default)]
1383    pub checkpoint_path: Option<String>,
1384    /// Whether to write output files per fiscal period (e.g., `period_01/`).
1385    #[serde(default = "default_true")]
1386    pub per_period_output: bool,
1387    /// Whether to also produce a single consolidated output across all periods.
1388    #[serde(default = "default_true")]
1389    pub consolidated_output: bool,
1390}
1391
1392impl Default for SessionSchemaConfig {
1393    fn default() -> Self {
1394        Self {
1395            enabled: false,
1396            checkpoint_path: None,
1397            per_period_output: true,
1398            consolidated_output: true,
1399        }
1400    }
1401}
1402
1403/// Company code configuration.
1404#[derive(Debug, Clone, Serialize, Deserialize)]
1405pub struct CompanyConfig {
1406    /// Company code identifier
1407    pub code: String,
1408    /// Company name
1409    pub name: String,
1410    /// Local currency (ISO 4217)
1411    pub currency: String,
1412    /// Functional currency for IAS 21 translation (ISO 4217).
1413    /// If not set, defaults to the `currency` field (i.e. local == functional).
1414    #[serde(default)]
1415    pub functional_currency: Option<String>,
1416    /// Country code (ISO 3166-1 alpha-2)
1417    pub country: String,
1418    /// Fiscal year variant
1419    #[serde(default = "default_fiscal_variant")]
1420    pub fiscal_year_variant: String,
1421    /// Transaction volume per year
1422    pub annual_transaction_volume: TransactionVolume,
1423    /// Company-specific transaction weight
1424    #[serde(default = "default_weight")]
1425    pub volume_weight: f64,
1426}
1427
1428fn default_fiscal_variant() -> String {
1429    "K4".to_string()
1430}
1431fn default_weight() -> f64 {
1432    1.0
1433}
1434
1435/// Transaction volume presets.
1436#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
1437#[serde(rename_all = "snake_case")]
1438pub enum TransactionVolume {
1439    /// 10,000 transactions per year
1440    TenK,
1441    /// 100,000 transactions per year
1442    HundredK,
1443    /// 1,000,000 transactions per year
1444    OneM,
1445    /// 10,000,000 transactions per year
1446    TenM,
1447    /// 100,000,000 transactions per year
1448    HundredM,
1449    /// Custom count
1450    Custom(u64),
1451}
1452
1453impl TransactionVolume {
1454    /// Get the transaction count.
1455    pub fn count(&self) -> u64 {
1456        match self {
1457            Self::TenK => 10_000,
1458            Self::HundredK => 100_000,
1459            Self::OneM => 1_000_000,
1460            Self::TenM => 10_000_000,
1461            Self::HundredM => 100_000_000,
1462            Self::Custom(n) => *n,
1463        }
1464    }
1465}
1466
1467/// Chart of Accounts configuration.
1468#[derive(Debug, Clone, Serialize, Deserialize)]
1469pub struct ChartOfAccountsConfig {
1470    /// CoA complexity level
1471    pub complexity: CoAComplexity,
1472    /// Use industry-specific accounts
1473    #[serde(default = "default_true")]
1474    pub industry_specific: bool,
1475    /// Custom account definitions file
1476    pub custom_accounts: Option<PathBuf>,
1477    /// Minimum hierarchy depth
1478    #[serde(default = "default_min_depth")]
1479    pub min_hierarchy_depth: u8,
1480    /// Maximum hierarchy depth
1481    #[serde(default = "default_max_depth")]
1482    pub max_hierarchy_depth: u8,
1483}
1484
1485fn default_min_depth() -> u8 {
1486    2
1487}
1488fn default_max_depth() -> u8 {
1489    5
1490}
1491
1492impl Default for ChartOfAccountsConfig {
1493    fn default() -> Self {
1494        Self {
1495            complexity: CoAComplexity::Small,
1496            industry_specific: true,
1497            custom_accounts: None,
1498            min_hierarchy_depth: default_min_depth(),
1499            max_hierarchy_depth: default_max_depth(),
1500        }
1501    }
1502}
1503
1504/// Transaction generation configuration.
1505#[derive(Debug, Clone, Serialize, Deserialize, Default)]
1506pub struct TransactionConfig {
1507    /// Line item distribution
1508    #[serde(default)]
1509    pub line_item_distribution: LineItemDistributionConfig,
1510    /// Debit/credit balance distribution
1511    #[serde(default)]
1512    pub debit_credit_distribution: DebitCreditDistributionConfig,
1513    /// Even/odd line count distribution
1514    #[serde(default)]
1515    pub even_odd_distribution: EvenOddDistributionConfig,
1516    /// Transaction source distribution
1517    #[serde(default)]
1518    pub source_distribution: SourceDistribution,
1519    /// Seasonality configuration
1520    #[serde(default)]
1521    pub seasonality: SeasonalityConfig,
1522    /// Amount distribution
1523    #[serde(default)]
1524    pub amounts: AmountDistributionConfig,
1525    /// Benford's Law compliance configuration
1526    #[serde(default)]
1527    pub benford: BenfordConfig,
1528}
1529
1530/// Benford's Law compliance configuration.
1531#[derive(Debug, Clone, Serialize, Deserialize)]
1532pub struct BenfordConfig {
1533    /// Enable Benford's Law compliance for amount generation
1534    #[serde(default = "default_true")]
1535    pub enabled: bool,
1536    /// Tolerance for deviation from ideal Benford distribution (0.0-1.0)
1537    #[serde(default = "default_benford_tolerance")]
1538    pub tolerance: f64,
1539    /// Transaction sources exempt from Benford's Law (fixed amounts)
1540    #[serde(default)]
1541    pub exempt_sources: Vec<BenfordExemption>,
1542}
1543
1544fn default_benford_tolerance() -> f64 {
1545    0.05
1546}
1547
1548impl Default for BenfordConfig {
1549    fn default() -> Self {
1550        Self {
1551            enabled: true,
1552            tolerance: default_benford_tolerance(),
1553            exempt_sources: vec![BenfordExemption::Recurring, BenfordExemption::Payroll],
1554        }
1555    }
1556}
1557
1558/// Types of transactions exempt from Benford's Law.
1559#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
1560#[serde(rename_all = "snake_case")]
1561pub enum BenfordExemption {
1562    /// Recurring fixed amounts (rent, subscriptions)
1563    Recurring,
1564    /// Payroll (standardized salaries)
1565    Payroll,
1566    /// Fixed fees and charges
1567    FixedFees,
1568    /// Round number purchases (often legitimate)
1569    RoundAmounts,
1570}
1571
1572/// Distribution of transaction sources.
1573#[derive(Debug, Clone, Serialize, Deserialize)]
1574pub struct SourceDistribution {
1575    /// Manual entries percentage
1576    pub manual: f64,
1577    /// Automated system entries
1578    pub automated: f64,
1579    /// Recurring entries
1580    pub recurring: f64,
1581    /// Adjustment entries
1582    pub adjustment: f64,
1583}
1584
1585impl Default for SourceDistribution {
1586    fn default() -> Self {
1587        Self {
1588            manual: 0.20,
1589            automated: 0.70,
1590            recurring: 0.07,
1591            adjustment: 0.03,
1592        }
1593    }
1594}
1595
1596/// Output configuration.
1597#[derive(Debug, Clone, Serialize, Deserialize)]
1598pub struct OutputConfig {
1599    /// Output mode
1600    #[serde(default)]
1601    pub mode: OutputMode,
1602    /// Output directory
1603    pub output_directory: PathBuf,
1604    /// File formats to generate
1605    #[serde(default = "default_formats")]
1606    pub formats: Vec<FileFormat>,
1607    /// Compression settings
1608    #[serde(default)]
1609    pub compression: CompressionConfig,
1610    /// Batch size for writes
1611    #[serde(default = "default_batch_size")]
1612    pub batch_size: usize,
1613    /// Include ACDOCA format
1614    #[serde(default = "default_true")]
1615    pub include_acdoca: bool,
1616    /// Include BSEG format
1617    #[serde(default)]
1618    pub include_bseg: bool,
1619    /// Partition by fiscal period
1620    #[serde(default = "default_true")]
1621    pub partition_by_period: bool,
1622    /// Partition by company code
1623    #[serde(default)]
1624    pub partition_by_company: bool,
1625    /// Numeric serialization mode for JSON output.
1626    /// "string" (default): decimals as `"1729237.30"` — lossless precision.
1627    /// "native": decimals as `1729237.30` — friendlier for pandas/analytics.
1628    #[serde(default)]
1629    pub numeric_mode: NumericMode,
1630    /// JSON export layout for journal entries and document flows.
1631    /// "nested" (default): `{"header": {...}, "lines": [...]}` — natural ERP structure.
1632    /// "flat": header fields repeated on every line — friendlier for analytics/ML.
1633    ///
1634    /// Accepts both `export_layout` (canonical / YAML) and `exportLayout`
1635    /// (camelCase / SDK JSON) so SDKs that follow camelCase conventions
1636    /// hit the flat path rather than silently getting the Nested default.
1637    /// Before v3.1.1 the missing camelCase alias meant SDK requests with
1638    /// `exportLayout: "flat"` were silently ignored, which SDK operators
1639    /// reported as "flat hangs generation" (the job completed with Nested
1640    /// layout, but manifests didn't match the expected flat shape).
1641    #[serde(default, alias = "exportLayout")]
1642    pub export_layout: ExportLayout,
1643}
1644
1645fn default_formats() -> Vec<FileFormat> {
1646    vec![FileFormat::Parquet]
1647}
1648fn default_batch_size() -> usize {
1649    100_000
1650}
1651
1652impl Default for OutputConfig {
1653    fn default() -> Self {
1654        Self {
1655            mode: OutputMode::FlatFile,
1656            output_directory: PathBuf::from("./output"),
1657            formats: default_formats(),
1658            compression: CompressionConfig::default(),
1659            batch_size: default_batch_size(),
1660            include_acdoca: true,
1661            include_bseg: false,
1662            partition_by_period: true,
1663            partition_by_company: false,
1664            numeric_mode: NumericMode::default(),
1665            export_layout: ExportLayout::default(),
1666        }
1667    }
1668}
1669
1670/// Numeric serialization mode for JSON decimal fields.
1671#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)]
1672#[serde(rename_all = "snake_case")]
1673pub enum NumericMode {
1674    /// Decimals as JSON strings (e.g. `"1729237.30"`). Preserves full precision.
1675    #[default]
1676    String,
1677    /// Decimals as JSON numbers (e.g. `1729237.30`). Friendlier for pandas/analytics.
1678    Native,
1679}
1680
1681/// JSON export layout for nested structures (journal entries, document flows).
1682#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)]
1683#[serde(rename_all = "snake_case")]
1684pub enum ExportLayout {
1685    /// Nested structure: `{"header": {...}, "lines": [...]}`. Natural ERP format.
1686    #[default]
1687    Nested,
1688    /// Flat structure: header fields repeated on every line. Analytics-friendly.
1689    Flat,
1690}
1691
1692/// Output mode.
1693#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize)]
1694#[serde(rename_all = "snake_case")]
1695pub enum OutputMode {
1696    /// Stream records as generated
1697    Streaming,
1698    /// Write to flat files
1699    #[default]
1700    FlatFile,
1701    /// Both streaming and flat file
1702    Both,
1703}
1704
1705/// Supported file formats.
1706#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
1707#[serde(rename_all = "snake_case")]
1708pub enum FileFormat {
1709    Csv,
1710    Parquet,
1711    Json,
1712    JsonLines,
1713}
1714
1715/// Compression configuration.
1716#[derive(Debug, Clone, Serialize, Deserialize)]
1717pub struct CompressionConfig {
1718    /// Enable compression
1719    #[serde(default = "default_true")]
1720    pub enabled: bool,
1721    /// Compression algorithm
1722    #[serde(default)]
1723    pub algorithm: CompressionAlgorithm,
1724    /// Compression level (1-9)
1725    #[serde(default = "default_compression_level")]
1726    pub level: u8,
1727}
1728
1729fn default_compression_level() -> u8 {
1730    3
1731}
1732
1733impl Default for CompressionConfig {
1734    fn default() -> Self {
1735        Self {
1736            enabled: true,
1737            algorithm: CompressionAlgorithm::default(),
1738            level: default_compression_level(),
1739        }
1740    }
1741}
1742
1743/// Compression algorithms.
1744#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize)]
1745#[serde(rename_all = "snake_case")]
1746pub enum CompressionAlgorithm {
1747    Gzip,
1748    #[default]
1749    Zstd,
1750    Lz4,
1751    Snappy,
1752}
1753
1754/// Fraud simulation configuration.
1755///
1756/// ## Document-level vs. line-level fraud
1757///
1758/// `fraud_rate` applies to individual journal-entry lines (line-level).
1759/// `document_fraud_rate` (optional) applies to source documents
1760/// (purchase orders, vendor invoices, customer invoices, payments), and when
1761/// `propagate_to_lines` is true, every JE derived from a fraudulent document
1762/// also gets `is_fraud = true`. This lets users express either:
1763///
1764///  * pure line-level fraud (`document_fraud_rate = None`): legacy behaviour;
1765///  * pure document-level fraud (`fraud_rate ≈ 0` and `document_fraud_rate` set):
1766///    fraud rings expressed at document granularity — realistic for PO/invoice
1767///    fraud schemes where one fraudulent document spawns multiple derived JEs;
1768///  * hybrid (both set): document-level scheme fraud plus unrelated line-level
1769///    slip-ups.
1770///
1771/// `propagate_to_document` does the inverse: when a JE is tagged as fraud by
1772/// the anomaly injector, its source document is also marked fraudulent.
1773#[derive(Debug, Clone, Serialize, Deserialize)]
1774pub struct FraudConfig {
1775    /// Enable fraud scenario generation
1776    #[serde(default)]
1777    pub enabled: bool,
1778    /// Line-level fraud rate: fraction of individual JE lines flagged as fraud (0.0 to 1.0).
1779    ///
1780    /// # Effective line-level prevalence
1781    ///
1782    /// If `document_fraud_rate = Some(d)` and `propagate_to_lines = true`,
1783    /// the observed line-level fraud prevalence is roughly:
1784    ///
1785    ///     P(line is_fraud) ≈ fraud_rate + d × avg_lines_per_fraud_doc / total_lines
1786    ///
1787    /// For a typical retail job (avg 3 lines per document, ~30 % of lines
1788    /// come from doc-flow-derived JEs) the combined rate lands near:
1789    ///
1790    ///     fraud_rate + 0.3 × d
1791    ///
1792    /// so setting `fraud_rate=0.02, document_fraud_rate=0.05, propagate_to_lines=true`
1793    /// produces ~3.5 % line-level fraud, not 2 %. To target a specific
1794    /// line-level prevalence X, choose `fraud_rate = X - 0.3 × d`.
1795    #[serde(default = "default_fraud_rate", alias = "fraudRate")]
1796    pub fraud_rate: f64,
1797    /// Document-level fraud rate: fraction of source documents (PO, vendor
1798    /// invoice, customer invoice, payment) flagged as fraud. `None` disables
1799    /// document-level injection; `Some(r)` marks ~r × document-count as fraud
1800    /// independently of the line-level rate.
1801    #[serde(default, alias = "documentFraudRate")]
1802    pub document_fraud_rate: Option<f64>,
1803    /// When true, flagging a document as fraudulent cascades `is_fraud = true`
1804    /// and `fraud_type` to every journal entry derived from that document,
1805    /// and records `fraud_source_document_id` on the JE header.
1806    /// Default: `true`.
1807    #[serde(default = "default_true", alias = "propagateToLines")]
1808    pub propagate_to_lines: bool,
1809    /// When true, tagging a JE as fraud via line-level anomaly injection also
1810    /// marks the JE's source document as fraudulent (if it can be resolved).
1811    /// Default: `true`.
1812    #[serde(default = "default_true", alias = "propagateToDocument")]
1813    pub propagate_to_document: bool,
1814    /// Fraud type distribution
1815    #[serde(default)]
1816    pub fraud_type_distribution: FraudTypeDistribution,
1817    /// Enable fraud clustering
1818    #[serde(default)]
1819    pub clustering_enabled: bool,
1820    /// Clustering factor
1821    #[serde(default = "default_clustering_factor")]
1822    pub clustering_factor: f64,
1823    /// Approval thresholds for threshold-adjacent fraud pattern
1824    #[serde(default = "default_approval_thresholds")]
1825    pub approval_thresholds: Vec<f64>,
1826}
1827
1828fn default_approval_thresholds() -> Vec<f64> {
1829    vec![1000.0, 5000.0, 10000.0, 25000.0, 50000.0, 100000.0]
1830}
1831
1832fn default_fraud_rate() -> f64 {
1833    0.005
1834}
1835fn default_clustering_factor() -> f64 {
1836    3.0
1837}
1838
1839impl Default for FraudConfig {
1840    fn default() -> Self {
1841        Self {
1842            enabled: false,
1843            fraud_rate: default_fraud_rate(),
1844            document_fraud_rate: None,
1845            propagate_to_lines: true,
1846            propagate_to_document: true,
1847            fraud_type_distribution: FraudTypeDistribution::default(),
1848            clustering_enabled: false,
1849            clustering_factor: default_clustering_factor(),
1850            approval_thresholds: default_approval_thresholds(),
1851        }
1852    }
1853}
1854
1855/// Distribution of fraud types.
1856#[derive(Debug, Clone, Serialize, Deserialize)]
1857pub struct FraudTypeDistribution {
1858    pub suspense_account_abuse: f64,
1859    pub fictitious_transaction: f64,
1860    pub revenue_manipulation: f64,
1861    pub expense_capitalization: f64,
1862    pub split_transaction: f64,
1863    pub timing_anomaly: f64,
1864    pub unauthorized_access: f64,
1865    pub duplicate_payment: f64,
1866}
1867
1868impl Default for FraudTypeDistribution {
1869    fn default() -> Self {
1870        Self {
1871            suspense_account_abuse: 0.25,
1872            fictitious_transaction: 0.15,
1873            revenue_manipulation: 0.10,
1874            expense_capitalization: 0.10,
1875            split_transaction: 0.15,
1876            timing_anomaly: 0.10,
1877            unauthorized_access: 0.10,
1878            duplicate_payment: 0.05,
1879        }
1880    }
1881}
1882
1883/// Internal Controls System (ICS) configuration.
1884#[derive(Debug, Clone, Serialize, Deserialize)]
1885pub struct InternalControlsConfig {
1886    /// Enable internal controls system
1887    #[serde(default)]
1888    pub enabled: bool,
1889    /// Rate at which controls result in exceptions (0.0 - 1.0)
1890    #[serde(default = "default_exception_rate")]
1891    pub exception_rate: f64,
1892    /// Rate at which SoD violations occur (0.0 - 1.0)
1893    #[serde(default = "default_sod_violation_rate")]
1894    pub sod_violation_rate: f64,
1895    /// Export control master data to separate files
1896    #[serde(default = "default_true")]
1897    pub export_control_master_data: bool,
1898    /// SOX materiality threshold for marking transactions as SOX-relevant
1899    #[serde(default = "default_sox_materiality_threshold")]
1900    pub sox_materiality_threshold: f64,
1901    /// Enable COSO 2013 framework integration
1902    #[serde(default = "default_true")]
1903    pub coso_enabled: bool,
1904    /// Include entity-level controls in generation
1905    #[serde(default)]
1906    pub include_entity_level_controls: bool,
1907    /// Target maturity level for controls
1908    /// Valid values: "ad_hoc", "repeatable", "defined", "managed", "optimized", "mixed"
1909    #[serde(default = "default_target_maturity_level")]
1910    pub target_maturity_level: String,
1911}
1912
1913fn default_exception_rate() -> f64 {
1914    0.02
1915}
1916
1917fn default_sod_violation_rate() -> f64 {
1918    0.01
1919}
1920
1921fn default_sox_materiality_threshold() -> f64 {
1922    10000.0
1923}
1924
1925fn default_target_maturity_level() -> String {
1926    "mixed".to_string()
1927}
1928
1929impl Default for InternalControlsConfig {
1930    fn default() -> Self {
1931        Self {
1932            enabled: false,
1933            exception_rate: default_exception_rate(),
1934            sod_violation_rate: default_sod_violation_rate(),
1935            export_control_master_data: true,
1936            sox_materiality_threshold: default_sox_materiality_threshold(),
1937            coso_enabled: true,
1938            include_entity_level_controls: false,
1939            target_maturity_level: default_target_maturity_level(),
1940        }
1941    }
1942}
1943
1944/// Business process configuration.
1945#[derive(Debug, Clone, Serialize, Deserialize)]
1946pub struct BusinessProcessConfig {
1947    /// Order-to-Cash weight
1948    #[serde(default = "default_o2c")]
1949    pub o2c_weight: f64,
1950    /// Procure-to-Pay weight
1951    #[serde(default = "default_p2p")]
1952    pub p2p_weight: f64,
1953    /// Record-to-Report weight
1954    #[serde(default = "default_r2r")]
1955    pub r2r_weight: f64,
1956    /// Hire-to-Retire weight
1957    #[serde(default = "default_h2r")]
1958    pub h2r_weight: f64,
1959    /// Acquire-to-Retire weight
1960    #[serde(default = "default_a2r")]
1961    pub a2r_weight: f64,
1962}
1963
1964fn default_o2c() -> f64 {
1965    0.35
1966}
1967fn default_p2p() -> f64 {
1968    0.30
1969}
1970fn default_r2r() -> f64 {
1971    0.20
1972}
1973fn default_h2r() -> f64 {
1974    0.10
1975}
1976fn default_a2r() -> f64 {
1977    0.05
1978}
1979
1980impl Default for BusinessProcessConfig {
1981    fn default() -> Self {
1982        Self {
1983            o2c_weight: default_o2c(),
1984            p2p_weight: default_p2p(),
1985            r2r_weight: default_r2r(),
1986            h2r_weight: default_h2r(),
1987            a2r_weight: default_a2r(),
1988        }
1989    }
1990}
1991
1992/// User persona configuration.
1993#[derive(Debug, Clone, Serialize, Deserialize, Default)]
1994pub struct UserPersonaConfig {
1995    /// Distribution of user personas
1996    #[serde(default)]
1997    pub persona_distribution: PersonaDistribution,
1998    /// Users per persona type
1999    #[serde(default)]
2000    pub users_per_persona: UsersPerPersona,
2001}
2002
2003/// Distribution of user personas for transaction generation.
2004#[derive(Debug, Clone, Serialize, Deserialize)]
2005pub struct PersonaDistribution {
2006    pub junior_accountant: f64,
2007    pub senior_accountant: f64,
2008    pub controller: f64,
2009    pub manager: f64,
2010    pub automated_system: f64,
2011}
2012
2013impl Default for PersonaDistribution {
2014    fn default() -> Self {
2015        Self {
2016            junior_accountant: 0.15,
2017            senior_accountant: 0.15,
2018            controller: 0.05,
2019            manager: 0.05,
2020            automated_system: 0.60,
2021        }
2022    }
2023}
2024
2025/// Number of users per persona type.
2026#[derive(Debug, Clone, Serialize, Deserialize)]
2027pub struct UsersPerPersona {
2028    pub junior_accountant: usize,
2029    pub senior_accountant: usize,
2030    pub controller: usize,
2031    pub manager: usize,
2032    pub automated_system: usize,
2033}
2034
2035impl Default for UsersPerPersona {
2036    fn default() -> Self {
2037        Self {
2038            junior_accountant: 10,
2039            senior_accountant: 5,
2040            controller: 2,
2041            manager: 3,
2042            automated_system: 20,
2043        }
2044    }
2045}
2046
2047/// Template configuration for realistic data generation.
2048///
2049/// # User-supplied template packs (v3.2.0+)
2050///
2051/// Set `path` to a directory (or single YAML/JSON file) to override or
2052/// extend the embedded default pools for vendor names, customer names,
2053/// material/asset descriptions, audit findings, bank names, and
2054/// department names. When `path` is `None` (the default), generators
2055/// use the compiled-in pools and output is byte-identical to v3.1.2.
2056///
2057/// See `crates/datasynth-core/src/templates/loader.rs::TemplateData`
2058/// for the full YAML schema. Use `datasynth-data templates export` to
2059/// dump the defaults as a starter pack.
2060#[derive(Debug, Clone, Serialize, Deserialize, Default)]
2061pub struct TemplateConfig {
2062    /// Name generation settings
2063    #[serde(default)]
2064    pub names: NameTemplateConfig,
2065    /// Description generation settings
2066    #[serde(default)]
2067    pub descriptions: DescriptionTemplateConfig,
2068    /// Reference number settings
2069    #[serde(default)]
2070    pub references: ReferenceTemplateConfig,
2071    /// Optional path to a user-supplied template file or directory.
2072    /// When set, entries from the file(s) augment or replace the
2073    /// embedded defaults according to `merge_strategy`.
2074    ///
2075    /// `None` (default) = use embedded pools only (byte-identical to v3.1.2).
2076    #[serde(default, alias = "templatesPath")]
2077    pub path: Option<std::path::PathBuf>,
2078    /// How file-based entries combine with embedded defaults.
2079    ///
2080    /// - `extend` (default): append file entries to embedded pools,
2081    ///   de-duplicating. Safe for incremental overlays.
2082    /// - `replace`: discard embedded pools entirely and use only file
2083    ///   entries. Requires a fully-populated template file.
2084    /// - `merge_prefer_file`: replace individual categories when present
2085    ///   in the file; keep embedded for absent categories.
2086    #[serde(default, alias = "mergeStrategy")]
2087    pub merge_strategy: TemplateMergeStrategy,
2088}
2089
2090/// Strategy for combining user-supplied template files with embedded defaults.
2091#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
2092#[serde(rename_all = "snake_case")]
2093pub enum TemplateMergeStrategy {
2094    /// Append file entries to embedded pools (default).
2095    #[default]
2096    Extend,
2097    /// Replace embedded pools entirely with file entries.
2098    Replace,
2099    /// Replace individual categories when present in file; keep embedded for absent ones.
2100    MergePreferFile,
2101}
2102
2103/// Name template configuration.
2104#[derive(Debug, Clone, Serialize, Deserialize)]
2105pub struct NameTemplateConfig {
2106    /// Distribution of name cultures
2107    #[serde(default)]
2108    pub culture_distribution: CultureDistribution,
2109    /// Email domain for generated users
2110    #[serde(default = "default_email_domain")]
2111    pub email_domain: String,
2112    /// Generate realistic display names
2113    #[serde(default = "default_true")]
2114    pub generate_realistic_names: bool,
2115}
2116
2117fn default_email_domain() -> String {
2118    "company.com".to_string()
2119}
2120
2121impl Default for NameTemplateConfig {
2122    fn default() -> Self {
2123        Self {
2124            culture_distribution: CultureDistribution::default(),
2125            email_domain: default_email_domain(),
2126            generate_realistic_names: true,
2127        }
2128    }
2129}
2130
2131/// Distribution of name cultures for generation.
2132#[derive(Debug, Clone, Serialize, Deserialize)]
2133pub struct CultureDistribution {
2134    pub western_us: f64,
2135    pub hispanic: f64,
2136    pub german: f64,
2137    pub french: f64,
2138    pub chinese: f64,
2139    pub japanese: f64,
2140    pub indian: f64,
2141}
2142
2143impl Default for CultureDistribution {
2144    fn default() -> Self {
2145        Self {
2146            western_us: 0.40,
2147            hispanic: 0.20,
2148            german: 0.10,
2149            french: 0.05,
2150            chinese: 0.10,
2151            japanese: 0.05,
2152            indian: 0.10,
2153        }
2154    }
2155}
2156
2157/// Description template configuration.
2158#[derive(Debug, Clone, Serialize, Deserialize)]
2159pub struct DescriptionTemplateConfig {
2160    /// Generate header text for journal entries
2161    #[serde(default = "default_true")]
2162    pub generate_header_text: bool,
2163    /// Generate line text for journal entry lines
2164    #[serde(default = "default_true")]
2165    pub generate_line_text: bool,
2166}
2167
2168impl Default for DescriptionTemplateConfig {
2169    fn default() -> Self {
2170        Self {
2171            generate_header_text: true,
2172            generate_line_text: true,
2173        }
2174    }
2175}
2176
2177/// Reference number template configuration.
2178#[derive(Debug, Clone, Serialize, Deserialize)]
2179pub struct ReferenceTemplateConfig {
2180    /// Generate reference numbers
2181    #[serde(default = "default_true")]
2182    pub generate_references: bool,
2183    /// Invoice prefix
2184    #[serde(default = "default_invoice_prefix")]
2185    pub invoice_prefix: String,
2186    /// Purchase order prefix
2187    #[serde(default = "default_po_prefix")]
2188    pub po_prefix: String,
2189    /// Sales order prefix
2190    #[serde(default = "default_so_prefix")]
2191    pub so_prefix: String,
2192}
2193
2194fn default_invoice_prefix() -> String {
2195    "INV".to_string()
2196}
2197fn default_po_prefix() -> String {
2198    "PO".to_string()
2199}
2200fn default_so_prefix() -> String {
2201    "SO".to_string()
2202}
2203
2204impl Default for ReferenceTemplateConfig {
2205    fn default() -> Self {
2206        Self {
2207            generate_references: true,
2208            invoice_prefix: default_invoice_prefix(),
2209            po_prefix: default_po_prefix(),
2210            so_prefix: default_so_prefix(),
2211        }
2212    }
2213}
2214
2215/// Approval workflow configuration.
2216#[derive(Debug, Clone, Serialize, Deserialize)]
2217pub struct ApprovalConfig {
2218    /// Enable approval workflow generation
2219    #[serde(default)]
2220    pub enabled: bool,
2221    /// Threshold below which transactions are auto-approved
2222    #[serde(default = "default_auto_approve_threshold")]
2223    pub auto_approve_threshold: f64,
2224    /// Rate at which approvals are rejected (0.0 to 1.0)
2225    #[serde(default = "default_rejection_rate")]
2226    pub rejection_rate: f64,
2227    /// Rate at which approvals require revision (0.0 to 1.0)
2228    #[serde(default = "default_revision_rate")]
2229    pub revision_rate: f64,
2230    /// Average delay in hours for approval processing
2231    #[serde(default = "default_approval_delay_hours")]
2232    pub average_approval_delay_hours: f64,
2233    /// Approval chain thresholds
2234    #[serde(default)]
2235    pub thresholds: Vec<ApprovalThresholdConfig>,
2236}
2237
2238fn default_auto_approve_threshold() -> f64 {
2239    1000.0
2240}
2241fn default_rejection_rate() -> f64 {
2242    0.02
2243}
2244fn default_revision_rate() -> f64 {
2245    0.05
2246}
2247fn default_approval_delay_hours() -> f64 {
2248    4.0
2249}
2250
2251impl Default for ApprovalConfig {
2252    fn default() -> Self {
2253        Self {
2254            enabled: false,
2255            auto_approve_threshold: default_auto_approve_threshold(),
2256            rejection_rate: default_rejection_rate(),
2257            revision_rate: default_revision_rate(),
2258            average_approval_delay_hours: default_approval_delay_hours(),
2259            thresholds: vec![
2260                ApprovalThresholdConfig {
2261                    amount: 1000.0,
2262                    level: 1,
2263                    roles: vec!["senior_accountant".to_string()],
2264                },
2265                ApprovalThresholdConfig {
2266                    amount: 10000.0,
2267                    level: 2,
2268                    roles: vec!["senior_accountant".to_string(), "controller".to_string()],
2269                },
2270                ApprovalThresholdConfig {
2271                    amount: 100000.0,
2272                    level: 3,
2273                    roles: vec![
2274                        "senior_accountant".to_string(),
2275                        "controller".to_string(),
2276                        "manager".to_string(),
2277                    ],
2278                },
2279                ApprovalThresholdConfig {
2280                    amount: 500000.0,
2281                    level: 4,
2282                    roles: vec![
2283                        "senior_accountant".to_string(),
2284                        "controller".to_string(),
2285                        "manager".to_string(),
2286                        "executive".to_string(),
2287                    ],
2288                },
2289            ],
2290        }
2291    }
2292}
2293
2294/// Configuration for a single approval threshold.
2295#[derive(Debug, Clone, Serialize, Deserialize)]
2296pub struct ApprovalThresholdConfig {
2297    /// Amount threshold
2298    pub amount: f64,
2299    /// Approval level required
2300    pub level: u8,
2301    /// Roles that can approve at this level
2302    pub roles: Vec<String>,
2303}
2304
2305/// Department configuration.
2306#[derive(Debug, Clone, Serialize, Deserialize)]
2307pub struct DepartmentConfig {
2308    /// Enable department assignment
2309    #[serde(default)]
2310    pub enabled: bool,
2311    /// Multiplier for department headcounts
2312    #[serde(default = "default_headcount_multiplier")]
2313    pub headcount_multiplier: f64,
2314    /// Custom department definitions (optional)
2315    #[serde(default)]
2316    pub custom_departments: Vec<CustomDepartmentConfig>,
2317}
2318
2319fn default_headcount_multiplier() -> f64 {
2320    1.0
2321}
2322
2323impl Default for DepartmentConfig {
2324    fn default() -> Self {
2325        Self {
2326            enabled: false,
2327            headcount_multiplier: default_headcount_multiplier(),
2328            custom_departments: Vec::new(),
2329        }
2330    }
2331}
2332
2333/// Custom department definition.
2334#[derive(Debug, Clone, Serialize, Deserialize)]
2335pub struct CustomDepartmentConfig {
2336    /// Department code
2337    pub code: String,
2338    /// Department name
2339    pub name: String,
2340    /// Associated cost center
2341    #[serde(default)]
2342    pub cost_center: Option<String>,
2343    /// Primary business processes
2344    #[serde(default)]
2345    pub primary_processes: Vec<String>,
2346    /// Parent department code
2347    #[serde(default)]
2348    pub parent_code: Option<String>,
2349}
2350
2351// ============================================================================
2352// Master Data Configuration
2353// ============================================================================
2354
2355/// Master data generation configuration.
2356#[derive(Debug, Clone, Default, Serialize, Deserialize)]
2357pub struct MasterDataConfig {
2358    /// Vendor master data settings
2359    #[serde(default)]
2360    pub vendors: VendorMasterConfig,
2361    /// Customer master data settings
2362    #[serde(default)]
2363    pub customers: CustomerMasterConfig,
2364    /// Material master data settings
2365    #[serde(default)]
2366    pub materials: MaterialMasterConfig,
2367    /// Fixed asset master data settings
2368    #[serde(default)]
2369    pub fixed_assets: FixedAssetMasterConfig,
2370    /// Employee master data settings
2371    #[serde(default)]
2372    pub employees: EmployeeMasterConfig,
2373    /// Cost center master data settings
2374    #[serde(default)]
2375    pub cost_centers: CostCenterMasterConfig,
2376}
2377
2378/// Vendor master data configuration.
2379#[derive(Debug, Clone, Serialize, Deserialize)]
2380pub struct VendorMasterConfig {
2381    /// Number of vendors to generate
2382    #[serde(default = "default_vendor_count")]
2383    pub count: usize,
2384    /// Percentage of vendors that are intercompany (0.0 to 1.0)
2385    #[serde(default = "default_intercompany_percent")]
2386    pub intercompany_percent: f64,
2387    /// Payment terms distribution
2388    #[serde(default)]
2389    pub payment_terms_distribution: PaymentTermsDistribution,
2390    /// Vendor behavior distribution
2391    #[serde(default)]
2392    pub behavior_distribution: VendorBehaviorDistribution,
2393    /// Generate bank account details
2394    #[serde(default = "default_true")]
2395    pub generate_bank_accounts: bool,
2396    /// Generate tax IDs
2397    #[serde(default = "default_true")]
2398    pub generate_tax_ids: bool,
2399}
2400
2401fn default_vendor_count() -> usize {
2402    500
2403}
2404
2405fn default_intercompany_percent() -> f64 {
2406    0.05
2407}
2408
2409impl Default for VendorMasterConfig {
2410    fn default() -> Self {
2411        Self {
2412            count: default_vendor_count(),
2413            intercompany_percent: default_intercompany_percent(),
2414            payment_terms_distribution: PaymentTermsDistribution::default(),
2415            behavior_distribution: VendorBehaviorDistribution::default(),
2416            generate_bank_accounts: true,
2417            generate_tax_ids: true,
2418        }
2419    }
2420}
2421
2422/// Payment terms distribution for vendors.
2423#[derive(Debug, Clone, Serialize, Deserialize)]
2424pub struct PaymentTermsDistribution {
2425    /// Net 30 days
2426    pub net_30: f64,
2427    /// Net 60 days
2428    pub net_60: f64,
2429    /// Net 90 days
2430    pub net_90: f64,
2431    /// 2% 10 Net 30 (early payment discount)
2432    pub two_ten_net_30: f64,
2433    /// Due on receipt
2434    pub due_on_receipt: f64,
2435    /// End of month
2436    pub end_of_month: f64,
2437}
2438
2439impl Default for PaymentTermsDistribution {
2440    fn default() -> Self {
2441        Self {
2442            net_30: 0.40,
2443            net_60: 0.20,
2444            net_90: 0.10,
2445            two_ten_net_30: 0.15,
2446            due_on_receipt: 0.05,
2447            end_of_month: 0.10,
2448        }
2449    }
2450}
2451
2452/// Vendor behavior distribution.
2453#[derive(Debug, Clone, Serialize, Deserialize)]
2454pub struct VendorBehaviorDistribution {
2455    /// Reliable vendors (consistent delivery, quality)
2456    pub reliable: f64,
2457    /// Sometimes late vendors
2458    pub sometimes_late: f64,
2459    /// Inconsistent quality vendors
2460    pub inconsistent_quality: f64,
2461    /// Premium vendors (high quality, premium pricing)
2462    pub premium: f64,
2463    /// Budget vendors (lower quality, lower pricing)
2464    pub budget: f64,
2465}
2466
2467impl Default for VendorBehaviorDistribution {
2468    fn default() -> Self {
2469        Self {
2470            reliable: 0.50,
2471            sometimes_late: 0.20,
2472            inconsistent_quality: 0.10,
2473            premium: 0.10,
2474            budget: 0.10,
2475        }
2476    }
2477}
2478
2479/// Customer master data configuration.
2480#[derive(Debug, Clone, Serialize, Deserialize)]
2481pub struct CustomerMasterConfig {
2482    /// Number of customers to generate
2483    #[serde(default = "default_customer_count")]
2484    pub count: usize,
2485    /// Percentage of customers that are intercompany (0.0 to 1.0)
2486    #[serde(default = "default_intercompany_percent")]
2487    pub intercompany_percent: f64,
2488    /// Credit rating distribution
2489    #[serde(default)]
2490    pub credit_rating_distribution: CreditRatingDistribution,
2491    /// Payment behavior distribution
2492    #[serde(default)]
2493    pub payment_behavior_distribution: PaymentBehaviorDistribution,
2494    /// Generate credit limits based on rating
2495    #[serde(default = "default_true")]
2496    pub generate_credit_limits: bool,
2497}
2498
2499fn default_customer_count() -> usize {
2500    2000
2501}
2502
2503impl Default for CustomerMasterConfig {
2504    fn default() -> Self {
2505        Self {
2506            count: default_customer_count(),
2507            intercompany_percent: default_intercompany_percent(),
2508            credit_rating_distribution: CreditRatingDistribution::default(),
2509            payment_behavior_distribution: PaymentBehaviorDistribution::default(),
2510            generate_credit_limits: true,
2511        }
2512    }
2513}
2514
2515/// Credit rating distribution for customers.
2516#[derive(Debug, Clone, Serialize, Deserialize)]
2517pub struct CreditRatingDistribution {
2518    /// AAA rating
2519    pub aaa: f64,
2520    /// AA rating
2521    pub aa: f64,
2522    /// A rating
2523    pub a: f64,
2524    /// BBB rating
2525    pub bbb: f64,
2526    /// BB rating
2527    pub bb: f64,
2528    /// B rating
2529    pub b: f64,
2530    /// Below B rating
2531    pub below_b: f64,
2532}
2533
2534impl Default for CreditRatingDistribution {
2535    fn default() -> Self {
2536        Self {
2537            aaa: 0.05,
2538            aa: 0.10,
2539            a: 0.20,
2540            bbb: 0.30,
2541            bb: 0.20,
2542            b: 0.10,
2543            below_b: 0.05,
2544        }
2545    }
2546}
2547
2548/// Payment behavior distribution for customers.
2549#[derive(Debug, Clone, Serialize, Deserialize)]
2550pub struct PaymentBehaviorDistribution {
2551    /// Always pays early
2552    pub early_payer: f64,
2553    /// Pays on time
2554    pub on_time: f64,
2555    /// Occasionally late
2556    pub occasional_late: f64,
2557    /// Frequently late
2558    pub frequent_late: f64,
2559    /// Takes early payment discounts
2560    pub discount_taker: f64,
2561}
2562
2563impl Default for PaymentBehaviorDistribution {
2564    fn default() -> Self {
2565        Self {
2566            early_payer: 0.10,
2567            on_time: 0.50,
2568            occasional_late: 0.25,
2569            frequent_late: 0.10,
2570            discount_taker: 0.05,
2571        }
2572    }
2573}
2574
2575/// Material master data configuration.
2576#[derive(Debug, Clone, Serialize, Deserialize)]
2577pub struct MaterialMasterConfig {
2578    /// Number of materials to generate
2579    #[serde(default = "default_material_count")]
2580    pub count: usize,
2581    /// Material type distribution
2582    #[serde(default)]
2583    pub type_distribution: MaterialTypeDistribution,
2584    /// Valuation method distribution
2585    #[serde(default)]
2586    pub valuation_distribution: ValuationMethodDistribution,
2587    /// Percentage of materials with BOM (bill of materials)
2588    #[serde(default = "default_bom_percent")]
2589    pub bom_percent: f64,
2590    /// Maximum BOM depth
2591    #[serde(default = "default_max_bom_depth")]
2592    pub max_bom_depth: u8,
2593}
2594
2595fn default_material_count() -> usize {
2596    5000
2597}
2598
2599fn default_bom_percent() -> f64 {
2600    0.20
2601}
2602
2603fn default_max_bom_depth() -> u8 {
2604    3
2605}
2606
2607impl Default for MaterialMasterConfig {
2608    fn default() -> Self {
2609        Self {
2610            count: default_material_count(),
2611            type_distribution: MaterialTypeDistribution::default(),
2612            valuation_distribution: ValuationMethodDistribution::default(),
2613            bom_percent: default_bom_percent(),
2614            max_bom_depth: default_max_bom_depth(),
2615        }
2616    }
2617}
2618
2619/// Material type distribution.
2620#[derive(Debug, Clone, Serialize, Deserialize)]
2621pub struct MaterialTypeDistribution {
2622    /// Raw materials
2623    pub raw_material: f64,
2624    /// Semi-finished goods
2625    pub semi_finished: f64,
2626    /// Finished goods
2627    pub finished_good: f64,
2628    /// Trading goods (purchased for resale)
2629    pub trading_good: f64,
2630    /// Operating supplies
2631    pub operating_supply: f64,
2632    /// Services
2633    pub service: f64,
2634}
2635
2636impl Default for MaterialTypeDistribution {
2637    fn default() -> Self {
2638        Self {
2639            raw_material: 0.30,
2640            semi_finished: 0.15,
2641            finished_good: 0.25,
2642            trading_good: 0.15,
2643            operating_supply: 0.10,
2644            service: 0.05,
2645        }
2646    }
2647}
2648
2649/// Valuation method distribution for materials.
2650#[derive(Debug, Clone, Serialize, Deserialize)]
2651pub struct ValuationMethodDistribution {
2652    /// Standard cost
2653    pub standard_cost: f64,
2654    /// Moving average
2655    pub moving_average: f64,
2656    /// FIFO (First In, First Out)
2657    pub fifo: f64,
2658    /// LIFO (Last In, First Out)
2659    pub lifo: f64,
2660}
2661
2662impl Default for ValuationMethodDistribution {
2663    fn default() -> Self {
2664        Self {
2665            standard_cost: 0.50,
2666            moving_average: 0.30,
2667            fifo: 0.15,
2668            lifo: 0.05,
2669        }
2670    }
2671}
2672
2673/// Fixed asset master data configuration.
2674#[derive(Debug, Clone, Serialize, Deserialize)]
2675pub struct FixedAssetMasterConfig {
2676    /// Number of fixed assets to generate
2677    #[serde(default = "default_asset_count")]
2678    pub count: usize,
2679    /// Asset class distribution
2680    #[serde(default)]
2681    pub class_distribution: AssetClassDistribution,
2682    /// Depreciation method distribution
2683    #[serde(default)]
2684    pub depreciation_distribution: DepreciationMethodDistribution,
2685    /// Percentage of assets that are fully depreciated
2686    #[serde(default = "default_fully_depreciated_percent")]
2687    pub fully_depreciated_percent: f64,
2688    /// Generate acquisition history
2689    #[serde(default = "default_true")]
2690    pub generate_acquisition_history: bool,
2691}
2692
2693fn default_asset_count() -> usize {
2694    800
2695}
2696
2697fn default_fully_depreciated_percent() -> f64 {
2698    0.15
2699}
2700
2701impl Default for FixedAssetMasterConfig {
2702    fn default() -> Self {
2703        Self {
2704            count: default_asset_count(),
2705            class_distribution: AssetClassDistribution::default(),
2706            depreciation_distribution: DepreciationMethodDistribution::default(),
2707            fully_depreciated_percent: default_fully_depreciated_percent(),
2708            generate_acquisition_history: true,
2709        }
2710    }
2711}
2712
2713/// Asset class distribution.
2714#[derive(Debug, Clone, Serialize, Deserialize)]
2715pub struct AssetClassDistribution {
2716    /// Buildings and structures
2717    pub buildings: f64,
2718    /// Machinery and equipment
2719    pub machinery: f64,
2720    /// Vehicles
2721    pub vehicles: f64,
2722    /// IT equipment
2723    pub it_equipment: f64,
2724    /// Furniture and fixtures
2725    pub furniture: f64,
2726    /// Land (non-depreciable)
2727    pub land: f64,
2728    /// Leasehold improvements
2729    pub leasehold: f64,
2730}
2731
2732impl Default for AssetClassDistribution {
2733    fn default() -> Self {
2734        Self {
2735            buildings: 0.15,
2736            machinery: 0.30,
2737            vehicles: 0.15,
2738            it_equipment: 0.20,
2739            furniture: 0.10,
2740            land: 0.05,
2741            leasehold: 0.05,
2742        }
2743    }
2744}
2745
2746/// Depreciation method distribution.
2747#[derive(Debug, Clone, Serialize, Deserialize)]
2748pub struct DepreciationMethodDistribution {
2749    /// Straight line
2750    pub straight_line: f64,
2751    /// Declining balance
2752    pub declining_balance: f64,
2753    /// Double declining balance
2754    pub double_declining: f64,
2755    /// Sum of years' digits
2756    pub sum_of_years: f64,
2757    /// Units of production
2758    pub units_of_production: f64,
2759}
2760
2761impl Default for DepreciationMethodDistribution {
2762    fn default() -> Self {
2763        Self {
2764            straight_line: 0.60,
2765            declining_balance: 0.20,
2766            double_declining: 0.10,
2767            sum_of_years: 0.05,
2768            units_of_production: 0.05,
2769        }
2770    }
2771}
2772
2773/// Employee master data configuration.
2774#[derive(Debug, Clone, Serialize, Deserialize)]
2775pub struct EmployeeMasterConfig {
2776    /// Number of employees to generate
2777    #[serde(default = "default_employee_count")]
2778    pub count: usize,
2779    /// Generate organizational hierarchy
2780    #[serde(default = "default_true")]
2781    pub generate_hierarchy: bool,
2782    /// Maximum hierarchy depth
2783    #[serde(default = "default_hierarchy_depth")]
2784    pub max_hierarchy_depth: u8,
2785    /// Average span of control (direct reports per manager)
2786    #[serde(default = "default_span_of_control")]
2787    pub average_span_of_control: f64,
2788    /// Approval limit distribution by job level
2789    #[serde(default)]
2790    pub approval_limits: ApprovalLimitDistribution,
2791    /// Department distribution
2792    #[serde(default)]
2793    pub department_distribution: EmployeeDepartmentDistribution,
2794}
2795
2796fn default_employee_count() -> usize {
2797    1500
2798}
2799
2800fn default_hierarchy_depth() -> u8 {
2801    6
2802}
2803
2804fn default_span_of_control() -> f64 {
2805    5.0
2806}
2807
2808impl Default for EmployeeMasterConfig {
2809    fn default() -> Self {
2810        Self {
2811            count: default_employee_count(),
2812            generate_hierarchy: true,
2813            max_hierarchy_depth: default_hierarchy_depth(),
2814            average_span_of_control: default_span_of_control(),
2815            approval_limits: ApprovalLimitDistribution::default(),
2816            department_distribution: EmployeeDepartmentDistribution::default(),
2817        }
2818    }
2819}
2820
2821/// Approval limit distribution by job level.
2822#[derive(Debug, Clone, Serialize, Deserialize)]
2823pub struct ApprovalLimitDistribution {
2824    /// Staff level approval limit
2825    #[serde(default = "default_staff_limit")]
2826    pub staff: f64,
2827    /// Senior staff approval limit
2828    #[serde(default = "default_senior_limit")]
2829    pub senior: f64,
2830    /// Manager approval limit
2831    #[serde(default = "default_manager_limit")]
2832    pub manager: f64,
2833    /// Director approval limit
2834    #[serde(default = "default_director_limit")]
2835    pub director: f64,
2836    /// VP approval limit
2837    #[serde(default = "default_vp_limit")]
2838    pub vp: f64,
2839    /// Executive approval limit
2840    #[serde(default = "default_executive_limit")]
2841    pub executive: f64,
2842}
2843
2844fn default_staff_limit() -> f64 {
2845    1000.0
2846}
2847fn default_senior_limit() -> f64 {
2848    5000.0
2849}
2850fn default_manager_limit() -> f64 {
2851    25000.0
2852}
2853fn default_director_limit() -> f64 {
2854    100000.0
2855}
2856fn default_vp_limit() -> f64 {
2857    500000.0
2858}
2859fn default_executive_limit() -> f64 {
2860    f64::INFINITY
2861}
2862
2863impl Default for ApprovalLimitDistribution {
2864    fn default() -> Self {
2865        Self {
2866            staff: default_staff_limit(),
2867            senior: default_senior_limit(),
2868            manager: default_manager_limit(),
2869            director: default_director_limit(),
2870            vp: default_vp_limit(),
2871            executive: default_executive_limit(),
2872        }
2873    }
2874}
2875
2876/// Employee distribution across departments.
2877#[derive(Debug, Clone, Serialize, Deserialize)]
2878pub struct EmployeeDepartmentDistribution {
2879    /// Finance and Accounting
2880    pub finance: f64,
2881    /// Procurement
2882    pub procurement: f64,
2883    /// Sales
2884    pub sales: f64,
2885    /// Warehouse and Logistics
2886    pub warehouse: f64,
2887    /// IT
2888    pub it: f64,
2889    /// Human Resources
2890    pub hr: f64,
2891    /// Operations
2892    pub operations: f64,
2893    /// Executive
2894    pub executive: f64,
2895}
2896
2897impl Default for EmployeeDepartmentDistribution {
2898    fn default() -> Self {
2899        Self {
2900            finance: 0.12,
2901            procurement: 0.10,
2902            sales: 0.25,
2903            warehouse: 0.15,
2904            it: 0.10,
2905            hr: 0.05,
2906            operations: 0.20,
2907            executive: 0.03,
2908        }
2909    }
2910}
2911
2912/// Cost center master data configuration.
2913#[derive(Debug, Clone, Serialize, Deserialize)]
2914pub struct CostCenterMasterConfig {
2915    /// Number of cost centers to generate
2916    #[serde(default = "default_cost_center_count")]
2917    pub count: usize,
2918    /// Generate cost center hierarchy
2919    #[serde(default = "default_true")]
2920    pub generate_hierarchy: bool,
2921    /// Maximum hierarchy depth
2922    #[serde(default = "default_cc_hierarchy_depth")]
2923    pub max_hierarchy_depth: u8,
2924}
2925
2926fn default_cost_center_count() -> usize {
2927    50
2928}
2929
2930fn default_cc_hierarchy_depth() -> u8 {
2931    3
2932}
2933
2934impl Default for CostCenterMasterConfig {
2935    fn default() -> Self {
2936        Self {
2937            count: default_cost_center_count(),
2938            generate_hierarchy: true,
2939            max_hierarchy_depth: default_cc_hierarchy_depth(),
2940        }
2941    }
2942}
2943
2944// ============================================================================
2945// Document Flow Configuration
2946// ============================================================================
2947
2948/// Document flow generation configuration.
2949#[derive(Debug, Clone, Serialize, Deserialize)]
2950pub struct DocumentFlowConfig {
2951    /// P2P (Procure-to-Pay) flow configuration
2952    #[serde(default)]
2953    pub p2p: P2PFlowConfig,
2954    /// O2C (Order-to-Cash) flow configuration
2955    #[serde(default)]
2956    pub o2c: O2CFlowConfig,
2957    /// Generate document reference chains
2958    #[serde(default = "default_true")]
2959    pub generate_document_references: bool,
2960    /// Export document flow graph
2961    #[serde(default)]
2962    pub export_flow_graph: bool,
2963}
2964
2965impl Default for DocumentFlowConfig {
2966    fn default() -> Self {
2967        Self {
2968            p2p: P2PFlowConfig::default(),
2969            o2c: O2CFlowConfig::default(),
2970            generate_document_references: true,
2971            export_flow_graph: false,
2972        }
2973    }
2974}
2975
2976/// P2P (Procure-to-Pay) flow configuration.
2977#[derive(Debug, Clone, Serialize, Deserialize)]
2978pub struct P2PFlowConfig {
2979    /// Enable P2P document flow generation
2980    #[serde(default = "default_true")]
2981    pub enabled: bool,
2982    /// Three-way match success rate (PO-GR-Invoice)
2983    #[serde(default = "default_three_way_match_rate")]
2984    pub three_way_match_rate: f64,
2985    /// Rate of partial deliveries
2986    #[serde(default = "default_partial_delivery_rate")]
2987    pub partial_delivery_rate: f64,
2988    /// Rate of price variances between PO and Invoice
2989    #[serde(default = "default_price_variance_rate")]
2990    pub price_variance_rate: f64,
2991    /// Maximum price variance percentage
2992    #[serde(default = "default_max_price_variance")]
2993    pub max_price_variance_percent: f64,
2994    /// Rate of quantity variances between PO/GR and Invoice
2995    #[serde(default = "default_quantity_variance_rate")]
2996    pub quantity_variance_rate: f64,
2997    /// Average days from PO to goods receipt
2998    #[serde(default = "default_po_to_gr_days")]
2999    pub average_po_to_gr_days: u32,
3000    /// Average days from GR to invoice
3001    #[serde(default = "default_gr_to_invoice_days")]
3002    pub average_gr_to_invoice_days: u32,
3003    /// Average days from invoice to payment
3004    #[serde(default = "default_invoice_to_payment_days")]
3005    pub average_invoice_to_payment_days: u32,
3006    /// PO line count distribution
3007    #[serde(default)]
3008    pub line_count_distribution: DocumentLineCountDistribution,
3009    /// Payment behavior configuration
3010    #[serde(default)]
3011    pub payment_behavior: P2PPaymentBehaviorConfig,
3012    /// Rate of over-deliveries (quantity received exceeds PO quantity)
3013    #[serde(default)]
3014    pub over_delivery_rate: Option<f64>,
3015    /// Rate of early payment discounts being taken
3016    #[serde(default)]
3017    pub early_payment_discount_rate: Option<f64>,
3018}
3019
3020fn default_three_way_match_rate() -> f64 {
3021    0.95
3022}
3023
3024fn default_partial_delivery_rate() -> f64 {
3025    0.15
3026}
3027
3028fn default_price_variance_rate() -> f64 {
3029    0.08
3030}
3031
3032fn default_max_price_variance() -> f64 {
3033    0.05
3034}
3035
3036fn default_quantity_variance_rate() -> f64 {
3037    0.05
3038}
3039
3040fn default_po_to_gr_days() -> u32 {
3041    14
3042}
3043
3044fn default_gr_to_invoice_days() -> u32 {
3045    5
3046}
3047
3048fn default_invoice_to_payment_days() -> u32 {
3049    30
3050}
3051
3052impl Default for P2PFlowConfig {
3053    fn default() -> Self {
3054        Self {
3055            enabled: true,
3056            three_way_match_rate: default_three_way_match_rate(),
3057            partial_delivery_rate: default_partial_delivery_rate(),
3058            price_variance_rate: default_price_variance_rate(),
3059            max_price_variance_percent: default_max_price_variance(),
3060            quantity_variance_rate: default_quantity_variance_rate(),
3061            average_po_to_gr_days: default_po_to_gr_days(),
3062            average_gr_to_invoice_days: default_gr_to_invoice_days(),
3063            average_invoice_to_payment_days: default_invoice_to_payment_days(),
3064            line_count_distribution: DocumentLineCountDistribution::default(),
3065            payment_behavior: P2PPaymentBehaviorConfig::default(),
3066            over_delivery_rate: None,
3067            early_payment_discount_rate: None,
3068        }
3069    }
3070}
3071
3072// ============================================================================
3073// P2P Payment Behavior Configuration
3074// ============================================================================
3075
3076/// P2P payment behavior configuration.
3077#[derive(Debug, Clone, Serialize, Deserialize)]
3078pub struct P2PPaymentBehaviorConfig {
3079    /// Rate of late payments (beyond due date)
3080    #[serde(default = "default_p2p_late_payment_rate")]
3081    pub late_payment_rate: f64,
3082    /// Distribution of late payment days
3083    #[serde(default)]
3084    pub late_payment_days_distribution: LatePaymentDaysDistribution,
3085    /// Rate of partial payments
3086    #[serde(default = "default_p2p_partial_payment_rate")]
3087    pub partial_payment_rate: f64,
3088    /// Rate of payment corrections (NSF, chargebacks, reversals)
3089    #[serde(default = "default_p2p_payment_correction_rate")]
3090    pub payment_correction_rate: f64,
3091    /// Average days until partial payment remainder is paid
3092    #[serde(default = "default_p2p_avg_days_until_remainder")]
3093    pub avg_days_until_remainder: u32,
3094}
3095
3096fn default_p2p_late_payment_rate() -> f64 {
3097    0.15
3098}
3099
3100fn default_p2p_partial_payment_rate() -> f64 {
3101    0.05
3102}
3103
3104fn default_p2p_payment_correction_rate() -> f64 {
3105    0.02
3106}
3107
3108fn default_p2p_avg_days_until_remainder() -> u32 {
3109    30
3110}
3111
3112impl Default for P2PPaymentBehaviorConfig {
3113    fn default() -> Self {
3114        Self {
3115            late_payment_rate: default_p2p_late_payment_rate(),
3116            late_payment_days_distribution: LatePaymentDaysDistribution::default(),
3117            partial_payment_rate: default_p2p_partial_payment_rate(),
3118            payment_correction_rate: default_p2p_payment_correction_rate(),
3119            avg_days_until_remainder: default_p2p_avg_days_until_remainder(),
3120        }
3121    }
3122}
3123
3124/// Distribution of late payment days for P2P.
3125#[derive(Debug, Clone, Serialize, Deserialize)]
3126pub struct LatePaymentDaysDistribution {
3127    /// 1-7 days late (slightly late)
3128    #[serde(default = "default_slightly_late")]
3129    pub slightly_late_1_to_7: f64,
3130    /// 8-14 days late
3131    #[serde(default = "default_late_8_14")]
3132    pub late_8_to_14: f64,
3133    /// 15-30 days late (very late)
3134    #[serde(default = "default_very_late")]
3135    pub very_late_15_to_30: f64,
3136    /// 31-60 days late (severely late)
3137    #[serde(default = "default_severely_late")]
3138    pub severely_late_31_to_60: f64,
3139    /// Over 60 days late (extremely late)
3140    #[serde(default = "default_extremely_late")]
3141    pub extremely_late_over_60: f64,
3142}
3143
3144fn default_slightly_late() -> f64 {
3145    0.50
3146}
3147
3148fn default_late_8_14() -> f64 {
3149    0.25
3150}
3151
3152fn default_very_late() -> f64 {
3153    0.15
3154}
3155
3156fn default_severely_late() -> f64 {
3157    0.07
3158}
3159
3160fn default_extremely_late() -> f64 {
3161    0.03
3162}
3163
3164impl Default for LatePaymentDaysDistribution {
3165    fn default() -> Self {
3166        Self {
3167            slightly_late_1_to_7: default_slightly_late(),
3168            late_8_to_14: default_late_8_14(),
3169            very_late_15_to_30: default_very_late(),
3170            severely_late_31_to_60: default_severely_late(),
3171            extremely_late_over_60: default_extremely_late(),
3172        }
3173    }
3174}
3175
3176/// O2C (Order-to-Cash) flow configuration.
3177#[derive(Debug, Clone, Serialize, Deserialize)]
3178pub struct O2CFlowConfig {
3179    /// Enable O2C document flow generation
3180    #[serde(default = "default_true")]
3181    pub enabled: bool,
3182    /// Credit check failure rate
3183    #[serde(default = "default_credit_check_failure_rate")]
3184    pub credit_check_failure_rate: f64,
3185    /// Rate of partial shipments
3186    #[serde(default = "default_partial_shipment_rate")]
3187    pub partial_shipment_rate: f64,
3188    /// Rate of returns
3189    #[serde(default = "default_return_rate")]
3190    pub return_rate: f64,
3191    /// Bad debt write-off rate
3192    #[serde(default = "default_bad_debt_rate")]
3193    pub bad_debt_rate: f64,
3194    /// Average days from SO to delivery
3195    #[serde(default = "default_so_to_delivery_days")]
3196    pub average_so_to_delivery_days: u32,
3197    /// Average days from delivery to invoice
3198    #[serde(default = "default_delivery_to_invoice_days")]
3199    pub average_delivery_to_invoice_days: u32,
3200    /// Average days from invoice to receipt
3201    #[serde(default = "default_invoice_to_receipt_days")]
3202    pub average_invoice_to_receipt_days: u32,
3203    /// SO line count distribution
3204    #[serde(default)]
3205    pub line_count_distribution: DocumentLineCountDistribution,
3206    /// Cash discount configuration
3207    #[serde(default)]
3208    pub cash_discount: CashDiscountConfig,
3209    /// Payment behavior configuration
3210    #[serde(default)]
3211    pub payment_behavior: O2CPaymentBehaviorConfig,
3212    /// Rate of late payments
3213    #[serde(default)]
3214    pub late_payment_rate: Option<f64>,
3215}
3216
3217fn default_credit_check_failure_rate() -> f64 {
3218    0.02
3219}
3220
3221fn default_partial_shipment_rate() -> f64 {
3222    0.10
3223}
3224
3225fn default_return_rate() -> f64 {
3226    0.03
3227}
3228
3229fn default_bad_debt_rate() -> f64 {
3230    0.01
3231}
3232
3233fn default_so_to_delivery_days() -> u32 {
3234    7
3235}
3236
3237fn default_delivery_to_invoice_days() -> u32 {
3238    1
3239}
3240
3241fn default_invoice_to_receipt_days() -> u32 {
3242    45
3243}
3244
3245impl Default for O2CFlowConfig {
3246    fn default() -> Self {
3247        Self {
3248            enabled: true,
3249            credit_check_failure_rate: default_credit_check_failure_rate(),
3250            partial_shipment_rate: default_partial_shipment_rate(),
3251            return_rate: default_return_rate(),
3252            bad_debt_rate: default_bad_debt_rate(),
3253            average_so_to_delivery_days: default_so_to_delivery_days(),
3254            average_delivery_to_invoice_days: default_delivery_to_invoice_days(),
3255            average_invoice_to_receipt_days: default_invoice_to_receipt_days(),
3256            line_count_distribution: DocumentLineCountDistribution::default(),
3257            cash_discount: CashDiscountConfig::default(),
3258            payment_behavior: O2CPaymentBehaviorConfig::default(),
3259            late_payment_rate: None,
3260        }
3261    }
3262}
3263
3264// ============================================================================
3265// O2C Payment Behavior Configuration
3266// ============================================================================
3267
3268/// O2C payment behavior configuration.
3269#[derive(Debug, Clone, Serialize, Deserialize, Default)]
3270pub struct O2CPaymentBehaviorConfig {
3271    /// Dunning (Mahnung) configuration
3272    #[serde(default)]
3273    pub dunning: DunningConfig,
3274    /// Partial payment configuration
3275    #[serde(default)]
3276    pub partial_payments: PartialPaymentConfig,
3277    /// Short payment configuration (unauthorized deductions)
3278    #[serde(default)]
3279    pub short_payments: ShortPaymentConfig,
3280    /// On-account payment configuration (unapplied payments)
3281    #[serde(default)]
3282    pub on_account_payments: OnAccountPaymentConfig,
3283    /// Payment correction configuration (NSF, chargebacks)
3284    #[serde(default)]
3285    pub payment_corrections: PaymentCorrectionConfig,
3286}
3287
3288/// Dunning (Mahnungen) configuration for AR collections.
3289#[derive(Debug, Clone, Serialize, Deserialize)]
3290pub struct DunningConfig {
3291    /// Enable dunning process
3292    #[serde(default)]
3293    pub enabled: bool,
3294    /// Days overdue for level 1 dunning (1st reminder)
3295    #[serde(default = "default_dunning_level_1_days")]
3296    pub level_1_days_overdue: u32,
3297    /// Days overdue for level 2 dunning (2nd reminder)
3298    #[serde(default = "default_dunning_level_2_days")]
3299    pub level_2_days_overdue: u32,
3300    /// Days overdue for level 3 dunning (final notice)
3301    #[serde(default = "default_dunning_level_3_days")]
3302    pub level_3_days_overdue: u32,
3303    /// Days overdue for collection handover
3304    #[serde(default = "default_collection_days")]
3305    pub collection_days_overdue: u32,
3306    /// Payment rates after each dunning level
3307    #[serde(default)]
3308    pub payment_after_dunning_rates: DunningPaymentRates,
3309    /// Rate of invoices blocked from dunning (disputes)
3310    #[serde(default = "default_dunning_block_rate")]
3311    pub dunning_block_rate: f64,
3312    /// Interest rate per year for overdue amounts
3313    #[serde(default = "default_dunning_interest_rate")]
3314    pub interest_rate_per_year: f64,
3315    /// Fixed dunning charge per letter
3316    #[serde(default = "default_dunning_charge")]
3317    pub dunning_charge: f64,
3318}
3319
3320fn default_dunning_level_1_days() -> u32 {
3321    14
3322}
3323
3324fn default_dunning_level_2_days() -> u32 {
3325    28
3326}
3327
3328fn default_dunning_level_3_days() -> u32 {
3329    42
3330}
3331
3332fn default_collection_days() -> u32 {
3333    60
3334}
3335
3336fn default_dunning_block_rate() -> f64 {
3337    0.05
3338}
3339
3340fn default_dunning_interest_rate() -> f64 {
3341    0.09
3342}
3343
3344fn default_dunning_charge() -> f64 {
3345    25.0
3346}
3347
3348impl Default for DunningConfig {
3349    fn default() -> Self {
3350        Self {
3351            enabled: false,
3352            level_1_days_overdue: default_dunning_level_1_days(),
3353            level_2_days_overdue: default_dunning_level_2_days(),
3354            level_3_days_overdue: default_dunning_level_3_days(),
3355            collection_days_overdue: default_collection_days(),
3356            payment_after_dunning_rates: DunningPaymentRates::default(),
3357            dunning_block_rate: default_dunning_block_rate(),
3358            interest_rate_per_year: default_dunning_interest_rate(),
3359            dunning_charge: default_dunning_charge(),
3360        }
3361    }
3362}
3363
3364/// Payment rates after each dunning level.
3365#[derive(Debug, Clone, Serialize, Deserialize)]
3366pub struct DunningPaymentRates {
3367    /// Rate that pays after level 1 reminder
3368    #[serde(default = "default_after_level_1")]
3369    pub after_level_1: f64,
3370    /// Rate that pays after level 2 reminder
3371    #[serde(default = "default_after_level_2")]
3372    pub after_level_2: f64,
3373    /// Rate that pays after level 3 final notice
3374    #[serde(default = "default_after_level_3")]
3375    pub after_level_3: f64,
3376    /// Rate that pays during collection
3377    #[serde(default = "default_during_collection")]
3378    pub during_collection: f64,
3379    /// Rate that never pays (becomes bad debt)
3380    #[serde(default = "default_never_pay")]
3381    pub never_pay: f64,
3382}
3383
3384fn default_after_level_1() -> f64 {
3385    0.40
3386}
3387
3388fn default_after_level_2() -> f64 {
3389    0.30
3390}
3391
3392fn default_after_level_3() -> f64 {
3393    0.15
3394}
3395
3396fn default_during_collection() -> f64 {
3397    0.05
3398}
3399
3400fn default_never_pay() -> f64 {
3401    0.10
3402}
3403
3404impl Default for DunningPaymentRates {
3405    fn default() -> Self {
3406        Self {
3407            after_level_1: default_after_level_1(),
3408            after_level_2: default_after_level_2(),
3409            after_level_3: default_after_level_3(),
3410            during_collection: default_during_collection(),
3411            never_pay: default_never_pay(),
3412        }
3413    }
3414}
3415
3416/// Partial payment configuration.
3417#[derive(Debug, Clone, Serialize, Deserialize)]
3418pub struct PartialPaymentConfig {
3419    /// Rate of invoices paid partially
3420    #[serde(default = "default_partial_payment_rate")]
3421    pub rate: f64,
3422    /// Distribution of partial payment percentages
3423    #[serde(default)]
3424    pub percentage_distribution: PartialPaymentPercentageDistribution,
3425    /// Average days until remainder is paid
3426    #[serde(default = "default_avg_days_until_remainder")]
3427    pub avg_days_until_remainder: u32,
3428}
3429
3430fn default_partial_payment_rate() -> f64 {
3431    0.08
3432}
3433
3434fn default_avg_days_until_remainder() -> u32 {
3435    30
3436}
3437
3438impl Default for PartialPaymentConfig {
3439    fn default() -> Self {
3440        Self {
3441            rate: default_partial_payment_rate(),
3442            percentage_distribution: PartialPaymentPercentageDistribution::default(),
3443            avg_days_until_remainder: default_avg_days_until_remainder(),
3444        }
3445    }
3446}
3447
3448/// Distribution of partial payment percentages.
3449#[derive(Debug, Clone, Serialize, Deserialize)]
3450pub struct PartialPaymentPercentageDistribution {
3451    /// Pay 25% of invoice
3452    #[serde(default = "default_partial_25")]
3453    pub pay_25_percent: f64,
3454    /// Pay 50% of invoice
3455    #[serde(default = "default_partial_50")]
3456    pub pay_50_percent: f64,
3457    /// Pay 75% of invoice
3458    #[serde(default = "default_partial_75")]
3459    pub pay_75_percent: f64,
3460    /// Pay random percentage
3461    #[serde(default = "default_partial_random")]
3462    pub pay_random_percent: f64,
3463}
3464
3465fn default_partial_25() -> f64 {
3466    0.15
3467}
3468
3469fn default_partial_50() -> f64 {
3470    0.50
3471}
3472
3473fn default_partial_75() -> f64 {
3474    0.25
3475}
3476
3477fn default_partial_random() -> f64 {
3478    0.10
3479}
3480
3481impl Default for PartialPaymentPercentageDistribution {
3482    fn default() -> Self {
3483        Self {
3484            pay_25_percent: default_partial_25(),
3485            pay_50_percent: default_partial_50(),
3486            pay_75_percent: default_partial_75(),
3487            pay_random_percent: default_partial_random(),
3488        }
3489    }
3490}
3491
3492/// Short payment configuration (unauthorized deductions).
3493#[derive(Debug, Clone, Serialize, Deserialize)]
3494pub struct ShortPaymentConfig {
3495    /// Rate of payments that are short
3496    #[serde(default = "default_short_payment_rate")]
3497    pub rate: f64,
3498    /// Distribution of short payment reasons
3499    #[serde(default)]
3500    pub reason_distribution: ShortPaymentReasonDistribution,
3501    /// Maximum percentage that can be short
3502    #[serde(default = "default_max_short_percent")]
3503    pub max_short_percent: f64,
3504}
3505
3506fn default_short_payment_rate() -> f64 {
3507    0.03
3508}
3509
3510fn default_max_short_percent() -> f64 {
3511    0.10
3512}
3513
3514impl Default for ShortPaymentConfig {
3515    fn default() -> Self {
3516        Self {
3517            rate: default_short_payment_rate(),
3518            reason_distribution: ShortPaymentReasonDistribution::default(),
3519            max_short_percent: default_max_short_percent(),
3520        }
3521    }
3522}
3523
3524/// Distribution of short payment reasons.
3525#[derive(Debug, Clone, Serialize, Deserialize)]
3526pub struct ShortPaymentReasonDistribution {
3527    /// Pricing dispute
3528    #[serde(default = "default_pricing_dispute")]
3529    pub pricing_dispute: f64,
3530    /// Quality issue
3531    #[serde(default = "default_quality_issue")]
3532    pub quality_issue: f64,
3533    /// Quantity discrepancy
3534    #[serde(default = "default_quantity_discrepancy")]
3535    pub quantity_discrepancy: f64,
3536    /// Unauthorized deduction
3537    #[serde(default = "default_unauthorized_deduction")]
3538    pub unauthorized_deduction: f64,
3539    /// Early payment discount taken incorrectly
3540    #[serde(default = "default_incorrect_discount")]
3541    pub incorrect_discount: f64,
3542}
3543
3544fn default_pricing_dispute() -> f64 {
3545    0.30
3546}
3547
3548fn default_quality_issue() -> f64 {
3549    0.20
3550}
3551
3552fn default_quantity_discrepancy() -> f64 {
3553    0.20
3554}
3555
3556fn default_unauthorized_deduction() -> f64 {
3557    0.15
3558}
3559
3560fn default_incorrect_discount() -> f64 {
3561    0.15
3562}
3563
3564impl Default for ShortPaymentReasonDistribution {
3565    fn default() -> Self {
3566        Self {
3567            pricing_dispute: default_pricing_dispute(),
3568            quality_issue: default_quality_issue(),
3569            quantity_discrepancy: default_quantity_discrepancy(),
3570            unauthorized_deduction: default_unauthorized_deduction(),
3571            incorrect_discount: default_incorrect_discount(),
3572        }
3573    }
3574}
3575
3576/// On-account payment configuration (unapplied payments).
3577#[derive(Debug, Clone, Serialize, Deserialize)]
3578pub struct OnAccountPaymentConfig {
3579    /// Rate of payments that are on-account (unapplied)
3580    #[serde(default = "default_on_account_rate")]
3581    pub rate: f64,
3582    /// Average days until on-account payments are applied
3583    #[serde(default = "default_avg_days_until_applied")]
3584    pub avg_days_until_applied: u32,
3585}
3586
3587fn default_on_account_rate() -> f64 {
3588    0.02
3589}
3590
3591fn default_avg_days_until_applied() -> u32 {
3592    14
3593}
3594
3595impl Default for OnAccountPaymentConfig {
3596    fn default() -> Self {
3597        Self {
3598            rate: default_on_account_rate(),
3599            avg_days_until_applied: default_avg_days_until_applied(),
3600        }
3601    }
3602}
3603
3604/// Payment correction configuration.
3605#[derive(Debug, Clone, Serialize, Deserialize)]
3606pub struct PaymentCorrectionConfig {
3607    /// Rate of payments requiring correction
3608    #[serde(default = "default_payment_correction_rate")]
3609    pub rate: f64,
3610    /// Distribution of correction types
3611    #[serde(default)]
3612    pub type_distribution: PaymentCorrectionTypeDistribution,
3613}
3614
3615fn default_payment_correction_rate() -> f64 {
3616    0.02
3617}
3618
3619impl Default for PaymentCorrectionConfig {
3620    fn default() -> Self {
3621        Self {
3622            rate: default_payment_correction_rate(),
3623            type_distribution: PaymentCorrectionTypeDistribution::default(),
3624        }
3625    }
3626}
3627
3628/// Distribution of payment correction types.
3629#[derive(Debug, Clone, Serialize, Deserialize)]
3630pub struct PaymentCorrectionTypeDistribution {
3631    /// NSF (Non-sufficient funds) / bounced check
3632    #[serde(default = "default_nsf_rate")]
3633    pub nsf: f64,
3634    /// Chargeback
3635    #[serde(default = "default_chargeback_rate")]
3636    pub chargeback: f64,
3637    /// Wrong amount applied
3638    #[serde(default = "default_wrong_amount_rate")]
3639    pub wrong_amount: f64,
3640    /// Wrong customer applied
3641    #[serde(default = "default_wrong_customer_rate")]
3642    pub wrong_customer: f64,
3643    /// Duplicate payment
3644    #[serde(default = "default_duplicate_payment_rate")]
3645    pub duplicate_payment: f64,
3646}
3647
3648fn default_nsf_rate() -> f64 {
3649    0.30
3650}
3651
3652fn default_chargeback_rate() -> f64 {
3653    0.20
3654}
3655
3656fn default_wrong_amount_rate() -> f64 {
3657    0.20
3658}
3659
3660fn default_wrong_customer_rate() -> f64 {
3661    0.15
3662}
3663
3664fn default_duplicate_payment_rate() -> f64 {
3665    0.15
3666}
3667
3668impl Default for PaymentCorrectionTypeDistribution {
3669    fn default() -> Self {
3670        Self {
3671            nsf: default_nsf_rate(),
3672            chargeback: default_chargeback_rate(),
3673            wrong_amount: default_wrong_amount_rate(),
3674            wrong_customer: default_wrong_customer_rate(),
3675            duplicate_payment: default_duplicate_payment_rate(),
3676        }
3677    }
3678}
3679
3680/// Document line count distribution.
3681#[derive(Debug, Clone, Serialize, Deserialize)]
3682pub struct DocumentLineCountDistribution {
3683    /// Minimum number of lines
3684    #[serde(default = "default_min_lines")]
3685    pub min_lines: u32,
3686    /// Maximum number of lines
3687    #[serde(default = "default_max_lines")]
3688    pub max_lines: u32,
3689    /// Most common line count (mode)
3690    #[serde(default = "default_mode_lines")]
3691    pub mode_lines: u32,
3692}
3693
3694fn default_min_lines() -> u32 {
3695    1
3696}
3697
3698fn default_max_lines() -> u32 {
3699    20
3700}
3701
3702fn default_mode_lines() -> u32 {
3703    3
3704}
3705
3706impl Default for DocumentLineCountDistribution {
3707    fn default() -> Self {
3708        Self {
3709            min_lines: default_min_lines(),
3710            max_lines: default_max_lines(),
3711            mode_lines: default_mode_lines(),
3712        }
3713    }
3714}
3715
3716/// Cash discount configuration.
3717#[derive(Debug, Clone, Serialize, Deserialize)]
3718pub struct CashDiscountConfig {
3719    /// Percentage of invoices eligible for cash discount
3720    #[serde(default = "default_discount_eligible_rate")]
3721    pub eligible_rate: f64,
3722    /// Rate at which customers take the discount
3723    #[serde(default = "default_discount_taken_rate")]
3724    pub taken_rate: f64,
3725    /// Standard discount percentage
3726    #[serde(default = "default_discount_percent")]
3727    pub discount_percent: f64,
3728    /// Days within which discount must be taken
3729    #[serde(default = "default_discount_days")]
3730    pub discount_days: u32,
3731}
3732
3733fn default_discount_eligible_rate() -> f64 {
3734    0.30
3735}
3736
3737fn default_discount_taken_rate() -> f64 {
3738    0.60
3739}
3740
3741fn default_discount_percent() -> f64 {
3742    0.02
3743}
3744
3745fn default_discount_days() -> u32 {
3746    10
3747}
3748
3749impl Default for CashDiscountConfig {
3750    fn default() -> Self {
3751        Self {
3752            eligible_rate: default_discount_eligible_rate(),
3753            taken_rate: default_discount_taken_rate(),
3754            discount_percent: default_discount_percent(),
3755            discount_days: default_discount_days(),
3756        }
3757    }
3758}
3759
3760// ============================================================================
3761// Intercompany Configuration
3762// ============================================================================
3763
3764/// Intercompany transaction configuration.
3765#[derive(Debug, Clone, Serialize, Deserialize)]
3766pub struct IntercompanyConfig {
3767    /// Enable intercompany transaction generation
3768    #[serde(default)]
3769    pub enabled: bool,
3770    /// Rate of transactions that are intercompany
3771    #[serde(default = "default_ic_transaction_rate")]
3772    pub ic_transaction_rate: f64,
3773    /// Transfer pricing method
3774    #[serde(default)]
3775    pub transfer_pricing_method: TransferPricingMethod,
3776    /// Transfer pricing markup percentage (for cost-plus)
3777    #[serde(default = "default_markup_percent")]
3778    pub markup_percent: f64,
3779    /// Generate matched IC pairs (offsetting entries)
3780    #[serde(default = "default_true")]
3781    pub generate_matched_pairs: bool,
3782    /// IC transaction type distribution
3783    #[serde(default)]
3784    pub transaction_type_distribution: ICTransactionTypeDistribution,
3785    /// Generate elimination entries for consolidation
3786    #[serde(default)]
3787    pub generate_eliminations: bool,
3788}
3789
3790fn default_ic_transaction_rate() -> f64 {
3791    0.15
3792}
3793
3794fn default_markup_percent() -> f64 {
3795    0.05
3796}
3797
3798impl Default for IntercompanyConfig {
3799    fn default() -> Self {
3800        Self {
3801            enabled: false,
3802            ic_transaction_rate: default_ic_transaction_rate(),
3803            transfer_pricing_method: TransferPricingMethod::default(),
3804            markup_percent: default_markup_percent(),
3805            generate_matched_pairs: true,
3806            transaction_type_distribution: ICTransactionTypeDistribution::default(),
3807            generate_eliminations: false,
3808        }
3809    }
3810}
3811
3812/// Transfer pricing method.
3813#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize)]
3814#[serde(rename_all = "snake_case")]
3815pub enum TransferPricingMethod {
3816    /// Cost plus a markup
3817    #[default]
3818    CostPlus,
3819    /// Comparable uncontrolled price
3820    ComparableUncontrolled,
3821    /// Resale price method
3822    ResalePrice,
3823    /// Transactional net margin method
3824    TransactionalNetMargin,
3825    /// Profit split method
3826    ProfitSplit,
3827}
3828
3829/// IC transaction type distribution.
3830#[derive(Debug, Clone, Serialize, Deserialize)]
3831pub struct ICTransactionTypeDistribution {
3832    /// Goods sales between entities
3833    pub goods_sale: f64,
3834    /// Services provided
3835    pub service_provided: f64,
3836    /// Intercompany loans
3837    pub loan: f64,
3838    /// Dividends
3839    pub dividend: f64,
3840    /// Management fees
3841    pub management_fee: f64,
3842    /// Royalties
3843    pub royalty: f64,
3844    /// Cost sharing
3845    pub cost_sharing: f64,
3846}
3847
3848impl Default for ICTransactionTypeDistribution {
3849    fn default() -> Self {
3850        Self {
3851            goods_sale: 0.35,
3852            service_provided: 0.20,
3853            loan: 0.10,
3854            dividend: 0.05,
3855            management_fee: 0.15,
3856            royalty: 0.10,
3857            cost_sharing: 0.05,
3858        }
3859    }
3860}
3861
3862// ============================================================================
3863// Balance Configuration
3864// ============================================================================
3865
3866/// Balance and trial balance configuration.
3867#[derive(Debug, Clone, Serialize, Deserialize)]
3868pub struct BalanceConfig {
3869    /// Generate opening balances
3870    #[serde(default)]
3871    pub generate_opening_balances: bool,
3872    /// Generate trial balances
3873    #[serde(default = "default_true")]
3874    pub generate_trial_balances: bool,
3875    /// Target gross margin (for revenue/COGS coherence)
3876    #[serde(default = "default_gross_margin")]
3877    pub target_gross_margin: f64,
3878    /// Target DSO (Days Sales Outstanding)
3879    #[serde(default = "default_dso")]
3880    pub target_dso_days: u32,
3881    /// Target DPO (Days Payable Outstanding)
3882    #[serde(default = "default_dpo")]
3883    pub target_dpo_days: u32,
3884    /// Target current ratio
3885    #[serde(default = "default_current_ratio")]
3886    pub target_current_ratio: f64,
3887    /// Target debt-to-equity ratio
3888    #[serde(default = "default_debt_equity")]
3889    pub target_debt_to_equity: f64,
3890    /// Validate balance sheet equation (A = L + E)
3891    #[serde(default = "default_true")]
3892    pub validate_balance_equation: bool,
3893    /// Reconcile subledgers to GL control accounts
3894    #[serde(default = "default_true")]
3895    pub reconcile_subledgers: bool,
3896}
3897
3898fn default_gross_margin() -> f64 {
3899    0.35
3900}
3901
3902fn default_dso() -> u32 {
3903    45
3904}
3905
3906fn default_dpo() -> u32 {
3907    30
3908}
3909
3910fn default_current_ratio() -> f64 {
3911    1.5
3912}
3913
3914fn default_debt_equity() -> f64 {
3915    0.5
3916}
3917
3918impl Default for BalanceConfig {
3919    fn default() -> Self {
3920        Self {
3921            generate_opening_balances: false,
3922            generate_trial_balances: true,
3923            target_gross_margin: default_gross_margin(),
3924            target_dso_days: default_dso(),
3925            target_dpo_days: default_dpo(),
3926            target_current_ratio: default_current_ratio(),
3927            target_debt_to_equity: default_debt_equity(),
3928            validate_balance_equation: true,
3929            reconcile_subledgers: true,
3930        }
3931    }
3932}
3933
3934// ==========================================================================
3935// OCPM (Object-Centric Process Mining) Configuration
3936// ==========================================================================
3937
3938/// OCPM (Object-Centric Process Mining) configuration.
3939///
3940/// Controls generation of OCEL 2.0 compatible event logs with
3941/// many-to-many event-to-object relationships.
3942#[derive(Debug, Clone, Serialize, Deserialize)]
3943pub struct OcpmConfig {
3944    /// Enable OCPM event log generation
3945    #[serde(default)]
3946    pub enabled: bool,
3947
3948    /// Generate lifecycle events (Start/Complete pairs vs atomic events)
3949    #[serde(default = "default_true")]
3950    pub generate_lifecycle_events: bool,
3951
3952    /// Include object-to-object relationships in output
3953    #[serde(default = "default_true")]
3954    pub include_object_relationships: bool,
3955
3956    /// Compute and export process variants
3957    #[serde(default = "default_true")]
3958    pub compute_variants: bool,
3959
3960    /// Maximum variants to track (0 = unlimited)
3961    #[serde(default)]
3962    pub max_variants: usize,
3963
3964    /// P2P process configuration
3965    #[serde(default)]
3966    pub p2p_process: OcpmProcessConfig,
3967
3968    /// O2C process configuration
3969    #[serde(default)]
3970    pub o2c_process: OcpmProcessConfig,
3971
3972    /// Output format configuration
3973    #[serde(default)]
3974    pub output: OcpmOutputConfig,
3975}
3976
3977impl Default for OcpmConfig {
3978    fn default() -> Self {
3979        Self {
3980            enabled: false,
3981            generate_lifecycle_events: true,
3982            include_object_relationships: true,
3983            compute_variants: true,
3984            max_variants: 0,
3985            p2p_process: OcpmProcessConfig::default(),
3986            o2c_process: OcpmProcessConfig::default(),
3987            output: OcpmOutputConfig::default(),
3988        }
3989    }
3990}
3991
3992/// Process-specific OCPM configuration.
3993#[derive(Debug, Clone, Serialize, Deserialize)]
3994pub struct OcpmProcessConfig {
3995    /// Rework probability (0.0-1.0)
3996    #[serde(default = "default_rework_probability")]
3997    pub rework_probability: f64,
3998
3999    /// Skip step probability (0.0-1.0)
4000    #[serde(default = "default_skip_probability")]
4001    pub skip_step_probability: f64,
4002
4003    /// Out-of-order step probability (0.0-1.0)
4004    #[serde(default = "default_out_of_order_probability")]
4005    pub out_of_order_probability: f64,
4006}
4007
4008// Defaults deliberately produce variant counts and Inductive-Miner fitness
4009// in the range seen in real ERP data (dozens of variants, ~0.7–0.9 fitness).
4010// Lowering them all to 0 yields a single-variant happy-path log.
4011fn default_rework_probability() -> f64 {
4012    0.15
4013}
4014
4015fn default_skip_probability() -> f64 {
4016    0.10
4017}
4018
4019fn default_out_of_order_probability() -> f64 {
4020    0.08
4021}
4022
4023impl Default for OcpmProcessConfig {
4024    fn default() -> Self {
4025        Self {
4026            rework_probability: default_rework_probability(),
4027            skip_step_probability: default_skip_probability(),
4028            out_of_order_probability: default_out_of_order_probability(),
4029        }
4030    }
4031}
4032
4033/// OCPM output format configuration.
4034#[derive(Debug, Clone, Serialize, Deserialize)]
4035pub struct OcpmOutputConfig {
4036    /// Export OCEL 2.0 JSON format
4037    #[serde(default = "default_true")]
4038    pub ocel_json: bool,
4039
4040    /// Export OCEL 2.0 XML format
4041    #[serde(default)]
4042    pub ocel_xml: bool,
4043
4044    /// Export XES 2.0 XML format (IEEE standard for process mining tools)
4045    #[serde(default)]
4046    pub xes: bool,
4047
4048    /// Include lifecycle transitions in XES output (start/complete pairs)
4049    #[serde(default = "default_true")]
4050    pub xes_include_lifecycle: bool,
4051
4052    /// Include resource attributes in XES output
4053    #[serde(default = "default_true")]
4054    pub xes_include_resources: bool,
4055
4056    /// Export flattened CSV for each object type
4057    #[serde(default = "default_true")]
4058    pub flattened_csv: bool,
4059
4060    /// Export event-object relationship table
4061    #[serde(default = "default_true")]
4062    pub event_object_csv: bool,
4063
4064    /// Export object-object relationship table
4065    #[serde(default = "default_true")]
4066    pub object_relationship_csv: bool,
4067
4068    /// Export process variants summary
4069    #[serde(default = "default_true")]
4070    pub variants_csv: bool,
4071
4072    /// Export reference process models (canonical P2P, O2C, R2R)
4073    #[serde(default)]
4074    pub export_reference_models: bool,
4075}
4076
4077impl Default for OcpmOutputConfig {
4078    fn default() -> Self {
4079        Self {
4080            ocel_json: true,
4081            ocel_xml: false,
4082            xes: false,
4083            xes_include_lifecycle: true,
4084            xes_include_resources: true,
4085            flattened_csv: true,
4086            event_object_csv: true,
4087            object_relationship_csv: true,
4088            variants_csv: true,
4089            export_reference_models: false,
4090        }
4091    }
4092}
4093
4094/// Audit engagement and workpaper generation configuration.
4095#[derive(Debug, Clone, Serialize, Deserialize)]
4096pub struct AuditGenerationConfig {
4097    /// Enable audit engagement generation
4098    #[serde(default)]
4099    pub enabled: bool,
4100
4101    /// Gate for workpaper generation (v3.3.2+).
4102    /// When `false`, workpapers and dependent evidence are skipped
4103    /// while engagements / risk assessments / findings still generate.
4104    #[serde(default = "default_true")]
4105    pub generate_workpapers: bool,
4106
4107    /// Engagement type distribution (v3.3.2+). Drives per-engagement
4108    /// type draw via `AuditEngagementGenerator::draw_engagement_type`.
4109    #[serde(default)]
4110    pub engagement_types: AuditEngagementTypesConfig,
4111
4112    /// Workpaper configuration (v3.3.2+). `average_per_phase` maps onto
4113    /// `WorkpaperGenerator.workpapers_per_section` as a ±50% band
4114    /// around the average. Sampling / ISA / cross-reference flags are
4115    /// surfaced for downstream formatting overlays.
4116    #[serde(default)]
4117    pub workpapers: WorkpaperConfig,
4118
4119    /// Audit team configuration (v3.3.2+). `min_team_size` /
4120    /// `max_team_size` map directly onto
4121    /// `AuditEngagementGenerator.team_size_range`.
4122    /// `specialist_probability` is reserved for v3.4 (explicit
4123    /// specialist-role support).
4124    #[serde(default)]
4125    pub team: AuditTeamConfig,
4126
4127    /// Review workflow configuration (v3.3.2+).
4128    /// `average_review_delay_days` drives both
4129    /// `first_review_delay_range` and `second_review_delay_range` as
4130    /// a ±1-day band around the average. `rework_probability` and
4131    /// `require_partner_signoff` are reserved for v3.4 workflow
4132    /// modeling.
4133    #[serde(default)]
4134    pub review: ReviewWorkflowConfig,
4135
4136    /// FSM-driven audit generation configuration.
4137    #[serde(default)]
4138    pub fsm: Option<AuditFsmConfig>,
4139
4140    /// v3.3.0: IT general controls (access logs, change management
4141    /// records) emitted alongside audit engagements. Requires both
4142    /// `audit.enabled = true` and `audit.it_controls.enabled = true`
4143    /// to take effect — the latter defaults to `false` so current
4144    /// archives are byte-identical to v3.2.1.
4145    #[serde(default)]
4146    pub it_controls: ItControlsConfig,
4147}
4148
4149/// IT general controls config (v3.3.0+).
4150#[derive(Debug, Clone, Serialize, Deserialize)]
4151pub struct ItControlsConfig {
4152    /// Master switch — when `false`, no access logs or change records
4153    /// are generated.
4154    #[serde(default)]
4155    pub enabled: bool,
4156    /// Number of access-log entries per engagement (approximate — the
4157    /// generator may round or scale based on company size).
4158    #[serde(default = "default_access_log_count")]
4159    pub access_logs_per_engagement: usize,
4160    /// Number of change-management records per engagement.
4161    #[serde(default = "default_change_record_count")]
4162    pub change_records_per_engagement: usize,
4163}
4164
4165fn default_access_log_count() -> usize {
4166    500
4167}
4168fn default_change_record_count() -> usize {
4169    50
4170}
4171
4172impl Default for ItControlsConfig {
4173    fn default() -> Self {
4174        Self {
4175            enabled: false,
4176            access_logs_per_engagement: default_access_log_count(),
4177            change_records_per_engagement: default_change_record_count(),
4178        }
4179    }
4180}
4181
4182impl Default for AuditGenerationConfig {
4183    fn default() -> Self {
4184        Self {
4185            enabled: false,
4186            generate_workpapers: true,
4187            engagement_types: AuditEngagementTypesConfig::default(),
4188            workpapers: WorkpaperConfig::default(),
4189            team: AuditTeamConfig::default(),
4190            review: ReviewWorkflowConfig::default(),
4191            fsm: None,
4192            it_controls: ItControlsConfig::default(),
4193        }
4194    }
4195}
4196
4197/// FSM-driven audit generation configuration.
4198#[derive(Debug, Clone, Serialize, Deserialize)]
4199pub struct AuditFsmConfig {
4200    /// Enable FSM-driven audit generation.
4201    #[serde(default)]
4202    pub enabled: bool,
4203
4204    /// Blueprint source: "builtin:fsa", "builtin:ia", or a file path.
4205    #[serde(default = "default_audit_fsm_blueprint")]
4206    pub blueprint: String,
4207
4208    /// Overlay source: "builtin:default", "builtin:thorough", "builtin:rushed", or a file path.
4209    #[serde(default = "default_audit_fsm_overlay")]
4210    pub overlay: String,
4211
4212    /// Depth level override.
4213    #[serde(default)]
4214    pub depth: Option<String>,
4215
4216    /// Discriminator filter.
4217    #[serde(default)]
4218    pub discriminators: std::collections::HashMap<String, Vec<String>>,
4219
4220    /// Event trail output config.
4221    #[serde(default)]
4222    pub event_trail: AuditEventTrailConfig,
4223
4224    /// RNG seed override.
4225    #[serde(default)]
4226    pub seed: Option<u64>,
4227}
4228
4229impl Default for AuditFsmConfig {
4230    fn default() -> Self {
4231        Self {
4232            enabled: false,
4233            blueprint: default_audit_fsm_blueprint(),
4234            overlay: default_audit_fsm_overlay(),
4235            depth: None,
4236            discriminators: std::collections::HashMap::new(),
4237            event_trail: AuditEventTrailConfig::default(),
4238            seed: None,
4239        }
4240    }
4241}
4242
4243fn default_audit_fsm_blueprint() -> String {
4244    "builtin:fsa".to_string()
4245}
4246
4247fn default_audit_fsm_overlay() -> String {
4248    "builtin:default".to_string()
4249}
4250
4251/// Event trail output configuration for FSM-driven audit generation.
4252#[derive(Debug, Clone, Serialize, Deserialize)]
4253pub struct AuditEventTrailConfig {
4254    /// Emit a flat event log.
4255    #[serde(default = "default_true")]
4256    pub flat_log: bool,
4257    /// Project events to OCEL 2.0 format.
4258    #[serde(default)]
4259    pub ocel_projection: bool,
4260}
4261
4262impl Default for AuditEventTrailConfig {
4263    fn default() -> Self {
4264        Self {
4265            flat_log: true,
4266            ocel_projection: false,
4267        }
4268    }
4269}
4270
4271/// Engagement type distribution configuration.
4272#[derive(Debug, Clone, Serialize, Deserialize)]
4273pub struct AuditEngagementTypesConfig {
4274    /// Financial statement audit probability
4275    #[serde(default = "default_financial_audit_prob")]
4276    pub financial_statement: f64,
4277    /// SOX/ICFR audit probability
4278    #[serde(default = "default_sox_audit_prob")]
4279    pub sox_icfr: f64,
4280    /// Integrated audit probability
4281    #[serde(default = "default_integrated_audit_prob")]
4282    pub integrated: f64,
4283    /// Review engagement probability
4284    #[serde(default = "default_review_prob")]
4285    pub review: f64,
4286    /// Agreed-upon procedures probability
4287    #[serde(default = "default_aup_prob")]
4288    pub agreed_upon_procedures: f64,
4289}
4290
4291fn default_financial_audit_prob() -> f64 {
4292    0.40
4293}
4294fn default_sox_audit_prob() -> f64 {
4295    0.20
4296}
4297fn default_integrated_audit_prob() -> f64 {
4298    0.25
4299}
4300fn default_review_prob() -> f64 {
4301    0.10
4302}
4303fn default_aup_prob() -> f64 {
4304    0.05
4305}
4306
4307impl Default for AuditEngagementTypesConfig {
4308    fn default() -> Self {
4309        Self {
4310            financial_statement: default_financial_audit_prob(),
4311            sox_icfr: default_sox_audit_prob(),
4312            integrated: default_integrated_audit_prob(),
4313            review: default_review_prob(),
4314            agreed_upon_procedures: default_aup_prob(),
4315        }
4316    }
4317}
4318
4319/// Workpaper generation configuration.
4320#[derive(Debug, Clone, Serialize, Deserialize)]
4321pub struct WorkpaperConfig {
4322    /// Average workpapers per engagement phase
4323    #[serde(default = "default_workpapers_per_phase")]
4324    pub average_per_phase: usize,
4325
4326    /// Include ISA compliance references
4327    #[serde(default = "default_true")]
4328    pub include_isa_references: bool,
4329
4330    /// Generate sample details
4331    #[serde(default = "default_true")]
4332    pub include_sample_details: bool,
4333
4334    /// Include cross-references between workpapers
4335    #[serde(default = "default_true")]
4336    pub include_cross_references: bool,
4337
4338    /// Sampling configuration
4339    #[serde(default)]
4340    pub sampling: SamplingConfig,
4341}
4342
4343fn default_workpapers_per_phase() -> usize {
4344    5
4345}
4346
4347impl Default for WorkpaperConfig {
4348    fn default() -> Self {
4349        Self {
4350            average_per_phase: default_workpapers_per_phase(),
4351            include_isa_references: true,
4352            include_sample_details: true,
4353            include_cross_references: true,
4354            sampling: SamplingConfig::default(),
4355        }
4356    }
4357}
4358
4359/// Sampling method configuration.
4360#[derive(Debug, Clone, Serialize, Deserialize)]
4361pub struct SamplingConfig {
4362    /// Statistical sampling rate (0.0-1.0)
4363    #[serde(default = "default_statistical_rate")]
4364    pub statistical_rate: f64,
4365    /// Judgmental sampling rate (0.0-1.0)
4366    #[serde(default = "default_judgmental_rate")]
4367    pub judgmental_rate: f64,
4368    /// Haphazard sampling rate (0.0-1.0)
4369    #[serde(default = "default_haphazard_rate")]
4370    pub haphazard_rate: f64,
4371    /// 100% examination rate (0.0-1.0)
4372    #[serde(default = "default_complete_examination_rate")]
4373    pub complete_examination_rate: f64,
4374}
4375
4376fn default_statistical_rate() -> f64 {
4377    0.40
4378}
4379fn default_judgmental_rate() -> f64 {
4380    0.30
4381}
4382fn default_haphazard_rate() -> f64 {
4383    0.20
4384}
4385fn default_complete_examination_rate() -> f64 {
4386    0.10
4387}
4388
4389impl Default for SamplingConfig {
4390    fn default() -> Self {
4391        Self {
4392            statistical_rate: default_statistical_rate(),
4393            judgmental_rate: default_judgmental_rate(),
4394            haphazard_rate: default_haphazard_rate(),
4395            complete_examination_rate: default_complete_examination_rate(),
4396        }
4397    }
4398}
4399
4400/// Audit team configuration.
4401#[derive(Debug, Clone, Serialize, Deserialize)]
4402pub struct AuditTeamConfig {
4403    /// Minimum team size
4404    #[serde(default = "default_min_team_size")]
4405    pub min_team_size: usize,
4406    /// Maximum team size
4407    #[serde(default = "default_max_team_size")]
4408    pub max_team_size: usize,
4409    /// Probability of having a specialist on the team
4410    #[serde(default = "default_specialist_probability")]
4411    pub specialist_probability: f64,
4412}
4413
4414fn default_min_team_size() -> usize {
4415    3
4416}
4417fn default_max_team_size() -> usize {
4418    8
4419}
4420fn default_specialist_probability() -> f64 {
4421    0.30
4422}
4423
4424impl Default for AuditTeamConfig {
4425    fn default() -> Self {
4426        Self {
4427            min_team_size: default_min_team_size(),
4428            max_team_size: default_max_team_size(),
4429            specialist_probability: default_specialist_probability(),
4430        }
4431    }
4432}
4433
4434/// Review workflow configuration.
4435#[derive(Debug, Clone, Serialize, Deserialize)]
4436pub struct ReviewWorkflowConfig {
4437    /// Average days between preparer completion and first review
4438    #[serde(default = "default_review_delay_days")]
4439    pub average_review_delay_days: u32,
4440    /// Probability of review notes requiring rework
4441    #[serde(default = "default_rework_probability_review")]
4442    pub rework_probability: f64,
4443    /// Require partner sign-off for all workpapers
4444    #[serde(default = "default_true")]
4445    pub require_partner_signoff: bool,
4446}
4447
4448fn default_review_delay_days() -> u32 {
4449    2
4450}
4451fn default_rework_probability_review() -> f64 {
4452    0.15
4453}
4454
4455impl Default for ReviewWorkflowConfig {
4456    fn default() -> Self {
4457        Self {
4458            average_review_delay_days: default_review_delay_days(),
4459            rework_probability: default_rework_probability_review(),
4460            require_partner_signoff: true,
4461        }
4462    }
4463}
4464
4465// =============================================================================
4466// Data Quality Configuration
4467// =============================================================================
4468
4469/// Data quality variation settings for realistic flakiness injection.
4470#[derive(Debug, Clone, Serialize, Deserialize)]
4471pub struct DataQualitySchemaConfig {
4472    /// Enable data quality variations
4473    #[serde(default)]
4474    pub enabled: bool,
4475    /// Preset to use (overrides individual settings if set)
4476    #[serde(default)]
4477    pub preset: DataQualityPreset,
4478    /// Missing value injection settings
4479    #[serde(default)]
4480    pub missing_values: MissingValuesSchemaConfig,
4481    /// Typo injection settings
4482    #[serde(default)]
4483    pub typos: TypoSchemaConfig,
4484    /// Format variation settings
4485    #[serde(default)]
4486    pub format_variations: FormatVariationSchemaConfig,
4487    /// Duplicate injection settings
4488    #[serde(default)]
4489    pub duplicates: DuplicateSchemaConfig,
4490    /// Encoding issue settings
4491    #[serde(default)]
4492    pub encoding_issues: EncodingIssueSchemaConfig,
4493    /// Generate quality issue labels for ML training
4494    #[serde(default)]
4495    pub generate_labels: bool,
4496    /// Per-sink quality profiles (different settings for CSV vs JSON etc.)
4497    #[serde(default)]
4498    pub sink_profiles: SinkQualityProfiles,
4499}
4500
4501impl Default for DataQualitySchemaConfig {
4502    fn default() -> Self {
4503        Self {
4504            enabled: false,
4505            preset: DataQualityPreset::None,
4506            missing_values: MissingValuesSchemaConfig::default(),
4507            typos: TypoSchemaConfig::default(),
4508            format_variations: FormatVariationSchemaConfig::default(),
4509            duplicates: DuplicateSchemaConfig::default(),
4510            encoding_issues: EncodingIssueSchemaConfig::default(),
4511            generate_labels: true,
4512            sink_profiles: SinkQualityProfiles::default(),
4513        }
4514    }
4515}
4516
4517impl DataQualitySchemaConfig {
4518    /// Creates a config for a specific preset profile.
4519    pub fn with_preset(preset: DataQualityPreset) -> Self {
4520        let mut config = Self {
4521            preset,
4522            ..Default::default()
4523        };
4524        config.apply_preset();
4525        config
4526    }
4527
4528    /// Applies the preset settings to the individual configuration fields.
4529    /// Call this after deserializing if preset is not Custom or None.
4530    pub fn apply_preset(&mut self) {
4531        if !self.preset.overrides_settings() {
4532            return;
4533        }
4534
4535        self.enabled = true;
4536
4537        // Missing values
4538        self.missing_values.enabled = self.preset.missing_rate() > 0.0;
4539        self.missing_values.rate = self.preset.missing_rate();
4540
4541        // Typos
4542        self.typos.enabled = self.preset.typo_rate() > 0.0;
4543        self.typos.char_error_rate = self.preset.typo_rate();
4544
4545        // Duplicates
4546        self.duplicates.enabled = self.preset.duplicate_rate() > 0.0;
4547        self.duplicates.exact_duplicate_ratio = self.preset.duplicate_rate() * 0.4;
4548        self.duplicates.near_duplicate_ratio = self.preset.duplicate_rate() * 0.4;
4549        self.duplicates.fuzzy_duplicate_ratio = self.preset.duplicate_rate() * 0.2;
4550
4551        // Format variations
4552        self.format_variations.enabled = self.preset.format_variations_enabled();
4553
4554        // Encoding issues
4555        self.encoding_issues.enabled = self.preset.encoding_issues_enabled();
4556        self.encoding_issues.rate = self.preset.encoding_issue_rate();
4557
4558        // OCR errors for typos in legacy preset
4559        if self.preset.ocr_errors_enabled() {
4560            self.typos.type_weights.ocr_errors = 0.3;
4561        }
4562    }
4563
4564    /// Returns the effective missing value rate (considering preset).
4565    pub fn effective_missing_rate(&self) -> f64 {
4566        if self.preset.overrides_settings() {
4567            self.preset.missing_rate()
4568        } else {
4569            self.missing_values.rate
4570        }
4571    }
4572
4573    /// Returns the effective typo rate (considering preset).
4574    pub fn effective_typo_rate(&self) -> f64 {
4575        if self.preset.overrides_settings() {
4576            self.preset.typo_rate()
4577        } else {
4578            self.typos.char_error_rate
4579        }
4580    }
4581
4582    /// Returns the effective duplicate rate (considering preset).
4583    pub fn effective_duplicate_rate(&self) -> f64 {
4584        if self.preset.overrides_settings() {
4585            self.preset.duplicate_rate()
4586        } else {
4587            self.duplicates.exact_duplicate_ratio
4588                + self.duplicates.near_duplicate_ratio
4589                + self.duplicates.fuzzy_duplicate_ratio
4590        }
4591    }
4592
4593    /// Creates a clean profile config.
4594    pub fn clean() -> Self {
4595        Self::with_preset(DataQualityPreset::Clean)
4596    }
4597
4598    /// Creates a noisy profile config.
4599    pub fn noisy() -> Self {
4600        Self::with_preset(DataQualityPreset::Noisy)
4601    }
4602
4603    /// Creates a legacy profile config.
4604    pub fn legacy() -> Self {
4605        Self::with_preset(DataQualityPreset::Legacy)
4606    }
4607}
4608
4609/// Preset configurations for common data quality scenarios.
4610#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
4611#[serde(rename_all = "snake_case")]
4612pub enum DataQualityPreset {
4613    /// No data quality variations (clean data)
4614    #[default]
4615    None,
4616    /// Minimal variations (very clean data with rare issues)
4617    Minimal,
4618    /// Normal variations (realistic enterprise data quality)
4619    Normal,
4620    /// High variations (messy data for stress testing)
4621    High,
4622    /// Custom (use individual settings)
4623    Custom,
4624
4625    // ========================================
4626    // ML-Oriented Profiles (Phase 2.1)
4627    // ========================================
4628    /// Clean profile for ML training - minimal data quality issues
4629    /// Missing: 0.1%, Typos: 0.05%, Duplicates: 0%, Format: None
4630    Clean,
4631    /// Noisy profile simulating typical production data issues
4632    /// Missing: 5%, Typos: 2%, Duplicates: 1%, Format: Medium
4633    Noisy,
4634    /// Legacy profile simulating migrated/OCR'd historical data
4635    /// Missing: 10%, Typos: 5%, Duplicates: 3%, Format: Heavy + OCR
4636    Legacy,
4637}
4638
4639impl DataQualityPreset {
4640    /// Returns the missing value rate for this preset.
4641    pub fn missing_rate(&self) -> f64 {
4642        match self {
4643            DataQualityPreset::None => 0.0,
4644            DataQualityPreset::Minimal => 0.005,
4645            DataQualityPreset::Normal => 0.02,
4646            DataQualityPreset::High => 0.08,
4647            DataQualityPreset::Custom => 0.01, // Use config value
4648            DataQualityPreset::Clean => 0.001,
4649            DataQualityPreset::Noisy => 0.05,
4650            DataQualityPreset::Legacy => 0.10,
4651        }
4652    }
4653
4654    /// Returns the typo rate for this preset.
4655    pub fn typo_rate(&self) -> f64 {
4656        match self {
4657            DataQualityPreset::None => 0.0,
4658            DataQualityPreset::Minimal => 0.0005,
4659            DataQualityPreset::Normal => 0.002,
4660            DataQualityPreset::High => 0.01,
4661            DataQualityPreset::Custom => 0.001, // Use config value
4662            DataQualityPreset::Clean => 0.0005,
4663            DataQualityPreset::Noisy => 0.02,
4664            DataQualityPreset::Legacy => 0.05,
4665        }
4666    }
4667
4668    /// Returns the duplicate rate for this preset.
4669    pub fn duplicate_rate(&self) -> f64 {
4670        match self {
4671            DataQualityPreset::None => 0.0,
4672            DataQualityPreset::Minimal => 0.001,
4673            DataQualityPreset::Normal => 0.005,
4674            DataQualityPreset::High => 0.02,
4675            DataQualityPreset::Custom => 0.0, // Use config value
4676            DataQualityPreset::Clean => 0.0,
4677            DataQualityPreset::Noisy => 0.01,
4678            DataQualityPreset::Legacy => 0.03,
4679        }
4680    }
4681
4682    /// Returns whether format variations are enabled for this preset.
4683    pub fn format_variations_enabled(&self) -> bool {
4684        match self {
4685            DataQualityPreset::None | DataQualityPreset::Clean => false,
4686            DataQualityPreset::Minimal => true,
4687            DataQualityPreset::Normal => true,
4688            DataQualityPreset::High => true,
4689            DataQualityPreset::Custom => true,
4690            DataQualityPreset::Noisy => true,
4691            DataQualityPreset::Legacy => true,
4692        }
4693    }
4694
4695    /// Returns whether OCR-style errors are enabled for this preset.
4696    pub fn ocr_errors_enabled(&self) -> bool {
4697        matches!(self, DataQualityPreset::Legacy | DataQualityPreset::High)
4698    }
4699
4700    /// Returns whether encoding issues are enabled for this preset.
4701    pub fn encoding_issues_enabled(&self) -> bool {
4702        matches!(
4703            self,
4704            DataQualityPreset::Legacy | DataQualityPreset::High | DataQualityPreset::Noisy
4705        )
4706    }
4707
4708    /// Returns the encoding issue rate for this preset.
4709    pub fn encoding_issue_rate(&self) -> f64 {
4710        match self {
4711            DataQualityPreset::None | DataQualityPreset::Clean | DataQualityPreset::Minimal => 0.0,
4712            DataQualityPreset::Normal => 0.002,
4713            DataQualityPreset::High => 0.01,
4714            DataQualityPreset::Custom => 0.0,
4715            DataQualityPreset::Noisy => 0.005,
4716            DataQualityPreset::Legacy => 0.02,
4717        }
4718    }
4719
4720    /// Returns true if this preset overrides individual settings.
4721    pub fn overrides_settings(&self) -> bool {
4722        !matches!(self, DataQualityPreset::Custom | DataQualityPreset::None)
4723    }
4724
4725    /// Returns a human-readable description of this preset.
4726    pub fn description(&self) -> &'static str {
4727        match self {
4728            DataQualityPreset::None => "No data quality issues (pristine data)",
4729            DataQualityPreset::Minimal => "Very rare data quality issues",
4730            DataQualityPreset::Normal => "Realistic enterprise data quality",
4731            DataQualityPreset::High => "Messy data for stress testing",
4732            DataQualityPreset::Custom => "Custom settings from configuration",
4733            DataQualityPreset::Clean => "ML-ready clean data with minimal issues",
4734            DataQualityPreset::Noisy => "Typical production data with moderate issues",
4735            DataQualityPreset::Legacy => "Legacy/migrated data with heavy issues and OCR errors",
4736        }
4737    }
4738}
4739
4740/// Missing value injection configuration.
4741#[derive(Debug, Clone, Serialize, Deserialize)]
4742pub struct MissingValuesSchemaConfig {
4743    /// Enable missing value injection
4744    #[serde(default)]
4745    pub enabled: bool,
4746    /// Global missing rate (0.0 to 1.0)
4747    #[serde(default = "default_missing_rate")]
4748    pub rate: f64,
4749    /// Missing value strategy
4750    #[serde(default)]
4751    pub strategy: MissingValueStrategy,
4752    /// Field-specific rates (field name -> rate)
4753    #[serde(default)]
4754    pub field_rates: std::collections::HashMap<String, f64>,
4755    /// Fields that should never have missing values
4756    #[serde(default)]
4757    pub protected_fields: Vec<String>,
4758}
4759
4760fn default_missing_rate() -> f64 {
4761    0.01
4762}
4763
4764impl Default for MissingValuesSchemaConfig {
4765    fn default() -> Self {
4766        Self {
4767            enabled: false,
4768            rate: default_missing_rate(),
4769            strategy: MissingValueStrategy::Mcar,
4770            field_rates: std::collections::HashMap::new(),
4771            protected_fields: vec![
4772                "document_id".to_string(),
4773                "company_code".to_string(),
4774                "posting_date".to_string(),
4775            ],
4776        }
4777    }
4778}
4779
4780/// Missing value strategy types.
4781#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
4782#[serde(rename_all = "snake_case")]
4783pub enum MissingValueStrategy {
4784    /// Missing Completely At Random - equal probability for all values
4785    #[default]
4786    Mcar,
4787    /// Missing At Random - depends on other observed values
4788    Mar,
4789    /// Missing Not At Random - depends on the value itself
4790    Mnar,
4791    /// Systematic - entire field groups missing together
4792    Systematic,
4793}
4794
4795/// Typo injection configuration.
4796#[derive(Debug, Clone, Serialize, Deserialize)]
4797pub struct TypoSchemaConfig {
4798    /// Enable typo injection
4799    #[serde(default)]
4800    pub enabled: bool,
4801    /// Character error rate (per character, not per field)
4802    #[serde(default = "default_typo_rate")]
4803    pub char_error_rate: f64,
4804    /// Typo type weights
4805    #[serde(default)]
4806    pub type_weights: TypoTypeWeights,
4807    /// Fields that should never have typos
4808    #[serde(default)]
4809    pub protected_fields: Vec<String>,
4810}
4811
4812fn default_typo_rate() -> f64 {
4813    0.001
4814}
4815
4816impl Default for TypoSchemaConfig {
4817    fn default() -> Self {
4818        Self {
4819            enabled: false,
4820            char_error_rate: default_typo_rate(),
4821            type_weights: TypoTypeWeights::default(),
4822            protected_fields: vec![
4823                "document_id".to_string(),
4824                "gl_account".to_string(),
4825                "company_code".to_string(),
4826            ],
4827        }
4828    }
4829}
4830
4831/// Weights for different typo types.
4832#[derive(Debug, Clone, Serialize, Deserialize)]
4833pub struct TypoTypeWeights {
4834    /// Keyboard-adjacent substitution (e.g., 'a' -> 's')
4835    #[serde(default = "default_substitution_weight")]
4836    pub substitution: f64,
4837    /// Adjacent character transposition (e.g., 'ab' -> 'ba')
4838    #[serde(default = "default_transposition_weight")]
4839    pub transposition: f64,
4840    /// Character insertion
4841    #[serde(default = "default_insertion_weight")]
4842    pub insertion: f64,
4843    /// Character deletion
4844    #[serde(default = "default_deletion_weight")]
4845    pub deletion: f64,
4846    /// OCR-style errors (e.g., '0' -> 'O')
4847    #[serde(default = "default_ocr_weight")]
4848    pub ocr_errors: f64,
4849    /// Homophone substitution (e.g., 'their' -> 'there')
4850    #[serde(default = "default_homophone_weight")]
4851    pub homophones: f64,
4852}
4853
4854fn default_substitution_weight() -> f64 {
4855    0.35
4856}
4857fn default_transposition_weight() -> f64 {
4858    0.25
4859}
4860fn default_insertion_weight() -> f64 {
4861    0.10
4862}
4863fn default_deletion_weight() -> f64 {
4864    0.15
4865}
4866fn default_ocr_weight() -> f64 {
4867    0.10
4868}
4869fn default_homophone_weight() -> f64 {
4870    0.05
4871}
4872
4873impl Default for TypoTypeWeights {
4874    fn default() -> Self {
4875        Self {
4876            substitution: default_substitution_weight(),
4877            transposition: default_transposition_weight(),
4878            insertion: default_insertion_weight(),
4879            deletion: default_deletion_weight(),
4880            ocr_errors: default_ocr_weight(),
4881            homophones: default_homophone_weight(),
4882        }
4883    }
4884}
4885
4886/// Format variation configuration.
4887#[derive(Debug, Clone, Serialize, Deserialize, Default)]
4888pub struct FormatVariationSchemaConfig {
4889    /// Enable format variations
4890    #[serde(default)]
4891    pub enabled: bool,
4892    /// Date format variation settings
4893    #[serde(default)]
4894    pub dates: DateFormatVariationConfig,
4895    /// Amount format variation settings
4896    #[serde(default)]
4897    pub amounts: AmountFormatVariationConfig,
4898    /// Identifier format variation settings
4899    #[serde(default)]
4900    pub identifiers: IdentifierFormatVariationConfig,
4901}
4902
4903/// Date format variation configuration.
4904#[derive(Debug, Clone, Serialize, Deserialize)]
4905pub struct DateFormatVariationConfig {
4906    /// Enable date format variations
4907    #[serde(default)]
4908    pub enabled: bool,
4909    /// Overall variation rate
4910    #[serde(default = "default_date_variation_rate")]
4911    pub rate: f64,
4912    /// Include ISO format (2024-01-15)
4913    #[serde(default = "default_true")]
4914    pub iso_format: bool,
4915    /// Include US format (01/15/2024)
4916    #[serde(default)]
4917    pub us_format: bool,
4918    /// Include EU format (15.01.2024)
4919    #[serde(default)]
4920    pub eu_format: bool,
4921    /// Include long format (January 15, 2024)
4922    #[serde(default)]
4923    pub long_format: bool,
4924}
4925
4926fn default_date_variation_rate() -> f64 {
4927    0.05
4928}
4929
4930impl Default for DateFormatVariationConfig {
4931    fn default() -> Self {
4932        Self {
4933            enabled: false,
4934            rate: default_date_variation_rate(),
4935            iso_format: true,
4936            us_format: false,
4937            eu_format: false,
4938            long_format: false,
4939        }
4940    }
4941}
4942
4943/// Amount format variation configuration.
4944#[derive(Debug, Clone, Serialize, Deserialize)]
4945pub struct AmountFormatVariationConfig {
4946    /// Enable amount format variations
4947    #[serde(default)]
4948    pub enabled: bool,
4949    /// Overall variation rate
4950    #[serde(default = "default_amount_variation_rate")]
4951    pub rate: f64,
4952    /// Include US comma format (1,234.56)
4953    #[serde(default)]
4954    pub us_comma_format: bool,
4955    /// Include EU format (1.234,56)
4956    #[serde(default)]
4957    pub eu_format: bool,
4958    /// Include currency prefix ($1,234.56)
4959    #[serde(default)]
4960    pub currency_prefix: bool,
4961    /// Include accounting format with parentheses for negatives
4962    #[serde(default)]
4963    pub accounting_format: bool,
4964}
4965
4966fn default_amount_variation_rate() -> f64 {
4967    0.02
4968}
4969
4970impl Default for AmountFormatVariationConfig {
4971    fn default() -> Self {
4972        Self {
4973            enabled: false,
4974            rate: default_amount_variation_rate(),
4975            us_comma_format: false,
4976            eu_format: false,
4977            currency_prefix: false,
4978            accounting_format: false,
4979        }
4980    }
4981}
4982
4983/// Identifier format variation configuration.
4984#[derive(Debug, Clone, Serialize, Deserialize)]
4985pub struct IdentifierFormatVariationConfig {
4986    /// Enable identifier format variations
4987    #[serde(default)]
4988    pub enabled: bool,
4989    /// Overall variation rate
4990    #[serde(default = "default_identifier_variation_rate")]
4991    pub rate: f64,
4992    /// Case variations (uppercase, lowercase, mixed)
4993    #[serde(default)]
4994    pub case_variations: bool,
4995    /// Padding variations (leading zeros)
4996    #[serde(default)]
4997    pub padding_variations: bool,
4998    /// Separator variations (dash vs underscore)
4999    #[serde(default)]
5000    pub separator_variations: bool,
5001}
5002
5003fn default_identifier_variation_rate() -> f64 {
5004    0.02
5005}
5006
5007impl Default for IdentifierFormatVariationConfig {
5008    fn default() -> Self {
5009        Self {
5010            enabled: false,
5011            rate: default_identifier_variation_rate(),
5012            case_variations: false,
5013            padding_variations: false,
5014            separator_variations: false,
5015        }
5016    }
5017}
5018
5019/// Duplicate injection configuration.
5020#[derive(Debug, Clone, Serialize, Deserialize)]
5021pub struct DuplicateSchemaConfig {
5022    /// Enable duplicate injection
5023    #[serde(default)]
5024    pub enabled: bool,
5025    /// Overall duplicate rate
5026    #[serde(default = "default_duplicate_rate")]
5027    pub rate: f64,
5028    /// Exact duplicate proportion (out of duplicates)
5029    #[serde(default = "default_exact_duplicate_ratio")]
5030    pub exact_duplicate_ratio: f64,
5031    /// Near duplicate proportion (slight variations)
5032    #[serde(default = "default_near_duplicate_ratio")]
5033    pub near_duplicate_ratio: f64,
5034    /// Fuzzy duplicate proportion (typos in key fields)
5035    #[serde(default = "default_fuzzy_duplicate_ratio")]
5036    pub fuzzy_duplicate_ratio: f64,
5037    /// Maximum date offset for near/fuzzy duplicates (days)
5038    #[serde(default = "default_max_date_offset")]
5039    pub max_date_offset_days: u32,
5040    /// Maximum amount variance for near duplicates (fraction)
5041    #[serde(default = "default_max_amount_variance")]
5042    pub max_amount_variance: f64,
5043}
5044
5045fn default_duplicate_rate() -> f64 {
5046    0.005
5047}
5048fn default_exact_duplicate_ratio() -> f64 {
5049    0.4
5050}
5051fn default_near_duplicate_ratio() -> f64 {
5052    0.35
5053}
5054fn default_fuzzy_duplicate_ratio() -> f64 {
5055    0.25
5056}
5057fn default_max_date_offset() -> u32 {
5058    3
5059}
5060fn default_max_amount_variance() -> f64 {
5061    0.01
5062}
5063
5064impl Default for DuplicateSchemaConfig {
5065    fn default() -> Self {
5066        Self {
5067            enabled: false,
5068            rate: default_duplicate_rate(),
5069            exact_duplicate_ratio: default_exact_duplicate_ratio(),
5070            near_duplicate_ratio: default_near_duplicate_ratio(),
5071            fuzzy_duplicate_ratio: default_fuzzy_duplicate_ratio(),
5072            max_date_offset_days: default_max_date_offset(),
5073            max_amount_variance: default_max_amount_variance(),
5074        }
5075    }
5076}
5077
5078/// Encoding issue configuration.
5079#[derive(Debug, Clone, Serialize, Deserialize)]
5080pub struct EncodingIssueSchemaConfig {
5081    /// Enable encoding issue injection
5082    #[serde(default)]
5083    pub enabled: bool,
5084    /// Overall encoding issue rate
5085    #[serde(default = "default_encoding_rate")]
5086    pub rate: f64,
5087    /// Include mojibake (UTF-8/Latin-1 confusion)
5088    #[serde(default)]
5089    pub mojibake: bool,
5090    /// Include HTML entity corruption
5091    #[serde(default)]
5092    pub html_entities: bool,
5093    /// Include BOM issues
5094    #[serde(default)]
5095    pub bom_issues: bool,
5096}
5097
5098fn default_encoding_rate() -> f64 {
5099    0.001
5100}
5101
5102impl Default for EncodingIssueSchemaConfig {
5103    fn default() -> Self {
5104        Self {
5105            enabled: false,
5106            rate: default_encoding_rate(),
5107            mojibake: false,
5108            html_entities: false,
5109            bom_issues: false,
5110        }
5111    }
5112}
5113
5114/// Per-sink quality profiles for different output formats.
5115#[derive(Debug, Clone, Serialize, Deserialize, Default)]
5116pub struct SinkQualityProfiles {
5117    /// CSV-specific quality settings
5118    #[serde(default)]
5119    pub csv: Option<SinkQualityOverride>,
5120    /// JSON-specific quality settings
5121    #[serde(default)]
5122    pub json: Option<SinkQualityOverride>,
5123    /// Parquet-specific quality settings
5124    #[serde(default)]
5125    pub parquet: Option<SinkQualityOverride>,
5126}
5127
5128/// Quality setting overrides for a specific sink type.
5129#[derive(Debug, Clone, Serialize, Deserialize)]
5130pub struct SinkQualityOverride {
5131    /// Override enabled state
5132    pub enabled: Option<bool>,
5133    /// Override missing value rate
5134    pub missing_rate: Option<f64>,
5135    /// Override typo rate
5136    pub typo_rate: Option<f64>,
5137    /// Override format variation rate
5138    pub format_variation_rate: Option<f64>,
5139    /// Override duplicate rate
5140    pub duplicate_rate: Option<f64>,
5141}
5142
5143// =============================================================================
5144// Accounting Standards Configuration
5145// =============================================================================
5146
5147/// Accounting standards framework configuration for generating standards-compliant data.
5148///
5149/// Supports US GAAP, IFRS, and French GAAP (PCG) frameworks with specific standards:
5150/// - ASC 606/IFRS 15/PCG: Revenue Recognition
5151/// - ASC 842/IFRS 16/PCG: Leases
5152/// - ASC 820/IFRS 13/PCG: Fair Value Measurement
5153/// - ASC 360/IAS 36/PCG: Impairment
5154#[derive(Debug, Clone, Serialize, Deserialize, Default)]
5155pub struct AccountingStandardsConfig {
5156    /// Enable accounting standards generation
5157    #[serde(default)]
5158    pub enabled: bool,
5159
5160    /// Accounting framework to use.
5161    /// When `None`, the country pack's `accounting.framework` is used as fallback;
5162    /// if that is also absent the orchestrator defaults to US GAAP.
5163    #[serde(default, skip_serializing_if = "Option::is_none")]
5164    pub framework: Option<AccountingFrameworkConfig>,
5165
5166    /// Revenue recognition configuration (ASC 606/IFRS 15)
5167    #[serde(default)]
5168    pub revenue_recognition: RevenueRecognitionConfig,
5169
5170    /// Lease accounting configuration (ASC 842/IFRS 16)
5171    #[serde(default)]
5172    pub leases: LeaseAccountingConfig,
5173
5174    /// Fair value measurement configuration (ASC 820/IFRS 13)
5175    #[serde(default)]
5176    pub fair_value: FairValueConfig,
5177
5178    /// Impairment testing configuration (ASC 360/IAS 36)
5179    #[serde(default)]
5180    pub impairment: ImpairmentConfig,
5181
5182    /// Business combination configuration (IFRS 3 / ASC 805)
5183    #[serde(default)]
5184    pub business_combinations: BusinessCombinationsConfig,
5185
5186    /// Expected Credit Loss configuration (IFRS 9 / ASC 326)
5187    #[serde(default)]
5188    pub expected_credit_loss: EclConfig,
5189
5190    /// Generate framework differences for dual reporting
5191    #[serde(default)]
5192    pub generate_differences: bool,
5193}
5194
5195/// Accounting framework selection.
5196#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
5197#[serde(rename_all = "snake_case")]
5198pub enum AccountingFrameworkConfig {
5199    /// US Generally Accepted Accounting Principles
5200    #[default]
5201    UsGaap,
5202    /// International Financial Reporting Standards
5203    Ifrs,
5204    /// Generate data for both frameworks with reconciliation
5205    DualReporting,
5206    /// French GAAP (Plan Comptable Général – PCG)
5207    FrenchGaap,
5208    /// German GAAP (Handelsgesetzbuch – HGB, §238-263)
5209    GermanGaap,
5210}
5211
5212/// Revenue recognition configuration (ASC 606/IFRS 15).
5213#[derive(Debug, Clone, Serialize, Deserialize)]
5214pub struct RevenueRecognitionConfig {
5215    /// Enable revenue recognition generation
5216    #[serde(default)]
5217    pub enabled: bool,
5218
5219    /// Generate customer contracts
5220    #[serde(default = "default_true")]
5221    pub generate_contracts: bool,
5222
5223    /// Average number of performance obligations per contract
5224    #[serde(default = "default_avg_obligations")]
5225    pub avg_obligations_per_contract: f64,
5226
5227    /// Rate of contracts with variable consideration
5228    #[serde(default = "default_variable_consideration_rate")]
5229    pub variable_consideration_rate: f64,
5230
5231    /// Rate of over-time revenue recognition (vs point-in-time)
5232    #[serde(default = "default_over_time_rate")]
5233    pub over_time_recognition_rate: f64,
5234
5235    /// Number of contracts to generate
5236    #[serde(default = "default_contract_count")]
5237    pub contract_count: usize,
5238}
5239
5240fn default_avg_obligations() -> f64 {
5241    2.0
5242}
5243
5244fn default_variable_consideration_rate() -> f64 {
5245    0.15
5246}
5247
5248fn default_over_time_rate() -> f64 {
5249    0.30
5250}
5251
5252fn default_contract_count() -> usize {
5253    100
5254}
5255
5256impl Default for RevenueRecognitionConfig {
5257    fn default() -> Self {
5258        Self {
5259            enabled: false,
5260            generate_contracts: true,
5261            avg_obligations_per_contract: default_avg_obligations(),
5262            variable_consideration_rate: default_variable_consideration_rate(),
5263            over_time_recognition_rate: default_over_time_rate(),
5264            contract_count: default_contract_count(),
5265        }
5266    }
5267}
5268
5269/// Lease accounting configuration (ASC 842/IFRS 16).
5270#[derive(Debug, Clone, Serialize, Deserialize)]
5271pub struct LeaseAccountingConfig {
5272    /// Enable lease accounting generation
5273    #[serde(default)]
5274    pub enabled: bool,
5275
5276    /// Number of leases to generate
5277    #[serde(default = "default_lease_count")]
5278    pub lease_count: usize,
5279
5280    /// Percentage of finance leases (vs operating)
5281    #[serde(default = "default_finance_lease_pct")]
5282    pub finance_lease_percent: f64,
5283
5284    /// Average lease term in months
5285    #[serde(default = "default_avg_lease_term")]
5286    pub avg_lease_term_months: u32,
5287
5288    /// Generate amortization schedules
5289    #[serde(default = "default_true")]
5290    pub generate_amortization: bool,
5291
5292    /// Real estate lease percentage
5293    #[serde(default = "default_real_estate_pct")]
5294    pub real_estate_percent: f64,
5295}
5296
5297fn default_lease_count() -> usize {
5298    50
5299}
5300
5301fn default_finance_lease_pct() -> f64 {
5302    0.30
5303}
5304
5305fn default_avg_lease_term() -> u32 {
5306    60
5307}
5308
5309fn default_real_estate_pct() -> f64 {
5310    0.40
5311}
5312
5313impl Default for LeaseAccountingConfig {
5314    fn default() -> Self {
5315        Self {
5316            enabled: false,
5317            lease_count: default_lease_count(),
5318            finance_lease_percent: default_finance_lease_pct(),
5319            avg_lease_term_months: default_avg_lease_term(),
5320            generate_amortization: true,
5321            real_estate_percent: default_real_estate_pct(),
5322        }
5323    }
5324}
5325
5326/// Fair value measurement configuration (ASC 820/IFRS 13).
5327#[derive(Debug, Clone, Serialize, Deserialize)]
5328pub struct FairValueConfig {
5329    /// Enable fair value measurement generation
5330    #[serde(default)]
5331    pub enabled: bool,
5332
5333    /// Number of fair value measurements to generate
5334    #[serde(default = "default_fv_count")]
5335    pub measurement_count: usize,
5336
5337    /// Level 1 (quoted prices) percentage
5338    #[serde(default = "default_level1_pct")]
5339    pub level1_percent: f64,
5340
5341    /// Level 2 (observable inputs) percentage
5342    #[serde(default = "default_level2_pct")]
5343    pub level2_percent: f64,
5344
5345    /// Level 3 (unobservable inputs) percentage
5346    #[serde(default = "default_level3_pct")]
5347    pub level3_percent: f64,
5348
5349    /// Include sensitivity analysis for Level 3
5350    #[serde(default)]
5351    pub include_sensitivity_analysis: bool,
5352}
5353
5354fn default_fv_count() -> usize {
5355    25
5356}
5357
5358fn default_level1_pct() -> f64 {
5359    0.40
5360}
5361
5362fn default_level2_pct() -> f64 {
5363    0.35
5364}
5365
5366fn default_level3_pct() -> f64 {
5367    0.25
5368}
5369
5370impl Default for FairValueConfig {
5371    fn default() -> Self {
5372        Self {
5373            enabled: false,
5374            measurement_count: default_fv_count(),
5375            level1_percent: default_level1_pct(),
5376            level2_percent: default_level2_pct(),
5377            level3_percent: default_level3_pct(),
5378            include_sensitivity_analysis: false,
5379        }
5380    }
5381}
5382
5383/// Impairment testing configuration (ASC 360/IAS 36).
5384#[derive(Debug, Clone, Serialize, Deserialize)]
5385pub struct ImpairmentConfig {
5386    /// Enable impairment testing generation
5387    #[serde(default)]
5388    pub enabled: bool,
5389
5390    /// Number of impairment tests to generate
5391    #[serde(default = "default_impairment_count")]
5392    pub test_count: usize,
5393
5394    /// Rate of tests resulting in impairment
5395    #[serde(default = "default_impairment_rate")]
5396    pub impairment_rate: f64,
5397
5398    /// Generate cash flow projections
5399    #[serde(default = "default_true")]
5400    pub generate_projections: bool,
5401
5402    /// Include goodwill impairment tests
5403    #[serde(default)]
5404    pub include_goodwill: bool,
5405}
5406
5407fn default_impairment_count() -> usize {
5408    15
5409}
5410
5411fn default_impairment_rate() -> f64 {
5412    0.10
5413}
5414
5415impl Default for ImpairmentConfig {
5416    fn default() -> Self {
5417        Self {
5418            enabled: false,
5419            test_count: default_impairment_count(),
5420            impairment_rate: default_impairment_rate(),
5421            generate_projections: true,
5422            include_goodwill: false,
5423        }
5424    }
5425}
5426
5427// =============================================================================
5428// Business Combinations Configuration (IFRS 3 / ASC 805)
5429// =============================================================================
5430
5431/// Configuration for generating business combination (acquisition) data.
5432#[derive(Debug, Clone, Serialize, Deserialize)]
5433pub struct BusinessCombinationsConfig {
5434    /// Enable business combination generation
5435    #[serde(default)]
5436    pub enabled: bool,
5437
5438    /// Number of acquisitions to generate per company (1-5)
5439    #[serde(default = "default_bc_acquisition_count")]
5440    pub acquisition_count: usize,
5441}
5442
5443fn default_bc_acquisition_count() -> usize {
5444    2
5445}
5446
5447impl Default for BusinessCombinationsConfig {
5448    fn default() -> Self {
5449        Self {
5450            enabled: false,
5451            acquisition_count: default_bc_acquisition_count(),
5452        }
5453    }
5454}
5455
5456// =============================================================================
5457// ECL Configuration (IFRS 9 / ASC 326)
5458// =============================================================================
5459
5460/// Configuration for Expected Credit Loss generation.
5461#[derive(Debug, Clone, Serialize, Deserialize)]
5462pub struct EclConfig {
5463    /// Enable ECL generation.
5464    #[serde(default)]
5465    pub enabled: bool,
5466
5467    /// Weight for base economic scenario (0–1).
5468    #[serde(default = "default_ecl_base_weight")]
5469    pub base_scenario_weight: f64,
5470
5471    /// Multiplier for base scenario (typically 1.0).
5472    #[serde(default = "default_ecl_base_multiplier")]
5473    pub base_scenario_multiplier: f64,
5474
5475    /// Weight for optimistic economic scenario (0–1).
5476    #[serde(default = "default_ecl_optimistic_weight")]
5477    pub optimistic_scenario_weight: f64,
5478
5479    /// Multiplier for optimistic scenario (< 1.0 means lower losses).
5480    #[serde(default = "default_ecl_optimistic_multiplier")]
5481    pub optimistic_scenario_multiplier: f64,
5482
5483    /// Weight for pessimistic economic scenario (0–1).
5484    #[serde(default = "default_ecl_pessimistic_weight")]
5485    pub pessimistic_scenario_weight: f64,
5486
5487    /// Multiplier for pessimistic scenario (> 1.0 means higher losses).
5488    #[serde(default = "default_ecl_pessimistic_multiplier")]
5489    pub pessimistic_scenario_multiplier: f64,
5490}
5491
5492fn default_ecl_base_weight() -> f64 {
5493    0.50
5494}
5495fn default_ecl_base_multiplier() -> f64 {
5496    1.0
5497}
5498fn default_ecl_optimistic_weight() -> f64 {
5499    0.30
5500}
5501fn default_ecl_optimistic_multiplier() -> f64 {
5502    0.8
5503}
5504fn default_ecl_pessimistic_weight() -> f64 {
5505    0.20
5506}
5507fn default_ecl_pessimistic_multiplier() -> f64 {
5508    1.4
5509}
5510
5511impl Default for EclConfig {
5512    fn default() -> Self {
5513        Self {
5514            enabled: false,
5515            base_scenario_weight: default_ecl_base_weight(),
5516            base_scenario_multiplier: default_ecl_base_multiplier(),
5517            optimistic_scenario_weight: default_ecl_optimistic_weight(),
5518            optimistic_scenario_multiplier: default_ecl_optimistic_multiplier(),
5519            pessimistic_scenario_weight: default_ecl_pessimistic_weight(),
5520            pessimistic_scenario_multiplier: default_ecl_pessimistic_multiplier(),
5521        }
5522    }
5523}
5524
5525// =============================================================================
5526// Audit Standards Configuration
5527// =============================================================================
5528
5529/// Audit standards framework configuration for generating standards-compliant audit data.
5530///
5531/// Supports ISA (International Standards on Auditing) and PCAOB standards:
5532/// - ISA 200-720: Complete coverage of audit standards
5533/// - ISA 520: Analytical Procedures
5534/// - ISA 505: External Confirmations
5535/// - ISA 700/705/706/701: Audit Reports
5536/// - PCAOB AS 2201: ICFR Auditing
5537#[derive(Debug, Clone, Serialize, Deserialize, Default)]
5538pub struct AuditStandardsConfig {
5539    /// Enable audit standards generation
5540    #[serde(default)]
5541    pub enabled: bool,
5542
5543    /// ISA compliance configuration
5544    #[serde(default)]
5545    pub isa_compliance: IsaComplianceConfig,
5546
5547    /// Analytical procedures configuration (ISA 520)
5548    #[serde(default)]
5549    pub analytical_procedures: AnalyticalProceduresConfig,
5550
5551    /// External confirmations configuration (ISA 505)
5552    #[serde(default)]
5553    pub confirmations: ConfirmationsConfig,
5554
5555    /// Audit opinion configuration (ISA 700/705/706/701)
5556    #[serde(default)]
5557    pub opinion: AuditOpinionConfig,
5558
5559    /// Generate complete audit trail with traceability
5560    #[serde(default)]
5561    pub generate_audit_trail: bool,
5562
5563    /// SOX 302/404 compliance configuration
5564    #[serde(default)]
5565    pub sox: SoxComplianceConfig,
5566
5567    /// PCAOB-specific configuration
5568    #[serde(default)]
5569    pub pcaob: PcaobConfig,
5570}
5571
5572/// ISA compliance level configuration.
5573#[derive(Debug, Clone, Serialize, Deserialize)]
5574pub struct IsaComplianceConfig {
5575    /// Enable ISA compliance tracking
5576    #[serde(default)]
5577    pub enabled: bool,
5578
5579    /// Compliance level: "basic", "standard", "comprehensive"
5580    #[serde(default = "default_compliance_level")]
5581    pub compliance_level: String,
5582
5583    /// Generate ISA requirement mappings
5584    #[serde(default = "default_true")]
5585    pub generate_isa_mappings: bool,
5586
5587    /// Generate ISA coverage summary
5588    #[serde(default = "default_true")]
5589    pub generate_coverage_summary: bool,
5590
5591    /// Include PCAOB standard mappings (for dual framework)
5592    #[serde(default)]
5593    pub include_pcaob: bool,
5594
5595    /// Framework to use: "isa", "pcaob", "dual"
5596    #[serde(default = "default_audit_framework")]
5597    pub framework: String,
5598}
5599
5600fn default_compliance_level() -> String {
5601    "standard".to_string()
5602}
5603
5604fn default_audit_framework() -> String {
5605    "isa".to_string()
5606}
5607
5608impl Default for IsaComplianceConfig {
5609    fn default() -> Self {
5610        Self {
5611            enabled: false,
5612            compliance_level: default_compliance_level(),
5613            generate_isa_mappings: true,
5614            generate_coverage_summary: true,
5615            include_pcaob: false,
5616            framework: default_audit_framework(),
5617        }
5618    }
5619}
5620
5621/// Analytical procedures configuration (ISA 520).
5622#[derive(Debug, Clone, Serialize, Deserialize)]
5623pub struct AnalyticalProceduresConfig {
5624    /// Enable analytical procedures generation
5625    #[serde(default)]
5626    pub enabled: bool,
5627
5628    /// Number of procedures per account/area
5629    #[serde(default = "default_procedures_per_account")]
5630    pub procedures_per_account: usize,
5631
5632    /// Probability of variance exceeding threshold
5633    #[serde(default = "default_variance_probability")]
5634    pub variance_probability: f64,
5635
5636    /// Include variance investigations
5637    #[serde(default = "default_true")]
5638    pub generate_investigations: bool,
5639
5640    /// Include financial ratio analysis
5641    #[serde(default = "default_true")]
5642    pub include_ratio_analysis: bool,
5643}
5644
5645fn default_procedures_per_account() -> usize {
5646    3
5647}
5648
5649fn default_variance_probability() -> f64 {
5650    0.20
5651}
5652
5653impl Default for AnalyticalProceduresConfig {
5654    fn default() -> Self {
5655        Self {
5656            enabled: false,
5657            procedures_per_account: default_procedures_per_account(),
5658            variance_probability: default_variance_probability(),
5659            generate_investigations: true,
5660            include_ratio_analysis: true,
5661        }
5662    }
5663}
5664
5665/// External confirmations configuration (ISA 505).
5666#[derive(Debug, Clone, Serialize, Deserialize)]
5667pub struct ConfirmationsConfig {
5668    /// Enable confirmation generation
5669    #[serde(default)]
5670    pub enabled: bool,
5671
5672    /// Number of confirmations to generate
5673    #[serde(default = "default_confirmation_count")]
5674    pub confirmation_count: usize,
5675
5676    /// Positive response rate
5677    #[serde(default = "default_positive_response_rate")]
5678    pub positive_response_rate: f64,
5679
5680    /// Exception rate (responses with differences)
5681    #[serde(default = "default_exception_rate_confirm")]
5682    pub exception_rate: f64,
5683
5684    /// Non-response rate
5685    #[serde(default = "default_non_response_rate")]
5686    pub non_response_rate: f64,
5687
5688    /// Generate alternative procedures for non-responses
5689    #[serde(default = "default_true")]
5690    pub generate_alternative_procedures: bool,
5691}
5692
5693fn default_confirmation_count() -> usize {
5694    50
5695}
5696
5697fn default_positive_response_rate() -> f64 {
5698    0.85
5699}
5700
5701fn default_exception_rate_confirm() -> f64 {
5702    0.10
5703}
5704
5705fn default_non_response_rate() -> f64 {
5706    0.05
5707}
5708
5709impl Default for ConfirmationsConfig {
5710    fn default() -> Self {
5711        Self {
5712            enabled: false,
5713            confirmation_count: default_confirmation_count(),
5714            positive_response_rate: default_positive_response_rate(),
5715            exception_rate: default_exception_rate_confirm(),
5716            non_response_rate: default_non_response_rate(),
5717            generate_alternative_procedures: true,
5718        }
5719    }
5720}
5721
5722/// Audit opinion configuration (ISA 700/705/706/701).
5723#[derive(Debug, Clone, Serialize, Deserialize)]
5724pub struct AuditOpinionConfig {
5725    /// Enable audit opinion generation
5726    #[serde(default)]
5727    pub enabled: bool,
5728
5729    /// Generate Key Audit Matters (KAM) / Critical Audit Matters (CAM)
5730    #[serde(default = "default_true")]
5731    pub generate_kam: bool,
5732
5733    /// Average number of KAMs/CAMs per opinion
5734    #[serde(default = "default_kam_count")]
5735    pub average_kam_count: usize,
5736
5737    /// Rate of modified opinions
5738    #[serde(default = "default_modified_opinion_rate")]
5739    pub modified_opinion_rate: f64,
5740
5741    /// Include emphasis of matter paragraphs
5742    #[serde(default)]
5743    pub include_emphasis_of_matter: bool,
5744
5745    /// Include going concern conclusions
5746    #[serde(default = "default_true")]
5747    pub include_going_concern: bool,
5748}
5749
5750fn default_kam_count() -> usize {
5751    3
5752}
5753
5754fn default_modified_opinion_rate() -> f64 {
5755    0.05
5756}
5757
5758impl Default for AuditOpinionConfig {
5759    fn default() -> Self {
5760        Self {
5761            enabled: false,
5762            generate_kam: true,
5763            average_kam_count: default_kam_count(),
5764            modified_opinion_rate: default_modified_opinion_rate(),
5765            include_emphasis_of_matter: false,
5766            include_going_concern: true,
5767        }
5768    }
5769}
5770
5771/// SOX compliance configuration (Sections 302/404).
5772#[derive(Debug, Clone, Serialize, Deserialize)]
5773pub struct SoxComplianceConfig {
5774    /// Enable SOX compliance generation
5775    #[serde(default)]
5776    pub enabled: bool,
5777
5778    /// Generate Section 302 CEO/CFO certifications
5779    #[serde(default = "default_true")]
5780    pub generate_302_certifications: bool,
5781
5782    /// Generate Section 404 ICFR assessments
5783    #[serde(default = "default_true")]
5784    pub generate_404_assessments: bool,
5785
5786    /// Materiality threshold for SOX testing
5787    #[serde(default = "default_sox_materiality_threshold")]
5788    pub materiality_threshold: f64,
5789
5790    /// Rate of material weaknesses
5791    #[serde(default = "default_material_weakness_rate")]
5792    pub material_weakness_rate: f64,
5793
5794    /// Rate of significant deficiencies
5795    #[serde(default = "default_significant_deficiency_rate")]
5796    pub significant_deficiency_rate: f64,
5797}
5798
5799fn default_material_weakness_rate() -> f64 {
5800    0.02
5801}
5802
5803fn default_significant_deficiency_rate() -> f64 {
5804    0.08
5805}
5806
5807impl Default for SoxComplianceConfig {
5808    fn default() -> Self {
5809        Self {
5810            enabled: false,
5811            generate_302_certifications: true,
5812            generate_404_assessments: true,
5813            materiality_threshold: default_sox_materiality_threshold(),
5814            material_weakness_rate: default_material_weakness_rate(),
5815            significant_deficiency_rate: default_significant_deficiency_rate(),
5816        }
5817    }
5818}
5819
5820/// PCAOB-specific configuration.
5821#[derive(Debug, Clone, Serialize, Deserialize)]
5822pub struct PcaobConfig {
5823    /// Enable PCAOB-specific elements
5824    #[serde(default)]
5825    pub enabled: bool,
5826
5827    /// Treat as PCAOB audit (vs ISA-only)
5828    #[serde(default)]
5829    pub is_pcaob_audit: bool,
5830
5831    /// Generate Critical Audit Matters (CAM)
5832    #[serde(default = "default_true")]
5833    pub generate_cam: bool,
5834
5835    /// Include ICFR opinion (for integrated audits)
5836    #[serde(default)]
5837    pub include_icfr_opinion: bool,
5838
5839    /// Generate PCAOB-ISA standard mappings
5840    #[serde(default)]
5841    pub generate_standard_mappings: bool,
5842}
5843
5844impl Default for PcaobConfig {
5845    fn default() -> Self {
5846        Self {
5847            enabled: false,
5848            is_pcaob_audit: false,
5849            generate_cam: true,
5850            include_icfr_opinion: false,
5851            generate_standard_mappings: false,
5852        }
5853    }
5854}
5855
5856// =============================================================================
5857// Advanced Distribution Configuration
5858// =============================================================================
5859
5860/// Advanced distribution configuration for realistic data generation.
5861///
5862/// This section enables sophisticated distribution models including:
5863/// - Mixture models (multi-modal distributions)
5864/// - Cross-field correlations
5865/// - Conditional distributions
5866/// - Regime changes and economic cycles
5867/// - Statistical validation
5868#[derive(Debug, Clone, Serialize, Deserialize, Default)]
5869pub struct AdvancedDistributionConfig {
5870    /// Enable advanced distribution features.
5871    #[serde(default)]
5872    pub enabled: bool,
5873
5874    /// Mixture model configuration for amounts.
5875    #[serde(default)]
5876    pub amounts: MixtureDistributionSchemaConfig,
5877
5878    /// Cross-field correlation configuration.
5879    #[serde(default)]
5880    pub correlations: CorrelationSchemaConfig,
5881
5882    /// Conditional distribution configurations.
5883    #[serde(default)]
5884    pub conditional: Vec<ConditionalDistributionSchemaConfig>,
5885
5886    /// Regime change configuration.
5887    #[serde(default)]
5888    pub regime_changes: RegimeChangeSchemaConfig,
5889
5890    /// Industry-specific distribution profile.
5891    #[serde(default)]
5892    pub industry_profile: Option<IndustryProfileType>,
5893
5894    /// Statistical validation configuration.
5895    #[serde(default)]
5896    pub validation: StatisticalValidationSchemaConfig,
5897
5898    /// v3.4.4+ — Pareto heavy-tailed distribution for monetary amounts.
5899    /// When set and `enabled`, overrides `amounts` mixture model for the
5900    /// non-fraud amount-sampling path (fraud patterns remain orthogonal).
5901    /// Useful for capex, strategic contracts, and any domain where a small
5902    /// number of very large values dominates the tail.
5903    #[serde(default)]
5904    pub pareto: Option<ParetoSchemaConfig>,
5905}
5906
5907/// Schema-level Pareto distribution configuration (v3.4.4+).
5908///
5909/// Thin wrapper around `datasynth_core::distributions::ParetoConfig` that
5910/// adds an `enabled` gate and serde-friendly field names.
5911#[derive(Debug, Clone, Serialize, Deserialize)]
5912pub struct ParetoSchemaConfig {
5913    /// Enable Pareto sampling. When true, replaces the `amounts` mixture
5914    /// model for the non-fraud amount-sampling path.
5915    #[serde(default)]
5916    pub enabled: bool,
5917
5918    /// Shape parameter (tail heaviness). Lower values → heavier tail.
5919    /// Typical range: 1.5-3.0. Default: 2.0.
5920    #[serde(default = "default_pareto_alpha")]
5921    pub alpha: f64,
5922
5923    /// Scale / minimum value. All samples are >= x_min.
5924    /// Typical: 1000 (for capex) to 100,000 (for large contracts). Default: 100.
5925    #[serde(default = "default_pareto_x_min")]
5926    pub x_min: f64,
5927
5928    /// Optional upper clamp. `None` = unbounded (recommended for realistic
5929    /// heavy tails).
5930    #[serde(default)]
5931    pub max_value: Option<f64>,
5932
5933    /// Decimal places for rounding. Default: 2.
5934    #[serde(default = "default_pareto_decimal_places")]
5935    pub decimal_places: u8,
5936}
5937
5938fn default_pareto_alpha() -> f64 {
5939    2.0
5940}
5941
5942fn default_pareto_x_min() -> f64 {
5943    100.0
5944}
5945
5946fn default_pareto_decimal_places() -> u8 {
5947    2
5948}
5949
5950impl Default for ParetoSchemaConfig {
5951    fn default() -> Self {
5952        Self {
5953            enabled: false,
5954            alpha: default_pareto_alpha(),
5955            x_min: default_pareto_x_min(),
5956            max_value: None,
5957            decimal_places: default_pareto_decimal_places(),
5958        }
5959    }
5960}
5961
5962impl ParetoSchemaConfig {
5963    /// Convert this schema config into a `datasynth_core::distributions::ParetoConfig`.
5964    pub fn to_core_config(&self) -> datasynth_core::distributions::ParetoConfig {
5965        datasynth_core::distributions::ParetoConfig {
5966            alpha: self.alpha,
5967            x_min: self.x_min,
5968            max_value: self.max_value,
5969            decimal_places: self.decimal_places,
5970        }
5971    }
5972}
5973
5974/// Industry profile types for pre-configured distribution settings.
5975#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
5976#[serde(rename_all = "snake_case")]
5977pub enum IndustryProfileType {
5978    /// Retail industry profile (POS sales, inventory, seasonal)
5979    Retail,
5980    /// Manufacturing industry profile (raw materials, maintenance, capital)
5981    Manufacturing,
5982    /// Financial services profile (wire transfers, ACH, fee income)
5983    FinancialServices,
5984    /// Healthcare profile (claims, procedures, supplies)
5985    Healthcare,
5986    /// Technology profile (subscriptions, services, R&D)
5987    Technology,
5988}
5989
5990/// Mixture model distribution configuration.
5991#[derive(Debug, Clone, Serialize, Deserialize)]
5992pub struct MixtureDistributionSchemaConfig {
5993    /// Enable mixture model for amount generation.
5994    #[serde(default)]
5995    pub enabled: bool,
5996
5997    /// Distribution type: "gaussian" or "lognormal".
5998    #[serde(default = "default_mixture_type")]
5999    pub distribution_type: MixtureDistributionType,
6000
6001    /// Mixture components with weights.
6002    #[serde(default)]
6003    pub components: Vec<MixtureComponentConfig>,
6004
6005    /// Minimum value constraint.
6006    #[serde(default = "default_min_amount")]
6007    pub min_value: f64,
6008
6009    /// Maximum value constraint (optional).
6010    #[serde(default)]
6011    pub max_value: Option<f64>,
6012
6013    /// Decimal places for rounding.
6014    #[serde(default = "default_decimal_places")]
6015    pub decimal_places: u8,
6016}
6017
6018fn default_mixture_type() -> MixtureDistributionType {
6019    MixtureDistributionType::LogNormal
6020}
6021
6022fn default_min_amount() -> f64 {
6023    0.01
6024}
6025
6026fn default_decimal_places() -> u8 {
6027    2
6028}
6029
6030impl Default for MixtureDistributionSchemaConfig {
6031    fn default() -> Self {
6032        Self {
6033            enabled: false,
6034            distribution_type: MixtureDistributionType::LogNormal,
6035            components: Vec::new(),
6036            min_value: 0.01,
6037            max_value: None,
6038            decimal_places: 2,
6039        }
6040    }
6041}
6042
6043impl MixtureDistributionSchemaConfig {
6044    /// Convert this schema-level config into a [`LogNormalMixtureConfig`]
6045    /// suitable for `LogNormalMixtureSampler::new`. Returns `None` if there
6046    /// are no components (schema default is an empty list, which cannot
6047    /// drive a sampler).
6048    ///
6049    /// Callers should gate this with `self.enabled` before invoking.
6050    pub fn to_log_normal_config(
6051        &self,
6052    ) -> Option<datasynth_core::distributions::LogNormalMixtureConfig> {
6053        if self.components.is_empty() {
6054            return None;
6055        }
6056        Some(datasynth_core::distributions::LogNormalMixtureConfig {
6057            components: self
6058                .components
6059                .iter()
6060                .map(|c| match &c.label {
6061                    Some(lbl) => datasynth_core::distributions::LogNormalComponent::with_label(
6062                        c.weight,
6063                        c.mu,
6064                        c.sigma,
6065                        lbl.clone(),
6066                    ),
6067                    None => datasynth_core::distributions::LogNormalComponent::new(
6068                        c.weight, c.mu, c.sigma,
6069                    ),
6070                })
6071                .collect(),
6072            min_value: self.min_value,
6073            max_value: self.max_value,
6074            decimal_places: self.decimal_places,
6075        })
6076    }
6077
6078    /// Convert this schema-level config into a [`GaussianMixtureConfig`].
6079    /// Returns `None` if there are no components.
6080    pub fn to_gaussian_config(
6081        &self,
6082    ) -> Option<datasynth_core::distributions::GaussianMixtureConfig> {
6083        if self.components.is_empty() {
6084            return None;
6085        }
6086        Some(datasynth_core::distributions::GaussianMixtureConfig {
6087            components: self
6088                .components
6089                .iter()
6090                .map(|c| {
6091                    datasynth_core::distributions::GaussianComponent::new(c.weight, c.mu, c.sigma)
6092                })
6093                .collect(),
6094            allow_negative: true,
6095            min_value: Some(self.min_value),
6096            max_value: self.max_value,
6097        })
6098    }
6099}
6100
6101/// Mixture distribution type.
6102#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
6103#[serde(rename_all = "snake_case")]
6104pub enum MixtureDistributionType {
6105    /// Gaussian (normal) mixture
6106    Gaussian,
6107    /// Log-normal mixture (for positive amounts)
6108    #[default]
6109    LogNormal,
6110}
6111
6112/// Configuration for a single mixture component.
6113#[derive(Debug, Clone, Serialize, Deserialize)]
6114pub struct MixtureComponentConfig {
6115    /// Weight of this component (must sum to 1.0 across all components).
6116    pub weight: f64,
6117
6118    /// Location parameter (mean for Gaussian, mu for log-normal).
6119    pub mu: f64,
6120
6121    /// Scale parameter (std dev for Gaussian, sigma for log-normal).
6122    pub sigma: f64,
6123
6124    /// Optional label for this component (e.g., "routine", "significant", "major").
6125    #[serde(default)]
6126    pub label: Option<String>,
6127}
6128
6129/// Cross-field correlation configuration.
6130#[derive(Debug, Clone, Serialize, Deserialize)]
6131pub struct CorrelationSchemaConfig {
6132    /// Enable correlation modeling.
6133    #[serde(default)]
6134    pub enabled: bool,
6135
6136    /// Copula type for dependency modeling.
6137    #[serde(default)]
6138    pub copula_type: CopulaSchemaType,
6139
6140    /// Field definitions for correlation.
6141    #[serde(default)]
6142    pub fields: Vec<CorrelatedFieldConfig>,
6143
6144    /// Correlation matrix (upper triangular, row-major).
6145    /// For n fields, this should have n*(n-1)/2 values.
6146    #[serde(default)]
6147    pub matrix: Vec<f64>,
6148
6149    /// Expected correlations for validation.
6150    #[serde(default)]
6151    pub expected_correlations: Vec<ExpectedCorrelationConfig>,
6152}
6153
6154impl Default for CorrelationSchemaConfig {
6155    fn default() -> Self {
6156        Self {
6157            enabled: false,
6158            copula_type: CopulaSchemaType::Gaussian,
6159            fields: Vec::new(),
6160            matrix: Vec::new(),
6161            expected_correlations: Vec::new(),
6162        }
6163    }
6164}
6165
6166impl CorrelationSchemaConfig {
6167    /// v3.5.4+: extract the correlation for a specific field pair from
6168    /// either the upper-triangular flat matrix (n*(n-1)/2 values) or a
6169    /// full symmetric n×n matrix (n*n values). Returns `None` when the
6170    /// named fields aren't both present or the matrix shape doesn't
6171    /// match.
6172    pub fn correlation_between(&self, field_a: &str, field_b: &str) -> Option<f64> {
6173        let idx_a = self.fields.iter().position(|f| f.name == field_a)?;
6174        let idx_b = self.fields.iter().position(|f| f.name == field_b)?;
6175        if idx_a == idx_b {
6176            return Some(1.0);
6177        }
6178        let (i, j) = if idx_a < idx_b {
6179            (idx_a, idx_b)
6180        } else {
6181            (idx_b, idx_a)
6182        };
6183        let n = self.fields.len();
6184        // Full n×n symmetric matrix?
6185        if self.matrix.len() == n * n {
6186            return self.matrix.get(idx_a * n + idx_b).copied();
6187        }
6188        // Upper triangular flat (row-major, excluding diagonal)?
6189        let expected_tri = n * (n - 1) / 2;
6190        if self.matrix.len() == expected_tri {
6191            // Row i, col j where j > i: flat index is
6192            //   sum_{k=0..i}((n-1-k)) + (j - i - 1)
6193            // = i*(n-1) - i*(i-1)/2 + (j - i - 1)
6194            let flat = i * (n - 1) - i * (i.saturating_sub(1)) / 2 + (j - i - 1);
6195            return self.matrix.get(flat).copied();
6196        }
6197        None
6198    }
6199
6200    /// Convert this schema config to a core `CopulaConfig` when the
6201    /// declared field pair `(field_a, field_b)` has a valid correlation
6202    /// entry. Returns `None` when disabled, fields missing, or matrix
6203    /// malformed.
6204    pub fn to_core_config_for_pair(
6205        &self,
6206        field_a: &str,
6207        field_b: &str,
6208    ) -> Option<datasynth_core::distributions::CopulaConfig> {
6209        if !self.enabled {
6210            return None;
6211        }
6212        let rho = self.correlation_between(field_a, field_b)?;
6213        use datasynth_core::distributions::{CopulaConfig, CopulaType};
6214        let copula_type = match self.copula_type {
6215            CopulaSchemaType::Gaussian => CopulaType::Gaussian,
6216            CopulaSchemaType::Clayton => CopulaType::Clayton,
6217            CopulaSchemaType::Gumbel => CopulaType::Gumbel,
6218            CopulaSchemaType::Frank => CopulaType::Frank,
6219            CopulaSchemaType::StudentT => CopulaType::StudentT,
6220        };
6221        // Gaussian / StudentT interpret theta as correlation; others
6222        // as a shape parameter. Minimal v3.5.4 only wires Gaussian in
6223        // the runtime, but the converter is general so follow-ups can
6224        // light up the other copulas.
6225        let theta = rho.clamp(-0.999, 0.999);
6226        Some(CopulaConfig {
6227            copula_type,
6228            theta,
6229            degrees_of_freedom: 4.0,
6230        })
6231    }
6232}
6233
6234/// Copula type for dependency modeling.
6235#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
6236#[serde(rename_all = "snake_case")]
6237pub enum CopulaSchemaType {
6238    /// Gaussian copula (symmetric, no tail dependence)
6239    #[default]
6240    Gaussian,
6241    /// Clayton copula (lower tail dependence)
6242    Clayton,
6243    /// Gumbel copula (upper tail dependence)
6244    Gumbel,
6245    /// Frank copula (symmetric, no tail dependence)
6246    Frank,
6247    /// Student-t copula (both tail dependencies)
6248    StudentT,
6249}
6250
6251/// Configuration for a correlated field.
6252#[derive(Debug, Clone, Serialize, Deserialize)]
6253pub struct CorrelatedFieldConfig {
6254    /// Field name.
6255    pub name: String,
6256
6257    /// Marginal distribution type.
6258    #[serde(default)]
6259    pub distribution: MarginalDistributionConfig,
6260}
6261
6262/// Marginal distribution configuration.
6263#[derive(Debug, Clone, Serialize, Deserialize)]
6264#[serde(tag = "type", rename_all = "snake_case")]
6265pub enum MarginalDistributionConfig {
6266    /// Normal distribution.
6267    Normal {
6268        /// Mean
6269        mu: f64,
6270        /// Standard deviation
6271        sigma: f64,
6272    },
6273    /// Log-normal distribution.
6274    LogNormal {
6275        /// Location parameter
6276        mu: f64,
6277        /// Scale parameter
6278        sigma: f64,
6279    },
6280    /// Uniform distribution.
6281    Uniform {
6282        /// Minimum value
6283        min: f64,
6284        /// Maximum value
6285        max: f64,
6286    },
6287    /// Discrete uniform distribution.
6288    DiscreteUniform {
6289        /// Minimum integer value
6290        min: i32,
6291        /// Maximum integer value
6292        max: i32,
6293    },
6294}
6295
6296impl Default for MarginalDistributionConfig {
6297    fn default() -> Self {
6298        Self::Normal {
6299            mu: 0.0,
6300            sigma: 1.0,
6301        }
6302    }
6303}
6304
6305/// Expected correlation for validation.
6306#[derive(Debug, Clone, Serialize, Deserialize)]
6307pub struct ExpectedCorrelationConfig {
6308    /// First field name.
6309    pub field1: String,
6310    /// Second field name.
6311    pub field2: String,
6312    /// Expected correlation coefficient.
6313    pub expected_r: f64,
6314    /// Acceptable tolerance.
6315    #[serde(default = "default_correlation_tolerance")]
6316    pub tolerance: f64,
6317}
6318
6319fn default_correlation_tolerance() -> f64 {
6320    0.10
6321}
6322
6323/// Conditional distribution configuration.
6324#[derive(Debug, Clone, Serialize, Deserialize)]
6325pub struct ConditionalDistributionSchemaConfig {
6326    /// Output field name to generate.
6327    pub output_field: String,
6328
6329    /// Input field name that conditions the distribution.
6330    pub input_field: String,
6331
6332    /// Breakpoints defining distribution changes.
6333    #[serde(default)]
6334    pub breakpoints: Vec<ConditionalBreakpointConfig>,
6335
6336    /// Default distribution when below all breakpoints.
6337    #[serde(default)]
6338    pub default_distribution: ConditionalDistributionParamsConfig,
6339
6340    /// Minimum output value constraint.
6341    #[serde(default)]
6342    pub min_value: Option<f64>,
6343
6344    /// Maximum output value constraint.
6345    #[serde(default)]
6346    pub max_value: Option<f64>,
6347
6348    /// Decimal places for output rounding.
6349    #[serde(default = "default_decimal_places")]
6350    pub decimal_places: u8,
6351}
6352
6353/// Breakpoint for conditional distribution.
6354#[derive(Debug, Clone, Serialize, Deserialize)]
6355pub struct ConditionalBreakpointConfig {
6356    /// Input value threshold.
6357    pub threshold: f64,
6358
6359    /// Distribution to use when input >= threshold.
6360    pub distribution: ConditionalDistributionParamsConfig,
6361}
6362
6363impl ConditionalDistributionSchemaConfig {
6364    /// Convert this schema config into a core
6365    /// [`ConditionalDistributionConfig`] suitable for
6366    /// [`ConditionalSampler::new`]. v3.5.3+.
6367    pub fn to_core_config(&self) -> datasynth_core::distributions::ConditionalDistributionConfig {
6368        use datasynth_core::distributions::{
6369            Breakpoint, ConditionalDistributionConfig, ConditionalDistributionParams,
6370        };
6371
6372        let default_distribution = convert_conditional_params(&self.default_distribution);
6373        let breakpoints: Vec<Breakpoint> = self
6374            .breakpoints
6375            .iter()
6376            .map(|bp| Breakpoint {
6377                threshold: bp.threshold,
6378                distribution: convert_conditional_params(&bp.distribution),
6379            })
6380            .collect();
6381
6382        // Use a sentinel default_distribution when the schema default is
6383        // its factory default (Fixed { value: 0.0 })  and we have
6384        // breakpoints — we don't want to clobber data for values below
6385        // the first breakpoint.
6386        let final_default = if breakpoints.is_empty() {
6387            default_distribution
6388        } else {
6389            match default_distribution {
6390                ConditionalDistributionParams::Fixed { value: 0.0 } => {
6391                    // Reuse the first breakpoint's distribution as the
6392                    // default to avoid surprising zeros.
6393                    breakpoints[0].distribution.clone()
6394                }
6395                other => other,
6396            }
6397        };
6398
6399        ConditionalDistributionConfig {
6400            output_field: self.output_field.clone(),
6401            input_field: self.input_field.clone(),
6402            breakpoints,
6403            default_distribution: final_default,
6404            min_value: self.min_value,
6405            max_value: self.max_value,
6406            decimal_places: self.decimal_places,
6407        }
6408    }
6409}
6410
6411fn convert_conditional_params(
6412    p: &ConditionalDistributionParamsConfig,
6413) -> datasynth_core::distributions::ConditionalDistributionParams {
6414    use datasynth_core::distributions::ConditionalDistributionParams as Core;
6415    match p {
6416        ConditionalDistributionParamsConfig::Fixed { value } => Core::Fixed { value: *value },
6417        ConditionalDistributionParamsConfig::Normal { mu, sigma } => Core::Normal {
6418            mu: *mu,
6419            sigma: *sigma,
6420        },
6421        ConditionalDistributionParamsConfig::LogNormal { mu, sigma } => Core::LogNormal {
6422            mu: *mu,
6423            sigma: *sigma,
6424        },
6425        ConditionalDistributionParamsConfig::Uniform { min, max } => Core::Uniform {
6426            min: *min,
6427            max: *max,
6428        },
6429        ConditionalDistributionParamsConfig::Beta {
6430            alpha,
6431            beta,
6432            min,
6433            max,
6434        } => Core::Beta {
6435            alpha: *alpha,
6436            beta: *beta,
6437            min: *min,
6438            max: *max,
6439        },
6440        ConditionalDistributionParamsConfig::Discrete { values, weights } => Core::Discrete {
6441            values: values.clone(),
6442            weights: weights.clone(),
6443        },
6444    }
6445}
6446
6447/// Distribution parameters for conditional distributions.
6448#[derive(Debug, Clone, Serialize, Deserialize)]
6449#[serde(tag = "type", rename_all = "snake_case")]
6450pub enum ConditionalDistributionParamsConfig {
6451    /// Fixed value.
6452    Fixed {
6453        /// The fixed value
6454        value: f64,
6455    },
6456    /// Normal distribution.
6457    Normal {
6458        /// Mean
6459        mu: f64,
6460        /// Standard deviation
6461        sigma: f64,
6462    },
6463    /// Log-normal distribution.
6464    LogNormal {
6465        /// Location parameter
6466        mu: f64,
6467        /// Scale parameter
6468        sigma: f64,
6469    },
6470    /// Uniform distribution.
6471    Uniform {
6472        /// Minimum
6473        min: f64,
6474        /// Maximum
6475        max: f64,
6476    },
6477    /// Beta distribution (scaled).
6478    Beta {
6479        /// Alpha parameter
6480        alpha: f64,
6481        /// Beta parameter
6482        beta: f64,
6483        /// Minimum output value
6484        min: f64,
6485        /// Maximum output value
6486        max: f64,
6487    },
6488    /// Discrete values with weights.
6489    Discrete {
6490        /// Possible values
6491        values: Vec<f64>,
6492        /// Weights (should sum to 1.0)
6493        weights: Vec<f64>,
6494    },
6495}
6496
6497impl Default for ConditionalDistributionParamsConfig {
6498    fn default() -> Self {
6499        Self::Normal {
6500            mu: 0.0,
6501            sigma: 1.0,
6502        }
6503    }
6504}
6505
6506/// Regime change configuration.
6507#[derive(Debug, Clone, Serialize, Deserialize, Default)]
6508pub struct RegimeChangeSchemaConfig {
6509    /// Enable regime change modeling.
6510    #[serde(default)]
6511    pub enabled: bool,
6512
6513    /// List of regime changes.
6514    #[serde(default)]
6515    pub changes: Vec<RegimeChangeEventConfig>,
6516
6517    /// Economic cycle configuration.
6518    #[serde(default)]
6519    pub economic_cycle: Option<EconomicCycleSchemaConfig>,
6520
6521    /// Parameter drift configurations.
6522    #[serde(default)]
6523    pub parameter_drifts: Vec<ParameterDriftSchemaConfig>,
6524}
6525
6526/// A single regime change event.
6527#[derive(Debug, Clone, Serialize, Deserialize)]
6528pub struct RegimeChangeEventConfig {
6529    /// Date when the change occurs (ISO 8601 format).
6530    pub date: String,
6531
6532    /// Type of regime change.
6533    pub change_type: RegimeChangeTypeConfig,
6534
6535    /// Description of the change.
6536    #[serde(default)]
6537    pub description: Option<String>,
6538
6539    /// Effects of this regime change.
6540    #[serde(default)]
6541    pub effects: Vec<RegimeEffectConfig>,
6542}
6543
6544/// Type of regime change.
6545#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
6546#[serde(rename_all = "snake_case")]
6547pub enum RegimeChangeTypeConfig {
6548    /// Acquisition - sudden volume and amount increase
6549    Acquisition,
6550    /// Divestiture - sudden volume and amount decrease
6551    Divestiture,
6552    /// Price increase - amounts increase
6553    PriceIncrease,
6554    /// Price decrease - amounts decrease
6555    PriceDecrease,
6556    /// New product launch - volume ramp-up
6557    ProductLaunch,
6558    /// Product discontinuation - volume ramp-down
6559    ProductDiscontinuation,
6560    /// Policy change - affects patterns
6561    PolicyChange,
6562    /// Competitor entry - market disruption
6563    CompetitorEntry,
6564    /// Custom effect
6565    Custom,
6566}
6567
6568/// Effect of a regime change on a specific field.
6569#[derive(Debug, Clone, Serialize, Deserialize)]
6570pub struct RegimeEffectConfig {
6571    /// Field being affected.
6572    pub field: String,
6573
6574    /// Multiplier to apply (1.0 = no change, 1.5 = 50% increase).
6575    pub multiplier: f64,
6576}
6577
6578/// Economic cycle configuration.
6579#[derive(Debug, Clone, Serialize, Deserialize)]
6580pub struct EconomicCycleSchemaConfig {
6581    /// Enable economic cycle modeling.
6582    #[serde(default)]
6583    pub enabled: bool,
6584
6585    /// Cycle period in months (e.g., 48 for 4-year business cycle).
6586    #[serde(default = "default_cycle_period")]
6587    pub period_months: u32,
6588
6589    /// Amplitude of cycle effect (0.0-1.0).
6590    #[serde(default = "default_cycle_amplitude")]
6591    pub amplitude: f64,
6592
6593    /// Phase offset in months.
6594    #[serde(default)]
6595    pub phase_offset: u32,
6596
6597    /// Recession periods (start_month, duration_months).
6598    #[serde(default)]
6599    pub recessions: Vec<RecessionPeriodConfig>,
6600}
6601
6602fn default_cycle_period() -> u32 {
6603    48
6604}
6605
6606fn default_cycle_amplitude() -> f64 {
6607    0.15
6608}
6609
6610impl Default for EconomicCycleSchemaConfig {
6611    fn default() -> Self {
6612        Self {
6613            enabled: false,
6614            period_months: 48,
6615            amplitude: 0.15,
6616            phase_offset: 0,
6617            recessions: Vec::new(),
6618        }
6619    }
6620}
6621
6622/// Recession period configuration.
6623#[derive(Debug, Clone, Serialize, Deserialize)]
6624pub struct RecessionPeriodConfig {
6625    /// Start month (0-indexed from generation start).
6626    pub start_month: u32,
6627
6628    /// Duration in months.
6629    pub duration_months: u32,
6630
6631    /// Severity (0.0-1.0, affects volume reduction).
6632    #[serde(default = "default_recession_severity")]
6633    pub severity: f64,
6634}
6635
6636impl RegimeChangeSchemaConfig {
6637    /// Populate the regime-change, economic-cycle, and parameter-drift
6638    /// slots on a `DriftConfig` from this schema config. v3.5.2+.
6639    ///
6640    /// `generation_start` must match `config.global.start_date` so that
6641    /// absolute regime-change dates can be mapped to 0-indexed periods.
6642    /// Unparseable / out-of-range dates are silently skipped to keep
6643    /// runtime robust against user typos.
6644    pub fn apply_to(
6645        &self,
6646        drift: &mut datasynth_core::distributions::DriftConfig,
6647        generation_start: chrono::NaiveDate,
6648    ) {
6649        if !self.enabled {
6650            return;
6651        }
6652
6653        // Enable drift if any regime-change feature wants it.
6654        drift.enabled = true;
6655
6656        // Regime-change events (absolute dates → period offsets).
6657        for event in &self.changes {
6658            let period = match chrono::NaiveDate::parse_from_str(&event.date, "%Y-%m-%d") {
6659                Ok(d) => {
6660                    let days = (d - generation_start).num_days();
6661                    if days < 0 {
6662                        continue;
6663                    }
6664                    // Approximate month by dividing by 30.4 so we don't
6665                    // need chrono::Months arithmetic.
6666                    (days as f64 / 30.4).round() as u32
6667                }
6668                Err(_) => continue,
6669            };
6670            let change_type = convert_regime_change_type(event.change_type);
6671            let core_effects = event
6672                .effects
6673                .iter()
6674                .map(|e| datasynth_core::distributions::RegimeEffect {
6675                    field: e.field.clone(),
6676                    multiplier: e.multiplier,
6677                })
6678                .collect();
6679            drift
6680                .regime_changes
6681                .push(datasynth_core::distributions::RegimeChange {
6682                    period,
6683                    change_type,
6684                    description: event.description.clone(),
6685                    effects: core_effects,
6686                    transition_periods: 0,
6687                });
6688        }
6689
6690        // Economic cycle.
6691        if let Some(ec) = &self.economic_cycle {
6692            if ec.enabled {
6693                let recession_periods: Vec<u32> = ec
6694                    .recessions
6695                    .iter()
6696                    .flat_map(|r| r.start_month..r.start_month + r.duration_months)
6697                    .collect();
6698                // Use the most-severe recession as the severity driver;
6699                // fall back to default when none declared.
6700                let severity = ec
6701                    .recessions
6702                    .iter()
6703                    .map(|r| 1.0 - r.severity)
6704                    .fold(0.75f64, f64::min);
6705                drift.economic_cycle = datasynth_core::distributions::EconomicCycleConfig {
6706                    enabled: true,
6707                    cycle_length: ec.period_months,
6708                    amplitude: ec.amplitude,
6709                    phase_offset: ec.phase_offset,
6710                    recession_periods,
6711                    recession_severity: severity,
6712                };
6713                drift.drift_type = datasynth_core::distributions::DriftType::Mixed;
6714            }
6715        }
6716
6717        // Parameter drifts.
6718        for pd in &self.parameter_drifts {
6719            let drift_type = match pd.drift_type {
6720                ParameterDriftTypeConfig::Linear => {
6721                    datasynth_core::distributions::ParameterDriftType::Linear
6722                }
6723                ParameterDriftTypeConfig::Exponential => {
6724                    datasynth_core::distributions::ParameterDriftType::Exponential
6725                }
6726                ParameterDriftTypeConfig::Logistic => {
6727                    datasynth_core::distributions::ParameterDriftType::Logistic
6728                }
6729                ParameterDriftTypeConfig::Step => {
6730                    datasynth_core::distributions::ParameterDriftType::Step
6731                }
6732            };
6733            drift
6734                .parameter_drifts
6735                .push(datasynth_core::distributions::ParameterDrift {
6736                    parameter: pd.parameter.clone(),
6737                    drift_type,
6738                    initial_value: pd.start_value,
6739                    target_or_rate: pd.end_value,
6740                    start_period: pd.start_period,
6741                    end_period: pd.end_period,
6742                    steepness: 1.0,
6743                });
6744        }
6745    }
6746}
6747
6748fn convert_regime_change_type(
6749    t: RegimeChangeTypeConfig,
6750) -> datasynth_core::distributions::RegimeChangeType {
6751    use datasynth_core::distributions::RegimeChangeType as Core;
6752    match t {
6753        RegimeChangeTypeConfig::Acquisition => Core::Acquisition,
6754        RegimeChangeTypeConfig::Divestiture => Core::Divestiture,
6755        RegimeChangeTypeConfig::PriceIncrease => Core::PriceIncrease,
6756        RegimeChangeTypeConfig::PriceDecrease => Core::PriceDecrease,
6757        RegimeChangeTypeConfig::ProductLaunch => Core::ProductLaunch,
6758        RegimeChangeTypeConfig::ProductDiscontinuation => Core::ProductDiscontinuation,
6759        RegimeChangeTypeConfig::PolicyChange => Core::PolicyChange,
6760        RegimeChangeTypeConfig::CompetitorEntry => Core::CompetitorEntry,
6761        RegimeChangeTypeConfig::Custom => Core::Custom,
6762    }
6763}
6764
6765fn default_recession_severity() -> f64 {
6766    0.20
6767}
6768
6769/// Parameter drift configuration.
6770#[derive(Debug, Clone, Serialize, Deserialize)]
6771pub struct ParameterDriftSchemaConfig {
6772    /// Parameter being drifted.
6773    pub parameter: String,
6774
6775    /// Drift type.
6776    pub drift_type: ParameterDriftTypeConfig,
6777
6778    /// Start value.
6779    pub start_value: f64,
6780
6781    /// End value.
6782    pub end_value: f64,
6783
6784    /// Start period (month, 0-indexed).
6785    #[serde(default)]
6786    pub start_period: u32,
6787
6788    /// End period (month, optional - defaults to end of generation).
6789    #[serde(default)]
6790    pub end_period: Option<u32>,
6791}
6792
6793/// Parameter drift type.
6794#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
6795#[serde(rename_all = "snake_case")]
6796pub enum ParameterDriftTypeConfig {
6797    /// Linear interpolation
6798    #[default]
6799    Linear,
6800    /// Exponential growth/decay
6801    Exponential,
6802    /// S-curve (logistic)
6803    Logistic,
6804    /// Step function
6805    Step,
6806}
6807
6808/// Statistical validation configuration.
6809#[derive(Debug, Clone, Serialize, Deserialize, Default)]
6810pub struct StatisticalValidationSchemaConfig {
6811    /// Enable statistical validation.
6812    #[serde(default)]
6813    pub enabled: bool,
6814
6815    /// Statistical tests to run.
6816    #[serde(default)]
6817    pub tests: Vec<StatisticalTestConfig>,
6818
6819    /// Validation reporting configuration.
6820    #[serde(default)]
6821    pub reporting: ValidationReportingConfig,
6822}
6823
6824/// Statistical test configuration.
6825#[derive(Debug, Clone, Serialize, Deserialize)]
6826#[serde(tag = "type", rename_all = "snake_case")]
6827pub enum StatisticalTestConfig {
6828    /// Benford's Law first digit test.
6829    BenfordFirstDigit {
6830        /// Threshold MAD for failure.
6831        #[serde(default = "default_benford_threshold")]
6832        threshold_mad: f64,
6833        /// Warning MAD threshold.
6834        #[serde(default = "default_benford_warning")]
6835        warning_mad: f64,
6836    },
6837    /// Distribution fit test.
6838    DistributionFit {
6839        /// Target distribution to test.
6840        target: TargetDistributionConfig,
6841        /// K-S test significance level.
6842        #[serde(default = "default_ks_significance")]
6843        ks_significance: f64,
6844        /// Test method (ks, anderson_darling, chi_squared).
6845        #[serde(default)]
6846        method: DistributionFitMethod,
6847    },
6848    /// Correlation check.
6849    CorrelationCheck {
6850        /// Expected correlations to validate.
6851        expected_correlations: Vec<ExpectedCorrelationConfig>,
6852    },
6853    /// Chi-squared test.
6854    ChiSquared {
6855        /// Number of bins.
6856        #[serde(default = "default_chi_squared_bins")]
6857        bins: usize,
6858        /// Significance level.
6859        #[serde(default = "default_chi_squared_significance")]
6860        significance: f64,
6861    },
6862    /// Anderson-Darling test.
6863    AndersonDarling {
6864        /// Target distribution.
6865        target: TargetDistributionConfig,
6866        /// Significance level.
6867        #[serde(default = "default_ad_significance")]
6868        significance: f64,
6869    },
6870}
6871
6872fn default_benford_threshold() -> f64 {
6873    0.015
6874}
6875
6876fn default_benford_warning() -> f64 {
6877    0.010
6878}
6879
6880fn default_ks_significance() -> f64 {
6881    0.05
6882}
6883
6884fn default_chi_squared_bins() -> usize {
6885    10
6886}
6887
6888fn default_chi_squared_significance() -> f64 {
6889    0.05
6890}
6891
6892fn default_ad_significance() -> f64 {
6893    0.05
6894}
6895
6896/// Target distribution for fit tests.
6897#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
6898#[serde(rename_all = "snake_case")]
6899pub enum TargetDistributionConfig {
6900    /// Normal distribution
6901    Normal,
6902    /// Log-normal distribution
6903    #[default]
6904    LogNormal,
6905    /// Exponential distribution
6906    Exponential,
6907    /// Uniform distribution
6908    Uniform,
6909}
6910
6911/// Distribution fit test method.
6912#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
6913#[serde(rename_all = "snake_case")]
6914pub enum DistributionFitMethod {
6915    /// Kolmogorov-Smirnov test
6916    #[default]
6917    KolmogorovSmirnov,
6918    /// Anderson-Darling test
6919    AndersonDarling,
6920    /// Chi-squared test
6921    ChiSquared,
6922}
6923
6924/// Validation reporting configuration.
6925#[derive(Debug, Clone, Serialize, Deserialize)]
6926pub struct ValidationReportingConfig {
6927    /// Output validation report to file.
6928    #[serde(default)]
6929    pub output_report: bool,
6930
6931    /// Report format.
6932    #[serde(default)]
6933    pub format: ValidationReportFormat,
6934
6935    /// Fail generation if validation fails.
6936    #[serde(default)]
6937    pub fail_on_error: bool,
6938
6939    /// Include detailed statistics in report.
6940    #[serde(default = "default_true")]
6941    pub include_details: bool,
6942}
6943
6944impl Default for ValidationReportingConfig {
6945    fn default() -> Self {
6946        Self {
6947            output_report: false,
6948            format: ValidationReportFormat::Json,
6949            fail_on_error: false,
6950            include_details: true,
6951        }
6952    }
6953}
6954
6955/// Validation report format.
6956#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
6957#[serde(rename_all = "snake_case")]
6958pub enum ValidationReportFormat {
6959    /// JSON format
6960    #[default]
6961    Json,
6962    /// YAML format
6963    Yaml,
6964    /// HTML report
6965    Html,
6966}
6967
6968// =============================================================================
6969// Temporal Patterns Configuration
6970// =============================================================================
6971
6972/// Temporal patterns configuration for business days, period-end dynamics, and processing lags.
6973///
6974/// This section enables sophisticated temporal modeling including:
6975/// - Business day calculations and settlement dates
6976/// - Regional holiday calendars
6977/// - Period-end decay curves (non-flat volume spikes)
6978/// - Processing lag modeling (event-to-posting delays)
6979#[derive(Debug, Clone, Serialize, Deserialize, Default)]
6980pub struct TemporalPatternsConfig {
6981    /// Enable temporal patterns features.
6982    #[serde(default)]
6983    pub enabled: bool,
6984
6985    /// Business day calculation configuration.
6986    #[serde(default)]
6987    pub business_days: BusinessDaySchemaConfig,
6988
6989    /// Regional calendar configuration.
6990    #[serde(default)]
6991    pub calendars: CalendarSchemaConfig,
6992
6993    /// Period-end dynamics configuration.
6994    #[serde(default)]
6995    pub period_end: PeriodEndSchemaConfig,
6996
6997    /// Processing lag configuration.
6998    #[serde(default)]
6999    pub processing_lags: ProcessingLagSchemaConfig,
7000
7001    /// Fiscal calendar configuration (custom year start, 4-4-5, 13-period).
7002    #[serde(default)]
7003    pub fiscal_calendar: FiscalCalendarSchemaConfig,
7004
7005    /// Intra-day patterns configuration (morning spike, lunch dip, EOD rush).
7006    #[serde(default)]
7007    pub intraday: IntraDaySchemaConfig,
7008
7009    /// Timezone handling configuration.
7010    #[serde(default)]
7011    pub timezones: TimezoneSchemaConfig,
7012}
7013
7014/// Business day calculation configuration.
7015#[derive(Debug, Clone, Serialize, Deserialize)]
7016pub struct BusinessDaySchemaConfig {
7017    /// Enable business day calculations.
7018    #[serde(default = "default_true")]
7019    pub enabled: bool,
7020
7021    /// Half-day policy: "full_day", "half_day", "non_business_day".
7022    #[serde(default = "default_half_day_policy")]
7023    pub half_day_policy: String,
7024
7025    /// Settlement rules configuration.
7026    #[serde(default)]
7027    pub settlement_rules: SettlementRulesSchemaConfig,
7028
7029    /// Month-end convention: "modified_following", "preceding", "following", "end_of_month".
7030    #[serde(default = "default_month_end_convention")]
7031    pub month_end_convention: String,
7032
7033    /// Weekend days (e.g., ["saturday", "sunday"] or ["friday", "saturday"] for Middle East).
7034    #[serde(default)]
7035    pub weekend_days: Option<Vec<String>>,
7036}
7037
7038fn default_half_day_policy() -> String {
7039    "half_day".to_string()
7040}
7041
7042fn default_month_end_convention() -> String {
7043    "modified_following".to_string()
7044}
7045
7046impl Default for BusinessDaySchemaConfig {
7047    fn default() -> Self {
7048        Self {
7049            enabled: true,
7050            half_day_policy: "half_day".to_string(),
7051            settlement_rules: SettlementRulesSchemaConfig::default(),
7052            month_end_convention: "modified_following".to_string(),
7053            weekend_days: None,
7054        }
7055    }
7056}
7057
7058/// Settlement rules configuration.
7059#[derive(Debug, Clone, Serialize, Deserialize)]
7060pub struct SettlementRulesSchemaConfig {
7061    /// Equity settlement days (T+N).
7062    #[serde(default = "default_settlement_2")]
7063    pub equity_days: i32,
7064
7065    /// Government bonds settlement days.
7066    #[serde(default = "default_settlement_1")]
7067    pub government_bonds_days: i32,
7068
7069    /// FX spot settlement days.
7070    #[serde(default = "default_settlement_2")]
7071    pub fx_spot_days: i32,
7072
7073    /// Corporate bonds settlement days.
7074    #[serde(default = "default_settlement_2")]
7075    pub corporate_bonds_days: i32,
7076
7077    /// Wire transfer cutoff time (HH:MM format).
7078    #[serde(default = "default_wire_cutoff")]
7079    pub wire_cutoff_time: String,
7080
7081    /// International wire settlement days.
7082    #[serde(default = "default_settlement_1")]
7083    pub wire_international_days: i32,
7084
7085    /// ACH settlement days.
7086    #[serde(default = "default_settlement_1")]
7087    pub ach_days: i32,
7088}
7089
7090fn default_settlement_1() -> i32 {
7091    1
7092}
7093
7094fn default_settlement_2() -> i32 {
7095    2
7096}
7097
7098fn default_wire_cutoff() -> String {
7099    "14:00".to_string()
7100}
7101
7102impl Default for SettlementRulesSchemaConfig {
7103    fn default() -> Self {
7104        Self {
7105            equity_days: 2,
7106            government_bonds_days: 1,
7107            fx_spot_days: 2,
7108            corporate_bonds_days: 2,
7109            wire_cutoff_time: "14:00".to_string(),
7110            wire_international_days: 1,
7111            ach_days: 1,
7112        }
7113    }
7114}
7115
7116/// Regional calendar configuration.
7117#[derive(Debug, Clone, Serialize, Deserialize, Default)]
7118pub struct CalendarSchemaConfig {
7119    /// List of regions to include (e.g., ["US", "DE", "BR", "SG", "KR"]).
7120    #[serde(default)]
7121    pub regions: Vec<String>,
7122
7123    /// Custom holidays (in addition to regional calendars).
7124    #[serde(default)]
7125    pub custom_holidays: Vec<CustomHolidaySchemaConfig>,
7126}
7127
7128/// Custom holiday configuration.
7129#[derive(Debug, Clone, Serialize, Deserialize)]
7130pub struct CustomHolidaySchemaConfig {
7131    /// Holiday name.
7132    pub name: String,
7133    /// Month (1-12).
7134    pub month: u8,
7135    /// Day of month.
7136    pub day: u8,
7137    /// Activity multiplier (0.0-1.0, default 0.05).
7138    #[serde(default = "default_holiday_multiplier")]
7139    pub activity_multiplier: f64,
7140}
7141
7142fn default_holiday_multiplier() -> f64 {
7143    0.05
7144}
7145
7146/// Period-end dynamics configuration.
7147#[derive(Debug, Clone, Serialize, Deserialize, Default)]
7148pub struct PeriodEndSchemaConfig {
7149    /// Model type: "flat", "exponential", "extended_crunch", "daily_profile".
7150    #[serde(default)]
7151    pub model: Option<String>,
7152
7153    /// Month-end configuration.
7154    #[serde(default)]
7155    pub month_end: Option<PeriodEndModelSchemaConfig>,
7156
7157    /// Quarter-end configuration.
7158    #[serde(default)]
7159    pub quarter_end: Option<PeriodEndModelSchemaConfig>,
7160
7161    /// Year-end configuration.
7162    #[serde(default)]
7163    pub year_end: Option<PeriodEndModelSchemaConfig>,
7164}
7165
7166/// Period-end model configuration.
7167#[derive(Debug, Clone, Serialize, Deserialize, Default)]
7168pub struct PeriodEndModelSchemaConfig {
7169    /// Inherit configuration from another period (e.g., "month_end").
7170    #[serde(default)]
7171    pub inherit_from: Option<String>,
7172
7173    /// Additional multiplier on top of inherited/base model.
7174    #[serde(default)]
7175    pub additional_multiplier: Option<f64>,
7176
7177    /// Days before period end to start acceleration (negative, e.g., -10).
7178    #[serde(default)]
7179    pub start_day: Option<i32>,
7180
7181    /// Base multiplier at start of acceleration.
7182    #[serde(default)]
7183    pub base_multiplier: Option<f64>,
7184
7185    /// Peak multiplier on last day.
7186    #[serde(default)]
7187    pub peak_multiplier: Option<f64>,
7188
7189    /// Decay rate for exponential model (0.1-0.5 typical).
7190    #[serde(default)]
7191    pub decay_rate: Option<f64>,
7192
7193    /// Sustained high days for crunch model.
7194    #[serde(default)]
7195    pub sustained_high_days: Option<i32>,
7196}
7197
7198/// Processing lag configuration.
7199#[derive(Debug, Clone, Serialize, Deserialize)]
7200pub struct ProcessingLagSchemaConfig {
7201    /// Enable processing lag calculations.
7202    #[serde(default = "default_true")]
7203    pub enabled: bool,
7204
7205    /// Sales order lag configuration (log-normal mu, sigma).
7206    #[serde(default)]
7207    pub sales_order_lag: Option<LagDistributionSchemaConfig>,
7208
7209    /// Purchase order lag configuration.
7210    #[serde(default)]
7211    pub purchase_order_lag: Option<LagDistributionSchemaConfig>,
7212
7213    /// Goods receipt lag configuration.
7214    #[serde(default)]
7215    pub goods_receipt_lag: Option<LagDistributionSchemaConfig>,
7216
7217    /// Invoice receipt lag configuration.
7218    #[serde(default)]
7219    pub invoice_receipt_lag: Option<LagDistributionSchemaConfig>,
7220
7221    /// Invoice issue lag configuration.
7222    #[serde(default)]
7223    pub invoice_issue_lag: Option<LagDistributionSchemaConfig>,
7224
7225    /// Payment lag configuration.
7226    #[serde(default)]
7227    pub payment_lag: Option<LagDistributionSchemaConfig>,
7228
7229    /// Journal entry lag configuration.
7230    #[serde(default)]
7231    pub journal_entry_lag: Option<LagDistributionSchemaConfig>,
7232
7233    /// Cross-day posting configuration.
7234    #[serde(default)]
7235    pub cross_day_posting: Option<CrossDayPostingSchemaConfig>,
7236}
7237
7238impl Default for ProcessingLagSchemaConfig {
7239    fn default() -> Self {
7240        Self {
7241            enabled: true,
7242            sales_order_lag: None,
7243            purchase_order_lag: None,
7244            goods_receipt_lag: None,
7245            invoice_receipt_lag: None,
7246            invoice_issue_lag: None,
7247            payment_lag: None,
7248            journal_entry_lag: None,
7249            cross_day_posting: None,
7250        }
7251    }
7252}
7253
7254/// Lag distribution configuration (log-normal parameters).
7255#[derive(Debug, Clone, Serialize, Deserialize)]
7256pub struct LagDistributionSchemaConfig {
7257    /// Log-scale mean (mu for log-normal).
7258    pub mu: f64,
7259    /// Log-scale standard deviation (sigma for log-normal).
7260    pub sigma: f64,
7261    /// Minimum lag in hours.
7262    #[serde(default)]
7263    pub min_hours: Option<f64>,
7264    /// Maximum lag in hours.
7265    #[serde(default)]
7266    pub max_hours: Option<f64>,
7267}
7268
7269/// Cross-day posting configuration.
7270#[derive(Debug, Clone, Serialize, Deserialize)]
7271pub struct CrossDayPostingSchemaConfig {
7272    /// Enable cross-day posting logic.
7273    #[serde(default = "default_true")]
7274    pub enabled: bool,
7275
7276    /// Probability of next-day posting by hour (map of hour -> probability).
7277    /// E.g., { 17: 0.7, 19: 0.9, 21: 0.99 }
7278    #[serde(default)]
7279    pub probability_by_hour: std::collections::HashMap<u8, f64>,
7280}
7281
7282impl Default for CrossDayPostingSchemaConfig {
7283    fn default() -> Self {
7284        let mut probability_by_hour = std::collections::HashMap::new();
7285        probability_by_hour.insert(17, 0.3);
7286        probability_by_hour.insert(18, 0.6);
7287        probability_by_hour.insert(19, 0.8);
7288        probability_by_hour.insert(20, 0.9);
7289        probability_by_hour.insert(21, 0.95);
7290        probability_by_hour.insert(22, 0.99);
7291
7292        Self {
7293            enabled: true,
7294            probability_by_hour,
7295        }
7296    }
7297}
7298
7299// =============================================================================
7300// Fiscal Calendar Configuration (P2)
7301// =============================================================================
7302
7303/// Fiscal calendar configuration.
7304///
7305/// Supports calendar year, custom year start, 4-4-5 retail calendar,
7306/// and 13-period calendars.
7307#[derive(Debug, Clone, Serialize, Deserialize, Default)]
7308pub struct FiscalCalendarSchemaConfig {
7309    /// Enable non-standard fiscal calendar.
7310    #[serde(default)]
7311    pub enabled: bool,
7312
7313    /// Fiscal calendar type: "calendar_year", "custom", "four_four_five", "thirteen_period".
7314    #[serde(default = "default_fiscal_calendar_type")]
7315    pub calendar_type: String,
7316
7317    /// Month the fiscal year starts (1-12). Used for custom year start.
7318    #[serde(default)]
7319    pub year_start_month: Option<u8>,
7320
7321    /// Day the fiscal year starts (1-31). Used for custom year start.
7322    #[serde(default)]
7323    pub year_start_day: Option<u8>,
7324
7325    /// 4-4-5 calendar configuration (if calendar_type is "four_four_five").
7326    #[serde(default)]
7327    pub four_four_five: Option<FourFourFiveSchemaConfig>,
7328}
7329
7330fn default_fiscal_calendar_type() -> String {
7331    "calendar_year".to_string()
7332}
7333
7334/// 4-4-5 retail calendar configuration.
7335#[derive(Debug, Clone, Serialize, Deserialize)]
7336pub struct FourFourFiveSchemaConfig {
7337    /// Week pattern: "four_four_five", "four_five_four", "five_four_four".
7338    #[serde(default = "default_week_pattern")]
7339    pub pattern: String,
7340
7341    /// Anchor type: "first_sunday", "last_saturday", "nearest_saturday".
7342    #[serde(default = "default_anchor_type")]
7343    pub anchor_type: String,
7344
7345    /// Anchor month (1-12).
7346    #[serde(default = "default_anchor_month")]
7347    pub anchor_month: u8,
7348
7349    /// Where to place leap week: "q4_period3" or "q1_period1".
7350    #[serde(default = "default_leap_week_placement")]
7351    pub leap_week_placement: String,
7352}
7353
7354fn default_week_pattern() -> String {
7355    "four_four_five".to_string()
7356}
7357
7358fn default_anchor_type() -> String {
7359    "last_saturday".to_string()
7360}
7361
7362fn default_anchor_month() -> u8 {
7363    1 // January
7364}
7365
7366fn default_leap_week_placement() -> String {
7367    "q4_period3".to_string()
7368}
7369
7370impl Default for FourFourFiveSchemaConfig {
7371    fn default() -> Self {
7372        Self {
7373            pattern: "four_four_five".to_string(),
7374            anchor_type: "last_saturday".to_string(),
7375            anchor_month: 1,
7376            leap_week_placement: "q4_period3".to_string(),
7377        }
7378    }
7379}
7380
7381// =============================================================================
7382// Intra-Day Patterns Configuration (P2)
7383// =============================================================================
7384
7385/// Intra-day patterns configuration.
7386///
7387/// Defines time-of-day segments with different activity multipliers
7388/// for realistic modeling of morning spikes, lunch dips, and end-of-day rushes.
7389#[derive(Debug, Clone, Serialize, Deserialize, Default)]
7390pub struct IntraDaySchemaConfig {
7391    /// Enable intra-day patterns.
7392    #[serde(default)]
7393    pub enabled: bool,
7394
7395    /// Custom intra-day segments.
7396    #[serde(default)]
7397    pub segments: Vec<IntraDaySegmentSchemaConfig>,
7398}
7399
7400/// Intra-day segment configuration.
7401#[derive(Debug, Clone, Serialize, Deserialize)]
7402pub struct IntraDaySegmentSchemaConfig {
7403    /// Name of the segment (e.g., "morning_spike", "lunch_dip").
7404    pub name: String,
7405
7406    /// Start time (HH:MM format).
7407    pub start: String,
7408
7409    /// End time (HH:MM format).
7410    pub end: String,
7411
7412    /// Activity multiplier (1.0 = normal).
7413    #[serde(default = "default_multiplier")]
7414    pub multiplier: f64,
7415
7416    /// Posting type: "human", "system", "both".
7417    #[serde(default = "default_posting_type")]
7418    pub posting_type: String,
7419}
7420
7421fn default_multiplier() -> f64 {
7422    1.0
7423}
7424
7425fn default_posting_type() -> String {
7426    "both".to_string()
7427}
7428
7429// =============================================================================
7430// Timezone Configuration
7431// =============================================================================
7432
7433/// Timezone handling configuration for multi-region entities.
7434#[derive(Debug, Clone, Serialize, Deserialize, Default)]
7435pub struct TimezoneSchemaConfig {
7436    /// Enable timezone handling.
7437    #[serde(default)]
7438    pub enabled: bool,
7439
7440    /// Default timezone (IANA format, e.g., "America/New_York").
7441    #[serde(default = "default_timezone")]
7442    pub default_timezone: String,
7443
7444    /// Consolidation timezone for group reporting (IANA format).
7445    #[serde(default = "default_consolidation_timezone")]
7446    pub consolidation_timezone: String,
7447
7448    /// Entity-to-timezone mappings.
7449    /// Supports patterns like "EU_*" -> "Europe/London".
7450    #[serde(default)]
7451    pub entity_mappings: Vec<EntityTimezoneMapping>,
7452}
7453
7454fn default_timezone() -> String {
7455    "America/New_York".to_string()
7456}
7457
7458fn default_consolidation_timezone() -> String {
7459    "UTC".to_string()
7460}
7461
7462/// Mapping from entity pattern to timezone.
7463#[derive(Debug, Clone, Serialize, Deserialize)]
7464pub struct EntityTimezoneMapping {
7465    /// Entity code pattern (e.g., "EU_*", "*_APAC", "1000").
7466    pub pattern: String,
7467
7468    /// Timezone (IANA format, e.g., "Europe/London").
7469    pub timezone: String,
7470}
7471
7472// =============================================================================
7473// Vendor Network Configuration
7474// =============================================================================
7475
7476/// Configuration for multi-tier vendor network generation.
7477#[derive(Debug, Clone, Serialize, Deserialize)]
7478pub struct VendorNetworkSchemaConfig {
7479    /// Enable vendor network generation.
7480    #[serde(default)]
7481    pub enabled: bool,
7482
7483    /// Maximum depth of supply chain tiers (1-3).
7484    #[serde(default = "default_vendor_tier_depth")]
7485    pub depth: u8,
7486
7487    /// Tier 1 vendor count configuration.
7488    #[serde(default)]
7489    pub tier1: TierCountSchemaConfig,
7490
7491    /// Tier 2 vendors per Tier 1 parent.
7492    #[serde(default)]
7493    pub tier2_per_parent: TierCountSchemaConfig,
7494
7495    /// Tier 3 vendors per Tier 2 parent.
7496    #[serde(default)]
7497    pub tier3_per_parent: TierCountSchemaConfig,
7498
7499    /// Vendor cluster distribution.
7500    #[serde(default)]
7501    pub clusters: VendorClusterSchemaConfig,
7502
7503    /// Concentration limits.
7504    #[serde(default)]
7505    pub dependencies: DependencySchemaConfig,
7506}
7507
7508fn default_vendor_tier_depth() -> u8 {
7509    3
7510}
7511
7512impl Default for VendorNetworkSchemaConfig {
7513    fn default() -> Self {
7514        Self {
7515            enabled: false,
7516            depth: 3,
7517            tier1: TierCountSchemaConfig { min: 50, max: 100 },
7518            tier2_per_parent: TierCountSchemaConfig { min: 4, max: 10 },
7519            tier3_per_parent: TierCountSchemaConfig { min: 2, max: 5 },
7520            clusters: VendorClusterSchemaConfig::default(),
7521            dependencies: DependencySchemaConfig::default(),
7522        }
7523    }
7524}
7525
7526/// Tier count configuration.
7527#[derive(Debug, Clone, Serialize, Deserialize)]
7528pub struct TierCountSchemaConfig {
7529    /// Minimum count.
7530    #[serde(default = "default_tier_min")]
7531    pub min: usize,
7532
7533    /// Maximum count.
7534    #[serde(default = "default_tier_max")]
7535    pub max: usize,
7536}
7537
7538fn default_tier_min() -> usize {
7539    5
7540}
7541
7542fn default_tier_max() -> usize {
7543    20
7544}
7545
7546impl Default for TierCountSchemaConfig {
7547    fn default() -> Self {
7548        Self {
7549            min: default_tier_min(),
7550            max: default_tier_max(),
7551        }
7552    }
7553}
7554
7555/// Vendor cluster distribution configuration.
7556#[derive(Debug, Clone, Serialize, Deserialize)]
7557pub struct VendorClusterSchemaConfig {
7558    /// Reliable strategic vendors percentage (default: 0.20).
7559    #[serde(default = "default_reliable_strategic")]
7560    pub reliable_strategic: f64,
7561
7562    /// Standard operational vendors percentage (default: 0.50).
7563    #[serde(default = "default_standard_operational")]
7564    pub standard_operational: f64,
7565
7566    /// Transactional vendors percentage (default: 0.25).
7567    #[serde(default = "default_transactional")]
7568    pub transactional: f64,
7569
7570    /// Problematic vendors percentage (default: 0.05).
7571    #[serde(default = "default_problematic")]
7572    pub problematic: f64,
7573}
7574
7575fn default_reliable_strategic() -> f64 {
7576    0.20
7577}
7578
7579fn default_standard_operational() -> f64 {
7580    0.50
7581}
7582
7583fn default_transactional() -> f64 {
7584    0.25
7585}
7586
7587fn default_problematic() -> f64 {
7588    0.05
7589}
7590
7591impl Default for VendorClusterSchemaConfig {
7592    fn default() -> Self {
7593        Self {
7594            reliable_strategic: 0.20,
7595            standard_operational: 0.50,
7596            transactional: 0.25,
7597            problematic: 0.05,
7598        }
7599    }
7600}
7601
7602/// Dependency and concentration limits configuration.
7603#[derive(Debug, Clone, Serialize, Deserialize)]
7604pub struct DependencySchemaConfig {
7605    /// Maximum concentration for a single vendor (default: 0.15).
7606    #[serde(default = "default_max_single_vendor")]
7607    pub max_single_vendor_concentration: f64,
7608
7609    /// Maximum concentration for top 5 vendors (default: 0.45).
7610    #[serde(default = "default_max_top5")]
7611    pub top_5_concentration: f64,
7612
7613    /// Percentage of single-source vendors (default: 0.05).
7614    #[serde(default = "default_single_source_percent")]
7615    pub single_source_percent: f64,
7616}
7617
7618fn default_max_single_vendor() -> f64 {
7619    0.15
7620}
7621
7622fn default_max_top5() -> f64 {
7623    0.45
7624}
7625
7626fn default_single_source_percent() -> f64 {
7627    0.05
7628}
7629
7630impl Default for DependencySchemaConfig {
7631    fn default() -> Self {
7632        Self {
7633            max_single_vendor_concentration: 0.15,
7634            top_5_concentration: 0.45,
7635            single_source_percent: 0.05,
7636        }
7637    }
7638}
7639
7640// =============================================================================
7641// Customer Segmentation Configuration
7642// =============================================================================
7643
7644/// Configuration for customer segmentation generation.
7645#[derive(Debug, Clone, Default, Serialize, Deserialize)]
7646pub struct CustomerSegmentationSchemaConfig {
7647    /// Enable customer segmentation generation.
7648    #[serde(default)]
7649    pub enabled: bool,
7650
7651    /// Value segment distribution.
7652    #[serde(default)]
7653    pub value_segments: ValueSegmentsSchemaConfig,
7654
7655    /// Lifecycle stage configuration.
7656    #[serde(default)]
7657    pub lifecycle: LifecycleSchemaConfig,
7658
7659    /// Network (referrals, hierarchies) configuration.
7660    #[serde(default)]
7661    pub networks: CustomerNetworksSchemaConfig,
7662}
7663
7664/// Customer value segments distribution configuration.
7665#[derive(Debug, Clone, Serialize, Deserialize)]
7666pub struct ValueSegmentsSchemaConfig {
7667    /// Enterprise segment configuration.
7668    #[serde(default)]
7669    pub enterprise: SegmentDetailSchemaConfig,
7670
7671    /// Mid-market segment configuration.
7672    #[serde(default)]
7673    pub mid_market: SegmentDetailSchemaConfig,
7674
7675    /// SMB segment configuration.
7676    #[serde(default)]
7677    pub smb: SegmentDetailSchemaConfig,
7678
7679    /// Consumer segment configuration.
7680    #[serde(default)]
7681    pub consumer: SegmentDetailSchemaConfig,
7682}
7683
7684impl Default for ValueSegmentsSchemaConfig {
7685    fn default() -> Self {
7686        Self {
7687            enterprise: SegmentDetailSchemaConfig {
7688                revenue_share: 0.40,
7689                customer_share: 0.05,
7690                avg_order_value_range: "50000+".to_string(),
7691            },
7692            mid_market: SegmentDetailSchemaConfig {
7693                revenue_share: 0.35,
7694                customer_share: 0.20,
7695                avg_order_value_range: "5000-50000".to_string(),
7696            },
7697            smb: SegmentDetailSchemaConfig {
7698                revenue_share: 0.20,
7699                customer_share: 0.50,
7700                avg_order_value_range: "500-5000".to_string(),
7701            },
7702            consumer: SegmentDetailSchemaConfig {
7703                revenue_share: 0.05,
7704                customer_share: 0.25,
7705                avg_order_value_range: "50-500".to_string(),
7706            },
7707        }
7708    }
7709}
7710
7711/// Individual segment detail configuration.
7712#[derive(Debug, Clone, Serialize, Deserialize)]
7713pub struct SegmentDetailSchemaConfig {
7714    /// Revenue share for this segment.
7715    #[serde(default)]
7716    pub revenue_share: f64,
7717
7718    /// Customer share for this segment.
7719    #[serde(default)]
7720    pub customer_share: f64,
7721
7722    /// Average order value range (e.g., "5000-50000" or "50000+").
7723    #[serde(default)]
7724    pub avg_order_value_range: String,
7725}
7726
7727impl Default for SegmentDetailSchemaConfig {
7728    fn default() -> Self {
7729        Self {
7730            revenue_share: 0.25,
7731            customer_share: 0.25,
7732            avg_order_value_range: "1000-10000".to_string(),
7733        }
7734    }
7735}
7736
7737/// Customer lifecycle stage configuration.
7738#[derive(Debug, Clone, Serialize, Deserialize)]
7739pub struct LifecycleSchemaConfig {
7740    /// Prospect stage rate.
7741    #[serde(default)]
7742    pub prospect_rate: f64,
7743
7744    /// New customer stage rate.
7745    #[serde(default = "default_new_rate")]
7746    pub new_rate: f64,
7747
7748    /// Growth stage rate.
7749    #[serde(default = "default_growth_rate")]
7750    pub growth_rate: f64,
7751
7752    /// Mature stage rate.
7753    #[serde(default = "default_mature_rate")]
7754    pub mature_rate: f64,
7755
7756    /// At-risk stage rate.
7757    #[serde(default = "default_at_risk_rate")]
7758    pub at_risk_rate: f64,
7759
7760    /// Churned stage rate.
7761    #[serde(default = "default_churned_rate")]
7762    pub churned_rate: f64,
7763
7764    /// Won-back stage rate (churned customers reacquired).
7765    #[serde(default)]
7766    pub won_back_rate: f64,
7767}
7768
7769fn default_new_rate() -> f64 {
7770    0.10
7771}
7772
7773fn default_growth_rate() -> f64 {
7774    0.15
7775}
7776
7777fn default_mature_rate() -> f64 {
7778    0.60
7779}
7780
7781fn default_at_risk_rate() -> f64 {
7782    0.10
7783}
7784
7785fn default_churned_rate() -> f64 {
7786    0.05
7787}
7788
7789impl Default for LifecycleSchemaConfig {
7790    fn default() -> Self {
7791        Self {
7792            prospect_rate: 0.0,
7793            new_rate: 0.10,
7794            growth_rate: 0.15,
7795            mature_rate: 0.60,
7796            at_risk_rate: 0.10,
7797            churned_rate: 0.05,
7798            won_back_rate: 0.0,
7799        }
7800    }
7801}
7802
7803/// Customer networks configuration (referrals, hierarchies).
7804#[derive(Debug, Clone, Default, Serialize, Deserialize)]
7805pub struct CustomerNetworksSchemaConfig {
7806    /// Referral network configuration.
7807    #[serde(default)]
7808    pub referrals: ReferralSchemaConfig,
7809
7810    /// Corporate hierarchy configuration.
7811    #[serde(default)]
7812    pub corporate_hierarchies: HierarchySchemaConfig,
7813}
7814
7815/// Referral network configuration.
7816#[derive(Debug, Clone, Serialize, Deserialize)]
7817pub struct ReferralSchemaConfig {
7818    /// Enable referral generation.
7819    #[serde(default = "default_true")]
7820    pub enabled: bool,
7821
7822    /// Rate of customers acquired via referral.
7823    #[serde(default = "default_referral_rate")]
7824    pub referral_rate: f64,
7825}
7826
7827fn default_referral_rate() -> f64 {
7828    0.15
7829}
7830
7831impl Default for ReferralSchemaConfig {
7832    fn default() -> Self {
7833        Self {
7834            enabled: true,
7835            referral_rate: 0.15,
7836        }
7837    }
7838}
7839
7840/// Corporate hierarchy configuration.
7841#[derive(Debug, Clone, Serialize, Deserialize)]
7842pub struct HierarchySchemaConfig {
7843    /// Enable corporate hierarchy generation.
7844    #[serde(default = "default_true")]
7845    pub enabled: bool,
7846
7847    /// Rate of customers in hierarchies.
7848    #[serde(default = "default_hierarchy_rate")]
7849    pub probability: f64,
7850}
7851
7852fn default_hierarchy_rate() -> f64 {
7853    0.30
7854}
7855
7856impl Default for HierarchySchemaConfig {
7857    fn default() -> Self {
7858        Self {
7859            enabled: true,
7860            probability: 0.30,
7861        }
7862    }
7863}
7864
7865// =============================================================================
7866// Relationship Strength Configuration
7867// =============================================================================
7868
7869/// Configuration for relationship strength calculation.
7870#[derive(Debug, Clone, Default, Serialize, Deserialize)]
7871pub struct RelationshipStrengthSchemaConfig {
7872    /// Enable relationship strength calculation.
7873    #[serde(default)]
7874    pub enabled: bool,
7875
7876    /// Calculation weights.
7877    #[serde(default)]
7878    pub calculation: StrengthCalculationSchemaConfig,
7879
7880    /// Strength thresholds for classification.
7881    #[serde(default)]
7882    pub thresholds: StrengthThresholdsSchemaConfig,
7883}
7884
7885/// Strength calculation weights configuration.
7886#[derive(Debug, Clone, Serialize, Deserialize)]
7887pub struct StrengthCalculationSchemaConfig {
7888    /// Weight for transaction volume (default: 0.30).
7889    #[serde(default = "default_volume_weight")]
7890    pub transaction_volume_weight: f64,
7891
7892    /// Weight for transaction count (default: 0.25).
7893    #[serde(default = "default_count_weight")]
7894    pub transaction_count_weight: f64,
7895
7896    /// Weight for relationship duration (default: 0.20).
7897    #[serde(default = "default_duration_weight")]
7898    pub relationship_duration_weight: f64,
7899
7900    /// Weight for recency (default: 0.15).
7901    #[serde(default = "default_recency_weight")]
7902    pub recency_weight: f64,
7903
7904    /// Weight for mutual connections (default: 0.10).
7905    #[serde(default = "default_mutual_weight")]
7906    pub mutual_connections_weight: f64,
7907
7908    /// Recency half-life in days (default: 90).
7909    #[serde(default = "default_recency_half_life")]
7910    pub recency_half_life_days: u32,
7911}
7912
7913fn default_volume_weight() -> f64 {
7914    0.30
7915}
7916
7917fn default_count_weight() -> f64 {
7918    0.25
7919}
7920
7921fn default_duration_weight() -> f64 {
7922    0.20
7923}
7924
7925fn default_recency_weight() -> f64 {
7926    0.15
7927}
7928
7929fn default_mutual_weight() -> f64 {
7930    0.10
7931}
7932
7933fn default_recency_half_life() -> u32 {
7934    90
7935}
7936
7937impl Default for StrengthCalculationSchemaConfig {
7938    fn default() -> Self {
7939        Self {
7940            transaction_volume_weight: 0.30,
7941            transaction_count_weight: 0.25,
7942            relationship_duration_weight: 0.20,
7943            recency_weight: 0.15,
7944            mutual_connections_weight: 0.10,
7945            recency_half_life_days: 90,
7946        }
7947    }
7948}
7949
7950/// Strength thresholds for relationship classification.
7951#[derive(Debug, Clone, Serialize, Deserialize)]
7952pub struct StrengthThresholdsSchemaConfig {
7953    /// Threshold for strong relationships (default: 0.7).
7954    #[serde(default = "default_strong_threshold")]
7955    pub strong: f64,
7956
7957    /// Threshold for moderate relationships (default: 0.4).
7958    #[serde(default = "default_moderate_threshold")]
7959    pub moderate: f64,
7960
7961    /// Threshold for weak relationships (default: 0.1).
7962    #[serde(default = "default_weak_threshold")]
7963    pub weak: f64,
7964}
7965
7966fn default_strong_threshold() -> f64 {
7967    0.7
7968}
7969
7970fn default_moderate_threshold() -> f64 {
7971    0.4
7972}
7973
7974fn default_weak_threshold() -> f64 {
7975    0.1
7976}
7977
7978impl Default for StrengthThresholdsSchemaConfig {
7979    fn default() -> Self {
7980        Self {
7981            strong: 0.7,
7982            moderate: 0.4,
7983            weak: 0.1,
7984        }
7985    }
7986}
7987
7988// =============================================================================
7989// Cross-Process Links Configuration
7990// =============================================================================
7991
7992/// Configuration for cross-process linkages.
7993#[derive(Debug, Clone, Serialize, Deserialize)]
7994pub struct CrossProcessLinksSchemaConfig {
7995    /// Enable cross-process link generation.
7996    #[serde(default)]
7997    pub enabled: bool,
7998
7999    /// Enable inventory links between P2P and O2C.
8000    #[serde(default = "default_true")]
8001    pub inventory_p2p_o2c: bool,
8002
8003    /// Enable payment to bank reconciliation links.
8004    #[serde(default = "default_true")]
8005    pub payment_bank_reconciliation: bool,
8006
8007    /// Enable intercompany bilateral matching.
8008    #[serde(default = "default_true")]
8009    pub intercompany_bilateral: bool,
8010
8011    /// Percentage of GR/Deliveries to link via inventory (0.0 - 1.0).
8012    #[serde(default = "default_inventory_link_rate")]
8013    pub inventory_link_rate: f64,
8014}
8015
8016fn default_inventory_link_rate() -> f64 {
8017    0.30
8018}
8019
8020impl Default for CrossProcessLinksSchemaConfig {
8021    fn default() -> Self {
8022        Self {
8023            enabled: false,
8024            inventory_p2p_o2c: true,
8025            payment_bank_reconciliation: true,
8026            intercompany_bilateral: true,
8027            inventory_link_rate: 0.30,
8028        }
8029    }
8030}
8031
8032// =============================================================================
8033// Organizational Events Configuration
8034// =============================================================================
8035
8036/// Configuration for organizational events (acquisitions, divestitures, etc.).
8037#[derive(Debug, Clone, Serialize, Deserialize, Default)]
8038pub struct OrganizationalEventsSchemaConfig {
8039    /// Enable organizational events.
8040    #[serde(default)]
8041    pub enabled: bool,
8042
8043    /// Effect blending mode (multiplicative, additive, maximum, minimum).
8044    #[serde(default)]
8045    pub effect_blending: EffectBlendingModeConfig,
8046
8047    /// Organizational events (acquisitions, divestitures, reorganizations, etc.).
8048    #[serde(default)]
8049    pub events: Vec<OrganizationalEventSchemaConfig>,
8050
8051    /// Process evolution events.
8052    #[serde(default)]
8053    pub process_evolution: Vec<ProcessEvolutionSchemaConfig>,
8054
8055    /// Technology transition events.
8056    #[serde(default)]
8057    pub technology_transitions: Vec<TechnologyTransitionSchemaConfig>,
8058}
8059
8060/// Effect blending mode for combining multiple event effects.
8061#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
8062#[serde(rename_all = "snake_case")]
8063pub enum EffectBlendingModeConfig {
8064    /// Multiply effects together.
8065    #[default]
8066    Multiplicative,
8067    /// Add effects together.
8068    Additive,
8069    /// Take the maximum effect.
8070    Maximum,
8071    /// Take the minimum effect.
8072    Minimum,
8073}
8074
8075/// Configuration for a single organizational event.
8076#[derive(Debug, Clone, Serialize, Deserialize)]
8077pub struct OrganizationalEventSchemaConfig {
8078    /// Event ID.
8079    pub id: String,
8080
8081    /// Event type and configuration.
8082    pub event_type: OrganizationalEventTypeSchemaConfig,
8083
8084    /// Effective date.
8085    pub effective_date: String,
8086
8087    /// Transition duration in months.
8088    #[serde(default = "default_org_transition_months")]
8089    pub transition_months: u32,
8090
8091    /// Description.
8092    #[serde(default)]
8093    pub description: Option<String>,
8094}
8095
8096fn default_org_transition_months() -> u32 {
8097    6
8098}
8099
8100/// Organizational event type configuration.
8101#[derive(Debug, Clone, Serialize, Deserialize)]
8102#[serde(tag = "type", rename_all = "snake_case")]
8103pub enum OrganizationalEventTypeSchemaConfig {
8104    /// Acquisition event.
8105    Acquisition {
8106        /// Acquired entity code.
8107        acquired_entity: String,
8108        /// Volume increase multiplier.
8109        #[serde(default = "default_acquisition_volume")]
8110        volume_increase: f64,
8111        /// Integration error rate.
8112        #[serde(default = "default_acquisition_error")]
8113        integration_error_rate: f64,
8114        /// Parallel posting days.
8115        #[serde(default = "default_parallel_days")]
8116        parallel_posting_days: u32,
8117    },
8118    /// Divestiture event.
8119    Divestiture {
8120        /// Divested entity code.
8121        divested_entity: String,
8122        /// Volume reduction factor.
8123        #[serde(default = "default_divestiture_volume")]
8124        volume_reduction: f64,
8125        /// Remove entity from generation.
8126        #[serde(default = "default_true_val")]
8127        remove_entity: bool,
8128    },
8129    /// Reorganization event.
8130    Reorganization {
8131        /// Cost center remapping.
8132        #[serde(default)]
8133        cost_center_remapping: std::collections::HashMap<String, String>,
8134        /// Transition error rate.
8135        #[serde(default = "default_reorg_error")]
8136        transition_error_rate: f64,
8137    },
8138    /// Leadership change event.
8139    LeadershipChange {
8140        /// Role that changed.
8141        role: String,
8142        /// Policy changes.
8143        #[serde(default)]
8144        policy_changes: Vec<String>,
8145    },
8146    /// Workforce reduction event.
8147    WorkforceReduction {
8148        /// Reduction percentage.
8149        #[serde(default = "default_workforce_reduction")]
8150        reduction_percent: f64,
8151        /// Error rate increase.
8152        #[serde(default = "default_workforce_error")]
8153        error_rate_increase: f64,
8154    },
8155    /// Merger event.
8156    Merger {
8157        /// Merged entity code.
8158        merged_entity: String,
8159        /// Volume increase multiplier.
8160        #[serde(default = "default_merger_volume")]
8161        volume_increase: f64,
8162    },
8163}
8164
8165fn default_acquisition_volume() -> f64 {
8166    1.35
8167}
8168
8169fn default_acquisition_error() -> f64 {
8170    0.05
8171}
8172
8173fn default_parallel_days() -> u32 {
8174    30
8175}
8176
8177fn default_divestiture_volume() -> f64 {
8178    0.70
8179}
8180
8181fn default_true_val() -> bool {
8182    true
8183}
8184
8185fn default_reorg_error() -> f64 {
8186    0.04
8187}
8188
8189fn default_workforce_reduction() -> f64 {
8190    0.10
8191}
8192
8193fn default_workforce_error() -> f64 {
8194    0.05
8195}
8196
8197fn default_merger_volume() -> f64 {
8198    1.80
8199}
8200
8201/// Configuration for a process evolution event.
8202#[derive(Debug, Clone, Serialize, Deserialize)]
8203pub struct ProcessEvolutionSchemaConfig {
8204    /// Event ID.
8205    pub id: String,
8206
8207    /// Event type.
8208    pub event_type: ProcessEvolutionTypeSchemaConfig,
8209
8210    /// Effective date.
8211    pub effective_date: String,
8212
8213    /// Description.
8214    #[serde(default)]
8215    pub description: Option<String>,
8216}
8217
8218/// Process evolution type configuration.
8219#[derive(Debug, Clone, Serialize, Deserialize)]
8220#[serde(tag = "type", rename_all = "snake_case")]
8221pub enum ProcessEvolutionTypeSchemaConfig {
8222    /// Process automation.
8223    ProcessAutomation {
8224        /// Process name.
8225        process_name: String,
8226        /// Manual rate before.
8227        #[serde(default = "default_manual_before")]
8228        manual_rate_before: f64,
8229        /// Manual rate after.
8230        #[serde(default = "default_manual_after")]
8231        manual_rate_after: f64,
8232    },
8233    /// Approval workflow change.
8234    ApprovalWorkflowChange {
8235        /// Description.
8236        description: String,
8237    },
8238    /// Control enhancement.
8239    ControlEnhancement {
8240        /// Control ID.
8241        control_id: String,
8242        /// Error reduction.
8243        #[serde(default = "default_error_reduction")]
8244        error_reduction: f64,
8245    },
8246}
8247
8248fn default_manual_before() -> f64 {
8249    0.80
8250}
8251
8252fn default_manual_after() -> f64 {
8253    0.15
8254}
8255
8256fn default_error_reduction() -> f64 {
8257    0.02
8258}
8259
8260/// Configuration for a technology transition event.
8261#[derive(Debug, Clone, Serialize, Deserialize)]
8262pub struct TechnologyTransitionSchemaConfig {
8263    /// Event ID.
8264    pub id: String,
8265
8266    /// Event type.
8267    pub event_type: TechnologyTransitionTypeSchemaConfig,
8268
8269    /// Description.
8270    #[serde(default)]
8271    pub description: Option<String>,
8272}
8273
8274/// Technology transition type configuration.
8275#[derive(Debug, Clone, Serialize, Deserialize)]
8276#[serde(tag = "type", rename_all = "snake_case")]
8277pub enum TechnologyTransitionTypeSchemaConfig {
8278    /// ERP migration.
8279    ErpMigration {
8280        /// Source system.
8281        source_system: String,
8282        /// Target system.
8283        target_system: String,
8284        /// Cutover date.
8285        cutover_date: String,
8286        /// Stabilization end date.
8287        stabilization_end: String,
8288        /// Duplicate rate during migration.
8289        #[serde(default = "default_erp_duplicate_rate")]
8290        duplicate_rate: f64,
8291        /// Format mismatch rate.
8292        #[serde(default = "default_format_mismatch")]
8293        format_mismatch_rate: f64,
8294    },
8295    /// Module implementation.
8296    ModuleImplementation {
8297        /// Module name.
8298        module_name: String,
8299        /// Go-live date.
8300        go_live_date: String,
8301    },
8302}
8303
8304fn default_erp_duplicate_rate() -> f64 {
8305    0.02
8306}
8307
8308fn default_format_mismatch() -> f64 {
8309    0.03
8310}
8311
8312// =============================================================================
8313// Behavioral Drift Configuration
8314// =============================================================================
8315
8316/// Configuration for behavioral drift (vendor, customer, employee behavior).
8317#[derive(Debug, Clone, Serialize, Deserialize, Default)]
8318pub struct BehavioralDriftSchemaConfig {
8319    /// Enable behavioral drift.
8320    #[serde(default)]
8321    pub enabled: bool,
8322
8323    /// Vendor behavior drift.
8324    #[serde(default)]
8325    pub vendor_behavior: VendorBehaviorSchemaConfig,
8326
8327    /// Customer behavior drift.
8328    #[serde(default)]
8329    pub customer_behavior: CustomerBehaviorSchemaConfig,
8330
8331    /// Employee behavior drift.
8332    #[serde(default)]
8333    pub employee_behavior: EmployeeBehaviorSchemaConfig,
8334
8335    /// Collective behavior drift.
8336    #[serde(default)]
8337    pub collective: CollectiveBehaviorSchemaConfig,
8338}
8339
8340/// Vendor behavior drift configuration.
8341#[derive(Debug, Clone, Default, Serialize, Deserialize)]
8342pub struct VendorBehaviorSchemaConfig {
8343    /// Payment terms drift.
8344    #[serde(default)]
8345    pub payment_terms_drift: PaymentTermsDriftSchemaConfig,
8346
8347    /// Quality drift.
8348    #[serde(default)]
8349    pub quality_drift: QualityDriftSchemaConfig,
8350}
8351
8352/// Payment terms drift configuration.
8353#[derive(Debug, Clone, Serialize, Deserialize)]
8354pub struct PaymentTermsDriftSchemaConfig {
8355    /// Extension rate per year (days).
8356    #[serde(default = "default_extension_rate")]
8357    pub extension_rate_per_year: f64,
8358
8359    /// Economic sensitivity.
8360    #[serde(default = "default_economic_sensitivity")]
8361    pub economic_sensitivity: f64,
8362}
8363
8364fn default_extension_rate() -> f64 {
8365    2.5
8366}
8367
8368fn default_economic_sensitivity() -> f64 {
8369    1.0
8370}
8371
8372impl Default for PaymentTermsDriftSchemaConfig {
8373    fn default() -> Self {
8374        Self {
8375            extension_rate_per_year: 2.5,
8376            economic_sensitivity: 1.0,
8377        }
8378    }
8379}
8380
8381/// Quality drift configuration.
8382#[derive(Debug, Clone, Serialize, Deserialize)]
8383pub struct QualityDriftSchemaConfig {
8384    /// New vendor improvement rate (per year).
8385    #[serde(default = "default_improvement_rate")]
8386    pub new_vendor_improvement_rate: f64,
8387
8388    /// Complacency decline rate (per year after first year).
8389    #[serde(default = "default_decline_rate")]
8390    pub complacency_decline_rate: f64,
8391}
8392
8393fn default_improvement_rate() -> f64 {
8394    0.02
8395}
8396
8397fn default_decline_rate() -> f64 {
8398    0.01
8399}
8400
8401impl Default for QualityDriftSchemaConfig {
8402    fn default() -> Self {
8403        Self {
8404            new_vendor_improvement_rate: 0.02,
8405            complacency_decline_rate: 0.01,
8406        }
8407    }
8408}
8409
8410/// Customer behavior drift configuration.
8411#[derive(Debug, Clone, Default, Serialize, Deserialize)]
8412pub struct CustomerBehaviorSchemaConfig {
8413    /// Payment drift.
8414    #[serde(default)]
8415    pub payment_drift: CustomerPaymentDriftSchemaConfig,
8416
8417    /// Order drift.
8418    #[serde(default)]
8419    pub order_drift: OrderDriftSchemaConfig,
8420}
8421
8422/// Customer payment drift configuration.
8423#[derive(Debug, Clone, Serialize, Deserialize)]
8424pub struct CustomerPaymentDriftSchemaConfig {
8425    /// Days extension during downturn (min, max).
8426    #[serde(default = "default_downturn_extension")]
8427    pub downturn_days_extension: (u32, u32),
8428
8429    /// Bad debt increase during downturn.
8430    #[serde(default = "default_bad_debt_increase")]
8431    pub downturn_bad_debt_increase: f64,
8432}
8433
8434fn default_downturn_extension() -> (u32, u32) {
8435    (5, 15)
8436}
8437
8438fn default_bad_debt_increase() -> f64 {
8439    0.02
8440}
8441
8442impl Default for CustomerPaymentDriftSchemaConfig {
8443    fn default() -> Self {
8444        Self {
8445            downturn_days_extension: (5, 15),
8446            downturn_bad_debt_increase: 0.02,
8447        }
8448    }
8449}
8450
8451/// Order drift configuration.
8452#[derive(Debug, Clone, Serialize, Deserialize)]
8453pub struct OrderDriftSchemaConfig {
8454    /// Digital shift rate (per year).
8455    #[serde(default = "default_digital_shift")]
8456    pub digital_shift_rate: f64,
8457}
8458
8459fn default_digital_shift() -> f64 {
8460    0.05
8461}
8462
8463impl Default for OrderDriftSchemaConfig {
8464    fn default() -> Self {
8465        Self {
8466            digital_shift_rate: 0.05,
8467        }
8468    }
8469}
8470
8471/// Employee behavior drift configuration.
8472#[derive(Debug, Clone, Default, Serialize, Deserialize)]
8473pub struct EmployeeBehaviorSchemaConfig {
8474    /// Approval drift.
8475    #[serde(default)]
8476    pub approval_drift: ApprovalDriftSchemaConfig,
8477
8478    /// Error drift.
8479    #[serde(default)]
8480    pub error_drift: ErrorDriftSchemaConfig,
8481}
8482
8483/// Approval drift configuration.
8484#[derive(Debug, Clone, Serialize, Deserialize)]
8485pub struct ApprovalDriftSchemaConfig {
8486    /// EOM intensity increase per year.
8487    #[serde(default = "default_eom_intensity")]
8488    pub eom_intensity_increase_per_year: f64,
8489
8490    /// Rubber stamp volume threshold.
8491    #[serde(default = "default_rubber_stamp")]
8492    pub rubber_stamp_volume_threshold: u32,
8493}
8494
8495fn default_eom_intensity() -> f64 {
8496    0.05
8497}
8498
8499fn default_rubber_stamp() -> u32 {
8500    50
8501}
8502
8503impl Default for ApprovalDriftSchemaConfig {
8504    fn default() -> Self {
8505        Self {
8506            eom_intensity_increase_per_year: 0.05,
8507            rubber_stamp_volume_threshold: 50,
8508        }
8509    }
8510}
8511
8512/// Error drift configuration.
8513#[derive(Debug, Clone, Serialize, Deserialize)]
8514pub struct ErrorDriftSchemaConfig {
8515    /// New employee error rate.
8516    #[serde(default = "default_new_error")]
8517    pub new_employee_error_rate: f64,
8518
8519    /// Learning curve months.
8520    #[serde(default = "default_learning_months")]
8521    pub learning_curve_months: u32,
8522}
8523
8524fn default_new_error() -> f64 {
8525    0.08
8526}
8527
8528fn default_learning_months() -> u32 {
8529    6
8530}
8531
8532impl Default for ErrorDriftSchemaConfig {
8533    fn default() -> Self {
8534        Self {
8535            new_employee_error_rate: 0.08,
8536            learning_curve_months: 6,
8537        }
8538    }
8539}
8540
8541/// Collective behavior drift configuration.
8542#[derive(Debug, Clone, Serialize, Deserialize, Default)]
8543pub struct CollectiveBehaviorSchemaConfig {
8544    /// Automation adoption configuration.
8545    #[serde(default)]
8546    pub automation_adoption: AutomationAdoptionSchemaConfig,
8547}
8548
8549/// Automation adoption configuration.
8550#[derive(Debug, Clone, Serialize, Deserialize)]
8551pub struct AutomationAdoptionSchemaConfig {
8552    /// Enable S-curve adoption model.
8553    #[serde(default)]
8554    pub s_curve_enabled: bool,
8555
8556    /// Adoption midpoint in months.
8557    #[serde(default = "default_midpoint")]
8558    pub adoption_midpoint_months: u32,
8559
8560    /// Steepness of adoption curve.
8561    #[serde(default = "default_steepness")]
8562    pub steepness: f64,
8563}
8564
8565fn default_midpoint() -> u32 {
8566    24
8567}
8568
8569fn default_steepness() -> f64 {
8570    0.15
8571}
8572
8573impl Default for AutomationAdoptionSchemaConfig {
8574    fn default() -> Self {
8575        Self {
8576            s_curve_enabled: false,
8577            adoption_midpoint_months: 24,
8578            steepness: 0.15,
8579        }
8580    }
8581}
8582
8583// =============================================================================
8584// Market Drift Configuration
8585// =============================================================================
8586
8587/// Configuration for market drift (economic cycles, commodities, price shocks).
8588#[derive(Debug, Clone, Serialize, Deserialize, Default)]
8589pub struct MarketDriftSchemaConfig {
8590    /// Enable market drift.
8591    #[serde(default)]
8592    pub enabled: bool,
8593
8594    /// Economic cycle configuration.
8595    #[serde(default)]
8596    pub economic_cycle: MarketEconomicCycleSchemaConfig,
8597
8598    /// Industry-specific cycles.
8599    #[serde(default)]
8600    pub industry_cycles: std::collections::HashMap<String, IndustryCycleSchemaConfig>,
8601
8602    /// Commodity drift configuration.
8603    #[serde(default)]
8604    pub commodities: CommoditiesSchemaConfig,
8605}
8606
8607/// Market economic cycle configuration.
8608#[derive(Debug, Clone, Serialize, Deserialize)]
8609pub struct MarketEconomicCycleSchemaConfig {
8610    /// Enable economic cycle.
8611    #[serde(default)]
8612    pub enabled: bool,
8613
8614    /// Cycle type.
8615    #[serde(default)]
8616    pub cycle_type: CycleTypeSchemaConfig,
8617
8618    /// Cycle period in months.
8619    #[serde(default = "default_market_cycle_period")]
8620    pub period_months: u32,
8621
8622    /// Amplitude.
8623    #[serde(default = "default_market_amplitude")]
8624    pub amplitude: f64,
8625
8626    /// Recession configuration.
8627    #[serde(default)]
8628    pub recession: RecessionSchemaConfig,
8629}
8630
8631fn default_market_cycle_period() -> u32 {
8632    48
8633}
8634
8635fn default_market_amplitude() -> f64 {
8636    0.15
8637}
8638
8639impl Default for MarketEconomicCycleSchemaConfig {
8640    fn default() -> Self {
8641        Self {
8642            enabled: false,
8643            cycle_type: CycleTypeSchemaConfig::Sinusoidal,
8644            period_months: 48,
8645            amplitude: 0.15,
8646            recession: RecessionSchemaConfig::default(),
8647        }
8648    }
8649}
8650
8651/// Cycle type configuration.
8652#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
8653#[serde(rename_all = "snake_case")]
8654pub enum CycleTypeSchemaConfig {
8655    /// Sinusoidal cycle.
8656    #[default]
8657    Sinusoidal,
8658    /// Asymmetric cycle.
8659    Asymmetric,
8660    /// Mean-reverting cycle.
8661    MeanReverting,
8662}
8663
8664/// Recession configuration.
8665#[derive(Debug, Clone, Serialize, Deserialize)]
8666pub struct RecessionSchemaConfig {
8667    /// Enable recession simulation.
8668    #[serde(default)]
8669    pub enabled: bool,
8670
8671    /// Probability per year.
8672    #[serde(default = "default_recession_prob")]
8673    pub probability_per_year: f64,
8674
8675    /// Severity.
8676    #[serde(default)]
8677    pub severity: RecessionSeveritySchemaConfig,
8678
8679    /// Specific recession periods.
8680    #[serde(default)]
8681    pub recession_periods: Vec<RecessionPeriodSchemaConfig>,
8682}
8683
8684fn default_recession_prob() -> f64 {
8685    0.10
8686}
8687
8688impl Default for RecessionSchemaConfig {
8689    fn default() -> Self {
8690        Self {
8691            enabled: false,
8692            probability_per_year: 0.10,
8693            severity: RecessionSeveritySchemaConfig::Moderate,
8694            recession_periods: Vec::new(),
8695        }
8696    }
8697}
8698
8699/// Recession severity configuration.
8700#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
8701#[serde(rename_all = "snake_case")]
8702pub enum RecessionSeveritySchemaConfig {
8703    /// Mild recession.
8704    Mild,
8705    /// Moderate recession.
8706    #[default]
8707    Moderate,
8708    /// Severe recession.
8709    Severe,
8710}
8711
8712/// Recession period configuration.
8713#[derive(Debug, Clone, Serialize, Deserialize)]
8714pub struct RecessionPeriodSchemaConfig {
8715    /// Start month.
8716    pub start_month: u32,
8717    /// Duration in months.
8718    pub duration_months: u32,
8719}
8720
8721/// Industry cycle configuration.
8722#[derive(Debug, Clone, Serialize, Deserialize)]
8723pub struct IndustryCycleSchemaConfig {
8724    /// Period in months.
8725    #[serde(default = "default_industry_period")]
8726    pub period_months: u32,
8727
8728    /// Amplitude.
8729    #[serde(default = "default_industry_amp")]
8730    pub amplitude: f64,
8731}
8732
8733fn default_industry_period() -> u32 {
8734    36
8735}
8736
8737fn default_industry_amp() -> f64 {
8738    0.20
8739}
8740
8741/// Commodities drift configuration.
8742#[derive(Debug, Clone, Serialize, Deserialize, Default)]
8743pub struct CommoditiesSchemaConfig {
8744    /// Enable commodity drift.
8745    #[serde(default)]
8746    pub enabled: bool,
8747
8748    /// Commodity items.
8749    #[serde(default)]
8750    pub items: Vec<CommodityItemSchemaConfig>,
8751}
8752
8753/// Commodity item configuration.
8754#[derive(Debug, Clone, Serialize, Deserialize)]
8755pub struct CommodityItemSchemaConfig {
8756    /// Commodity name.
8757    pub name: String,
8758
8759    /// Volatility.
8760    #[serde(default = "default_volatility")]
8761    pub volatility: f64,
8762
8763    /// COGS pass-through.
8764    #[serde(default)]
8765    pub cogs_pass_through: f64,
8766
8767    /// Overhead pass-through.
8768    #[serde(default)]
8769    pub overhead_pass_through: f64,
8770}
8771
8772fn default_volatility() -> f64 {
8773    0.20
8774}
8775
8776// =============================================================================
8777// Drift Labeling Configuration
8778// =============================================================================
8779
8780/// Configuration for drift ground truth labeling.
8781#[derive(Debug, Clone, Serialize, Deserialize, Default)]
8782pub struct DriftLabelingSchemaConfig {
8783    /// Enable drift labeling.
8784    #[serde(default)]
8785    pub enabled: bool,
8786
8787    /// Statistical drift labeling.
8788    #[serde(default)]
8789    pub statistical: StatisticalDriftLabelingSchemaConfig,
8790
8791    /// Categorical drift labeling.
8792    #[serde(default)]
8793    pub categorical: CategoricalDriftLabelingSchemaConfig,
8794
8795    /// Temporal drift labeling.
8796    #[serde(default)]
8797    pub temporal: TemporalDriftLabelingSchemaConfig,
8798
8799    /// Regulatory calendar preset.
8800    #[serde(default)]
8801    pub regulatory_calendar_preset: Option<String>,
8802}
8803
8804/// Statistical drift labeling configuration.
8805#[derive(Debug, Clone, Serialize, Deserialize)]
8806pub struct StatisticalDriftLabelingSchemaConfig {
8807    /// Enable statistical drift labeling.
8808    #[serde(default = "default_true_val")]
8809    pub enabled: bool,
8810
8811    /// Minimum magnitude threshold.
8812    #[serde(default = "default_min_magnitude")]
8813    pub min_magnitude_threshold: f64,
8814}
8815
8816fn default_min_magnitude() -> f64 {
8817    0.05
8818}
8819
8820impl Default for StatisticalDriftLabelingSchemaConfig {
8821    fn default() -> Self {
8822        Self {
8823            enabled: true,
8824            min_magnitude_threshold: 0.05,
8825        }
8826    }
8827}
8828
8829/// Categorical drift labeling configuration.
8830#[derive(Debug, Clone, Serialize, Deserialize)]
8831pub struct CategoricalDriftLabelingSchemaConfig {
8832    /// Enable categorical drift labeling.
8833    #[serde(default = "default_true_val")]
8834    pub enabled: bool,
8835}
8836
8837impl Default for CategoricalDriftLabelingSchemaConfig {
8838    fn default() -> Self {
8839        Self { enabled: true }
8840    }
8841}
8842
8843/// Temporal drift labeling configuration.
8844#[derive(Debug, Clone, Serialize, Deserialize)]
8845pub struct TemporalDriftLabelingSchemaConfig {
8846    /// Enable temporal drift labeling.
8847    #[serde(default = "default_true_val")]
8848    pub enabled: bool,
8849}
8850
8851impl Default for TemporalDriftLabelingSchemaConfig {
8852    fn default() -> Self {
8853        Self { enabled: true }
8854    }
8855}
8856
8857// =============================================================================
8858// Enhanced Anomaly Injection Configuration
8859// =============================================================================
8860
8861/// Enhanced anomaly injection configuration.
8862///
8863/// Provides comprehensive anomaly injection capabilities including:
8864/// - Multi-stage fraud schemes (embezzlement, revenue manipulation, kickbacks)
8865/// - Correlated anomaly injection (co-occurrence patterns, error cascades)
8866/// - Near-miss generation for false positive reduction
8867/// - Detection difficulty classification
8868/// - Context-aware injection based on entity behavior
8869#[derive(Debug, Clone, Serialize, Deserialize, Default)]
8870pub struct EnhancedAnomalyConfig {
8871    /// Enable enhanced anomaly injection.
8872    #[serde(default)]
8873    pub enabled: bool,
8874
8875    /// Base anomaly rates.
8876    #[serde(default)]
8877    pub rates: AnomalyRateConfig,
8878
8879    /// Multi-stage fraud scheme configuration.
8880    #[serde(default)]
8881    pub multi_stage_schemes: MultiStageSchemeConfig,
8882
8883    /// Correlated anomaly injection configuration.
8884    #[serde(default)]
8885    pub correlated_injection: CorrelatedInjectionConfig,
8886
8887    /// Near-miss generation configuration.
8888    #[serde(default)]
8889    pub near_miss: NearMissConfig,
8890
8891    /// Detection difficulty classification configuration.
8892    #[serde(default)]
8893    pub difficulty_classification: DifficultyClassificationConfig,
8894
8895    /// Context-aware injection configuration.
8896    #[serde(default)]
8897    pub context_aware: ContextAwareConfig,
8898
8899    /// Enhanced labeling configuration.
8900    #[serde(default)]
8901    pub labeling: EnhancedLabelingConfig,
8902}
8903
8904/// Base anomaly rate configuration.
8905#[derive(Debug, Clone, Serialize, Deserialize)]
8906pub struct AnomalyRateConfig {
8907    /// Total anomaly rate (0.0 to 1.0).
8908    #[serde(default = "default_total_anomaly_rate")]
8909    pub total_rate: f64,
8910
8911    /// Fraud anomaly rate.
8912    #[serde(default = "default_fraud_anomaly_rate")]
8913    pub fraud_rate: f64,
8914
8915    /// Error anomaly rate.
8916    #[serde(default = "default_error_anomaly_rate")]
8917    pub error_rate: f64,
8918
8919    /// Process issue rate.
8920    #[serde(default = "default_process_anomaly_rate")]
8921    pub process_rate: f64,
8922}
8923
8924fn default_total_anomaly_rate() -> f64 {
8925    0.03
8926}
8927fn default_fraud_anomaly_rate() -> f64 {
8928    0.01
8929}
8930fn default_error_anomaly_rate() -> f64 {
8931    0.015
8932}
8933fn default_process_anomaly_rate() -> f64 {
8934    0.005
8935}
8936
8937impl Default for AnomalyRateConfig {
8938    fn default() -> Self {
8939        Self {
8940            total_rate: default_total_anomaly_rate(),
8941            fraud_rate: default_fraud_anomaly_rate(),
8942            error_rate: default_error_anomaly_rate(),
8943            process_rate: default_process_anomaly_rate(),
8944        }
8945    }
8946}
8947
8948/// Multi-stage fraud scheme configuration.
8949#[derive(Debug, Clone, Serialize, Deserialize, Default)]
8950pub struct MultiStageSchemeConfig {
8951    /// Enable multi-stage fraud schemes.
8952    #[serde(default)]
8953    pub enabled: bool,
8954
8955    /// Embezzlement scheme configuration.
8956    #[serde(default)]
8957    pub embezzlement: EmbezzlementSchemeConfig,
8958
8959    /// Revenue manipulation scheme configuration.
8960    #[serde(default)]
8961    pub revenue_manipulation: RevenueManipulationSchemeConfig,
8962
8963    /// Vendor kickback scheme configuration.
8964    #[serde(default)]
8965    pub kickback: KickbackSchemeConfig,
8966}
8967
8968/// Embezzlement scheme configuration.
8969#[derive(Debug, Clone, Serialize, Deserialize)]
8970pub struct EmbezzlementSchemeConfig {
8971    /// Probability of starting an embezzlement scheme per perpetrator per year.
8972    #[serde(default = "default_embezzlement_probability")]
8973    pub probability: f64,
8974
8975    /// Testing stage configuration.
8976    #[serde(default)]
8977    pub testing_stage: SchemeStageConfig,
8978
8979    /// Escalation stage configuration.
8980    #[serde(default)]
8981    pub escalation_stage: SchemeStageConfig,
8982
8983    /// Acceleration stage configuration.
8984    #[serde(default)]
8985    pub acceleration_stage: SchemeStageConfig,
8986
8987    /// Desperation stage configuration.
8988    #[serde(default)]
8989    pub desperation_stage: SchemeStageConfig,
8990}
8991
8992fn default_embezzlement_probability() -> f64 {
8993    0.02
8994}
8995
8996impl Default for EmbezzlementSchemeConfig {
8997    fn default() -> Self {
8998        Self {
8999            probability: default_embezzlement_probability(),
9000            testing_stage: SchemeStageConfig {
9001                duration_months: 2,
9002                amount_min: 100.0,
9003                amount_max: 500.0,
9004                transaction_count_min: 2,
9005                transaction_count_max: 5,
9006                difficulty: "hard".to_string(),
9007            },
9008            escalation_stage: SchemeStageConfig {
9009                duration_months: 6,
9010                amount_min: 500.0,
9011                amount_max: 2000.0,
9012                transaction_count_min: 3,
9013                transaction_count_max: 8,
9014                difficulty: "moderate".to_string(),
9015            },
9016            acceleration_stage: SchemeStageConfig {
9017                duration_months: 3,
9018                amount_min: 2000.0,
9019                amount_max: 10000.0,
9020                transaction_count_min: 5,
9021                transaction_count_max: 12,
9022                difficulty: "easy".to_string(),
9023            },
9024            desperation_stage: SchemeStageConfig {
9025                duration_months: 1,
9026                amount_min: 10000.0,
9027                amount_max: 50000.0,
9028                transaction_count_min: 3,
9029                transaction_count_max: 6,
9030                difficulty: "trivial".to_string(),
9031            },
9032        }
9033    }
9034}
9035
9036/// Revenue manipulation scheme configuration.
9037#[derive(Debug, Clone, Serialize, Deserialize)]
9038pub struct RevenueManipulationSchemeConfig {
9039    /// Probability of starting a revenue manipulation scheme per period.
9040    #[serde(default = "default_revenue_manipulation_probability")]
9041    pub probability: f64,
9042
9043    /// Early revenue recognition inflation target (Q4).
9044    #[serde(default = "default_early_recognition_target")]
9045    pub early_recognition_target: f64,
9046
9047    /// Expense deferral inflation target (Q1).
9048    #[serde(default = "default_expense_deferral_target")]
9049    pub expense_deferral_target: f64,
9050
9051    /// Reserve release inflation target (Q2).
9052    #[serde(default = "default_reserve_release_target")]
9053    pub reserve_release_target: f64,
9054
9055    /// Channel stuffing inflation target (Q4).
9056    #[serde(default = "default_channel_stuffing_target")]
9057    pub channel_stuffing_target: f64,
9058}
9059
9060fn default_revenue_manipulation_probability() -> f64 {
9061    0.01
9062}
9063fn default_early_recognition_target() -> f64 {
9064    0.02
9065}
9066fn default_expense_deferral_target() -> f64 {
9067    0.03
9068}
9069fn default_reserve_release_target() -> f64 {
9070    0.02
9071}
9072fn default_channel_stuffing_target() -> f64 {
9073    0.05
9074}
9075
9076impl Default for RevenueManipulationSchemeConfig {
9077    fn default() -> Self {
9078        Self {
9079            probability: default_revenue_manipulation_probability(),
9080            early_recognition_target: default_early_recognition_target(),
9081            expense_deferral_target: default_expense_deferral_target(),
9082            reserve_release_target: default_reserve_release_target(),
9083            channel_stuffing_target: default_channel_stuffing_target(),
9084        }
9085    }
9086}
9087
9088/// Vendor kickback scheme configuration.
9089#[derive(Debug, Clone, Serialize, Deserialize)]
9090pub struct KickbackSchemeConfig {
9091    /// Probability of starting a kickback scheme.
9092    #[serde(default = "default_kickback_probability")]
9093    pub probability: f64,
9094
9095    /// Minimum price inflation percentage.
9096    #[serde(default = "default_kickback_inflation_min")]
9097    pub inflation_min: f64,
9098
9099    /// Maximum price inflation percentage.
9100    #[serde(default = "default_kickback_inflation_max")]
9101    pub inflation_max: f64,
9102
9103    /// Kickback percentage (of inflation).
9104    #[serde(default = "default_kickback_percent")]
9105    pub kickback_percent: f64,
9106
9107    /// Setup duration in months.
9108    #[serde(default = "default_kickback_setup_months")]
9109    pub setup_months: u32,
9110
9111    /// Main operation duration in months.
9112    #[serde(default = "default_kickback_operation_months")]
9113    pub operation_months: u32,
9114}
9115
9116fn default_kickback_probability() -> f64 {
9117    0.01
9118}
9119fn default_kickback_inflation_min() -> f64 {
9120    0.10
9121}
9122fn default_kickback_inflation_max() -> f64 {
9123    0.25
9124}
9125fn default_kickback_percent() -> f64 {
9126    0.50
9127}
9128fn default_kickback_setup_months() -> u32 {
9129    3
9130}
9131fn default_kickback_operation_months() -> u32 {
9132    12
9133}
9134
9135impl Default for KickbackSchemeConfig {
9136    fn default() -> Self {
9137        Self {
9138            probability: default_kickback_probability(),
9139            inflation_min: default_kickback_inflation_min(),
9140            inflation_max: default_kickback_inflation_max(),
9141            kickback_percent: default_kickback_percent(),
9142            setup_months: default_kickback_setup_months(),
9143            operation_months: default_kickback_operation_months(),
9144        }
9145    }
9146}
9147
9148/// Individual scheme stage configuration.
9149#[derive(Debug, Clone, Serialize, Deserialize)]
9150pub struct SchemeStageConfig {
9151    /// Duration in months.
9152    pub duration_months: u32,
9153
9154    /// Minimum transaction amount.
9155    pub amount_min: f64,
9156
9157    /// Maximum transaction amount.
9158    pub amount_max: f64,
9159
9160    /// Minimum number of transactions.
9161    pub transaction_count_min: u32,
9162
9163    /// Maximum number of transactions.
9164    pub transaction_count_max: u32,
9165
9166    /// Detection difficulty level (trivial, easy, moderate, hard, expert).
9167    pub difficulty: String,
9168}
9169
9170impl Default for SchemeStageConfig {
9171    fn default() -> Self {
9172        Self {
9173            duration_months: 3,
9174            amount_min: 100.0,
9175            amount_max: 1000.0,
9176            transaction_count_min: 2,
9177            transaction_count_max: 10,
9178            difficulty: "moderate".to_string(),
9179        }
9180    }
9181}
9182
9183/// Correlated anomaly injection configuration.
9184#[derive(Debug, Clone, Serialize, Deserialize)]
9185pub struct CorrelatedInjectionConfig {
9186    /// Enable correlated anomaly injection.
9187    #[serde(default)]
9188    pub enabled: bool,
9189
9190    /// Enable fraud concealment co-occurrence patterns.
9191    #[serde(default = "default_true_val")]
9192    pub fraud_concealment: bool,
9193
9194    /// Enable error cascade patterns.
9195    #[serde(default = "default_true_val")]
9196    pub error_cascade: bool,
9197
9198    /// Enable temporal clustering (period-end spikes).
9199    #[serde(default = "default_true_val")]
9200    pub temporal_clustering: bool,
9201
9202    /// Temporal clustering configuration.
9203    #[serde(default)]
9204    pub temporal_clustering_config: TemporalClusteringConfig,
9205
9206    /// Co-occurrence patterns.
9207    #[serde(default)]
9208    pub co_occurrence_patterns: Vec<CoOccurrencePatternConfig>,
9209}
9210
9211impl Default for CorrelatedInjectionConfig {
9212    fn default() -> Self {
9213        Self {
9214            enabled: false,
9215            fraud_concealment: true,
9216            error_cascade: true,
9217            temporal_clustering: true,
9218            temporal_clustering_config: TemporalClusteringConfig::default(),
9219            co_occurrence_patterns: Vec::new(),
9220        }
9221    }
9222}
9223
9224/// Temporal clustering configuration.
9225#[derive(Debug, Clone, Serialize, Deserialize)]
9226pub struct TemporalClusteringConfig {
9227    /// Period-end error multiplier.
9228    #[serde(default = "default_period_end_multiplier")]
9229    pub period_end_multiplier: f64,
9230
9231    /// Number of business days before period end to apply multiplier.
9232    #[serde(default = "default_period_end_days")]
9233    pub period_end_days: u32,
9234
9235    /// Quarter-end additional multiplier.
9236    #[serde(default = "default_quarter_end_multiplier")]
9237    pub quarter_end_multiplier: f64,
9238
9239    /// Year-end additional multiplier.
9240    #[serde(default = "default_year_end_multiplier")]
9241    pub year_end_multiplier: f64,
9242}
9243
9244fn default_period_end_multiplier() -> f64 {
9245    2.5
9246}
9247fn default_period_end_days() -> u32 {
9248    5
9249}
9250fn default_quarter_end_multiplier() -> f64 {
9251    1.5
9252}
9253fn default_year_end_multiplier() -> f64 {
9254    2.0
9255}
9256
9257impl Default for TemporalClusteringConfig {
9258    fn default() -> Self {
9259        Self {
9260            period_end_multiplier: default_period_end_multiplier(),
9261            period_end_days: default_period_end_days(),
9262            quarter_end_multiplier: default_quarter_end_multiplier(),
9263            year_end_multiplier: default_year_end_multiplier(),
9264        }
9265    }
9266}
9267
9268/// Co-occurrence pattern configuration.
9269#[derive(Debug, Clone, Serialize, Deserialize)]
9270pub struct CoOccurrencePatternConfig {
9271    /// Pattern name.
9272    pub name: String,
9273
9274    /// Primary anomaly type that triggers the pattern.
9275    pub primary_type: String,
9276
9277    /// Correlated anomalies.
9278    pub correlated: Vec<CorrelatedAnomalyConfig>,
9279}
9280
9281/// Correlated anomaly configuration.
9282#[derive(Debug, Clone, Serialize, Deserialize)]
9283pub struct CorrelatedAnomalyConfig {
9284    /// Anomaly type.
9285    pub anomaly_type: String,
9286
9287    /// Probability of occurrence (0.0 to 1.0).
9288    pub probability: f64,
9289
9290    /// Minimum lag in days.
9291    pub lag_days_min: i32,
9292
9293    /// Maximum lag in days.
9294    pub lag_days_max: i32,
9295}
9296
9297/// Near-miss generation configuration.
9298#[derive(Debug, Clone, Serialize, Deserialize)]
9299pub struct NearMissConfig {
9300    /// Enable near-miss generation.
9301    #[serde(default)]
9302    pub enabled: bool,
9303
9304    /// Proportion of "anomalies" that are actually near-misses (0.0 to 1.0).
9305    #[serde(default = "default_near_miss_proportion")]
9306    pub proportion: f64,
9307
9308    /// Enable near-duplicate pattern.
9309    #[serde(default = "default_true_val")]
9310    pub near_duplicate: bool,
9311
9312    /// Near-duplicate date difference range in days.
9313    #[serde(default)]
9314    pub near_duplicate_days: NearDuplicateDaysConfig,
9315
9316    /// Enable threshold proximity pattern.
9317    #[serde(default = "default_true_val")]
9318    pub threshold_proximity: bool,
9319
9320    /// Threshold proximity range (e.g., 0.90-0.99 of threshold).
9321    #[serde(default)]
9322    pub threshold_proximity_range: ThresholdProximityRangeConfig,
9323
9324    /// Enable unusual but legitimate patterns.
9325    #[serde(default = "default_true_val")]
9326    pub unusual_legitimate: bool,
9327
9328    /// Types of unusual legitimate patterns to generate.
9329    #[serde(default = "default_unusual_legitimate_types")]
9330    pub unusual_legitimate_types: Vec<String>,
9331
9332    /// Enable corrected error patterns.
9333    #[serde(default = "default_true_val")]
9334    pub corrected_errors: bool,
9335
9336    /// Corrected error correction lag range in days.
9337    #[serde(default)]
9338    pub corrected_error_lag: CorrectedErrorLagConfig,
9339}
9340
9341fn default_near_miss_proportion() -> f64 {
9342    0.30
9343}
9344
9345fn default_unusual_legitimate_types() -> Vec<String> {
9346    vec![
9347        "year_end_bonus".to_string(),
9348        "contract_prepayment".to_string(),
9349        "insurance_claim".to_string(),
9350        "settlement_payment".to_string(),
9351    ]
9352}
9353
9354impl Default for NearMissConfig {
9355    fn default() -> Self {
9356        Self {
9357            enabled: false,
9358            proportion: default_near_miss_proportion(),
9359            near_duplicate: true,
9360            near_duplicate_days: NearDuplicateDaysConfig::default(),
9361            threshold_proximity: true,
9362            threshold_proximity_range: ThresholdProximityRangeConfig::default(),
9363            unusual_legitimate: true,
9364            unusual_legitimate_types: default_unusual_legitimate_types(),
9365            corrected_errors: true,
9366            corrected_error_lag: CorrectedErrorLagConfig::default(),
9367        }
9368    }
9369}
9370
9371/// Near-duplicate days configuration.
9372#[derive(Debug, Clone, Serialize, Deserialize)]
9373pub struct NearDuplicateDaysConfig {
9374    /// Minimum days apart.
9375    #[serde(default = "default_near_duplicate_min")]
9376    pub min: u32,
9377
9378    /// Maximum days apart.
9379    #[serde(default = "default_near_duplicate_max")]
9380    pub max: u32,
9381}
9382
9383fn default_near_duplicate_min() -> u32 {
9384    1
9385}
9386fn default_near_duplicate_max() -> u32 {
9387    3
9388}
9389
9390impl Default for NearDuplicateDaysConfig {
9391    fn default() -> Self {
9392        Self {
9393            min: default_near_duplicate_min(),
9394            max: default_near_duplicate_max(),
9395        }
9396    }
9397}
9398
9399/// Threshold proximity range configuration.
9400#[derive(Debug, Clone, Serialize, Deserialize)]
9401pub struct ThresholdProximityRangeConfig {
9402    /// Minimum proximity (e.g., 0.90 = 90% of threshold).
9403    #[serde(default = "default_threshold_proximity_min")]
9404    pub min: f64,
9405
9406    /// Maximum proximity (e.g., 0.99 = 99% of threshold).
9407    #[serde(default = "default_threshold_proximity_max")]
9408    pub max: f64,
9409}
9410
9411fn default_threshold_proximity_min() -> f64 {
9412    0.90
9413}
9414fn default_threshold_proximity_max() -> f64 {
9415    0.99
9416}
9417
9418impl Default for ThresholdProximityRangeConfig {
9419    fn default() -> Self {
9420        Self {
9421            min: default_threshold_proximity_min(),
9422            max: default_threshold_proximity_max(),
9423        }
9424    }
9425}
9426
9427/// Corrected error lag configuration.
9428#[derive(Debug, Clone, Serialize, Deserialize)]
9429pub struct CorrectedErrorLagConfig {
9430    /// Minimum correction lag in days.
9431    #[serde(default = "default_corrected_error_lag_min")]
9432    pub min: u32,
9433
9434    /// Maximum correction lag in days.
9435    #[serde(default = "default_corrected_error_lag_max")]
9436    pub max: u32,
9437}
9438
9439fn default_corrected_error_lag_min() -> u32 {
9440    1
9441}
9442fn default_corrected_error_lag_max() -> u32 {
9443    5
9444}
9445
9446impl Default for CorrectedErrorLagConfig {
9447    fn default() -> Self {
9448        Self {
9449            min: default_corrected_error_lag_min(),
9450            max: default_corrected_error_lag_max(),
9451        }
9452    }
9453}
9454
9455/// Detection difficulty classification configuration.
9456#[derive(Debug, Clone, Serialize, Deserialize)]
9457pub struct DifficultyClassificationConfig {
9458    /// Enable detection difficulty classification.
9459    #[serde(default)]
9460    pub enabled: bool,
9461
9462    /// Target distribution of difficulty levels.
9463    #[serde(default)]
9464    pub target_distribution: DifficultyDistributionConfig,
9465}
9466
9467impl Default for DifficultyClassificationConfig {
9468    fn default() -> Self {
9469        Self {
9470            enabled: true,
9471            target_distribution: DifficultyDistributionConfig::default(),
9472        }
9473    }
9474}
9475
9476/// Target distribution of detection difficulty levels.
9477#[derive(Debug, Clone, Serialize, Deserialize)]
9478pub struct DifficultyDistributionConfig {
9479    /// Proportion of trivial anomalies (expected 99% detection).
9480    #[serde(default = "default_difficulty_trivial")]
9481    pub trivial: f64,
9482
9483    /// Proportion of easy anomalies (expected 90% detection).
9484    #[serde(default = "default_difficulty_easy")]
9485    pub easy: f64,
9486
9487    /// Proportion of moderate anomalies (expected 70% detection).
9488    #[serde(default = "default_difficulty_moderate")]
9489    pub moderate: f64,
9490
9491    /// Proportion of hard anomalies (expected 40% detection).
9492    #[serde(default = "default_difficulty_hard")]
9493    pub hard: f64,
9494
9495    /// Proportion of expert anomalies (expected 15% detection).
9496    #[serde(default = "default_difficulty_expert")]
9497    pub expert: f64,
9498}
9499
9500fn default_difficulty_trivial() -> f64 {
9501    0.15
9502}
9503fn default_difficulty_easy() -> f64 {
9504    0.25
9505}
9506fn default_difficulty_moderate() -> f64 {
9507    0.30
9508}
9509fn default_difficulty_hard() -> f64 {
9510    0.20
9511}
9512fn default_difficulty_expert() -> f64 {
9513    0.10
9514}
9515
9516impl Default for DifficultyDistributionConfig {
9517    fn default() -> Self {
9518        Self {
9519            trivial: default_difficulty_trivial(),
9520            easy: default_difficulty_easy(),
9521            moderate: default_difficulty_moderate(),
9522            hard: default_difficulty_hard(),
9523            expert: default_difficulty_expert(),
9524        }
9525    }
9526}
9527
9528/// Context-aware injection configuration.
9529#[derive(Debug, Clone, Serialize, Deserialize, Default)]
9530pub struct ContextAwareConfig {
9531    /// Enable context-aware injection.
9532    #[serde(default)]
9533    pub enabled: bool,
9534
9535    /// Vendor-specific anomaly rules.
9536    #[serde(default)]
9537    pub vendor_rules: VendorAnomalyRulesConfig,
9538
9539    /// Employee-specific anomaly rules.
9540    #[serde(default)]
9541    pub employee_rules: EmployeeAnomalyRulesConfig,
9542
9543    /// Account-specific anomaly rules.
9544    #[serde(default)]
9545    pub account_rules: AccountAnomalyRulesConfig,
9546
9547    /// Behavioral baseline configuration.
9548    #[serde(default)]
9549    pub behavioral_baseline: BehavioralBaselineConfig,
9550}
9551
9552/// Vendor-specific anomaly rules configuration.
9553#[derive(Debug, Clone, Serialize, Deserialize)]
9554pub struct VendorAnomalyRulesConfig {
9555    /// Error rate multiplier for new vendors (< threshold days).
9556    #[serde(default = "default_new_vendor_multiplier")]
9557    pub new_vendor_error_multiplier: f64,
9558
9559    /// Days threshold for "new" vendor classification.
9560    #[serde(default = "default_new_vendor_threshold")]
9561    pub new_vendor_threshold_days: u32,
9562
9563    /// Error rate multiplier for international vendors.
9564    #[serde(default = "default_international_multiplier")]
9565    pub international_error_multiplier: f64,
9566
9567    /// Strategic vendor anomaly types (may differ from general vendors).
9568    #[serde(default = "default_strategic_vendor_types")]
9569    pub strategic_vendor_anomaly_types: Vec<String>,
9570}
9571
9572fn default_new_vendor_multiplier() -> f64 {
9573    2.5
9574}
9575fn default_new_vendor_threshold() -> u32 {
9576    90
9577}
9578fn default_international_multiplier() -> f64 {
9579    1.5
9580}
9581fn default_strategic_vendor_types() -> Vec<String> {
9582    vec![
9583        "pricing_dispute".to_string(),
9584        "contract_violation".to_string(),
9585    ]
9586}
9587
9588impl Default for VendorAnomalyRulesConfig {
9589    fn default() -> Self {
9590        Self {
9591            new_vendor_error_multiplier: default_new_vendor_multiplier(),
9592            new_vendor_threshold_days: default_new_vendor_threshold(),
9593            international_error_multiplier: default_international_multiplier(),
9594            strategic_vendor_anomaly_types: default_strategic_vendor_types(),
9595        }
9596    }
9597}
9598
9599/// Employee-specific anomaly rules configuration.
9600#[derive(Debug, Clone, Serialize, Deserialize)]
9601pub struct EmployeeAnomalyRulesConfig {
9602    /// Error rate for new employees (< threshold days).
9603    #[serde(default = "default_new_employee_rate")]
9604    pub new_employee_error_rate: f64,
9605
9606    /// Days threshold for "new" employee classification.
9607    #[serde(default = "default_new_employee_threshold")]
9608    pub new_employee_threshold_days: u32,
9609
9610    /// Transaction volume threshold for fatigue errors.
9611    #[serde(default = "default_volume_fatigue_threshold")]
9612    pub volume_fatigue_threshold: u32,
9613
9614    /// Error rate multiplier when primary approver is absent.
9615    #[serde(default = "default_coverage_multiplier")]
9616    pub coverage_error_multiplier: f64,
9617}
9618
9619fn default_new_employee_rate() -> f64 {
9620    0.05
9621}
9622fn default_new_employee_threshold() -> u32 {
9623    180
9624}
9625fn default_volume_fatigue_threshold() -> u32 {
9626    50
9627}
9628fn default_coverage_multiplier() -> f64 {
9629    1.8
9630}
9631
9632impl Default for EmployeeAnomalyRulesConfig {
9633    fn default() -> Self {
9634        Self {
9635            new_employee_error_rate: default_new_employee_rate(),
9636            new_employee_threshold_days: default_new_employee_threshold(),
9637            volume_fatigue_threshold: default_volume_fatigue_threshold(),
9638            coverage_error_multiplier: default_coverage_multiplier(),
9639        }
9640    }
9641}
9642
9643/// Account-specific anomaly rules configuration.
9644#[derive(Debug, Clone, Serialize, Deserialize)]
9645pub struct AccountAnomalyRulesConfig {
9646    /// Error rate multiplier for high-risk accounts.
9647    #[serde(default = "default_high_risk_multiplier")]
9648    pub high_risk_account_multiplier: f64,
9649
9650    /// Account codes considered high-risk.
9651    #[serde(default = "default_high_risk_accounts")]
9652    pub high_risk_accounts: Vec<String>,
9653
9654    /// Error rate multiplier for suspense accounts.
9655    #[serde(default = "default_suspense_multiplier")]
9656    pub suspense_account_multiplier: f64,
9657
9658    /// Account codes considered suspense accounts.
9659    #[serde(default = "default_suspense_accounts")]
9660    pub suspense_accounts: Vec<String>,
9661
9662    /// Error rate multiplier for intercompany accounts.
9663    #[serde(default = "default_intercompany_multiplier")]
9664    pub intercompany_account_multiplier: f64,
9665}
9666
9667fn default_high_risk_multiplier() -> f64 {
9668    2.0
9669}
9670fn default_high_risk_accounts() -> Vec<String> {
9671    vec![
9672        "1100".to_string(), // AR Control
9673        "2000".to_string(), // AP Control
9674        "3000".to_string(), // Cash
9675    ]
9676}
9677fn default_suspense_multiplier() -> f64 {
9678    3.0
9679}
9680fn default_suspense_accounts() -> Vec<String> {
9681    vec!["9999".to_string(), "9998".to_string()]
9682}
9683fn default_intercompany_multiplier() -> f64 {
9684    1.5
9685}
9686
9687impl Default for AccountAnomalyRulesConfig {
9688    fn default() -> Self {
9689        Self {
9690            high_risk_account_multiplier: default_high_risk_multiplier(),
9691            high_risk_accounts: default_high_risk_accounts(),
9692            suspense_account_multiplier: default_suspense_multiplier(),
9693            suspense_accounts: default_suspense_accounts(),
9694            intercompany_account_multiplier: default_intercompany_multiplier(),
9695        }
9696    }
9697}
9698
9699/// Behavioral baseline configuration.
9700#[derive(Debug, Clone, Serialize, Deserialize)]
9701pub struct BehavioralBaselineConfig {
9702    /// Enable behavioral baseline tracking.
9703    #[serde(default)]
9704    pub enabled: bool,
9705
9706    /// Number of days to build baseline from.
9707    #[serde(default = "default_baseline_period")]
9708    pub baseline_period_days: u32,
9709
9710    /// Standard deviation threshold for amount anomalies.
9711    #[serde(default = "default_deviation_threshold")]
9712    pub deviation_threshold_std: f64,
9713
9714    /// Standard deviation threshold for frequency anomalies.
9715    #[serde(default = "default_frequency_deviation")]
9716    pub frequency_deviation_threshold: f64,
9717}
9718
9719fn default_baseline_period() -> u32 {
9720    90
9721}
9722fn default_deviation_threshold() -> f64 {
9723    3.0
9724}
9725fn default_frequency_deviation() -> f64 {
9726    2.0
9727}
9728
9729impl Default for BehavioralBaselineConfig {
9730    fn default() -> Self {
9731        Self {
9732            enabled: false,
9733            baseline_period_days: default_baseline_period(),
9734            deviation_threshold_std: default_deviation_threshold(),
9735            frequency_deviation_threshold: default_frequency_deviation(),
9736        }
9737    }
9738}
9739
9740/// Enhanced labeling configuration.
9741#[derive(Debug, Clone, Serialize, Deserialize)]
9742pub struct EnhancedLabelingConfig {
9743    /// Enable severity scoring.
9744    #[serde(default = "default_true_val")]
9745    pub severity_scoring: bool,
9746
9747    /// Enable difficulty classification.
9748    #[serde(default = "default_true_val")]
9749    pub difficulty_classification: bool,
9750
9751    /// Materiality thresholds for severity classification.
9752    #[serde(default)]
9753    pub materiality_thresholds: MaterialityThresholdsConfig,
9754}
9755
9756impl Default for EnhancedLabelingConfig {
9757    fn default() -> Self {
9758        Self {
9759            severity_scoring: true,
9760            difficulty_classification: true,
9761            materiality_thresholds: MaterialityThresholdsConfig::default(),
9762        }
9763    }
9764}
9765
9766/// Materiality thresholds configuration.
9767#[derive(Debug, Clone, Serialize, Deserialize)]
9768pub struct MaterialityThresholdsConfig {
9769    /// Threshold for trivial impact (as percentage of total).
9770    #[serde(default = "default_materiality_trivial")]
9771    pub trivial: f64,
9772
9773    /// Threshold for immaterial impact.
9774    #[serde(default = "default_materiality_immaterial")]
9775    pub immaterial: f64,
9776
9777    /// Threshold for material impact.
9778    #[serde(default = "default_materiality_material")]
9779    pub material: f64,
9780
9781    /// Threshold for highly material impact.
9782    #[serde(default = "default_materiality_highly_material")]
9783    pub highly_material: f64,
9784}
9785
9786fn default_materiality_trivial() -> f64 {
9787    0.001
9788}
9789fn default_materiality_immaterial() -> f64 {
9790    0.01
9791}
9792fn default_materiality_material() -> f64 {
9793    0.05
9794}
9795fn default_materiality_highly_material() -> f64 {
9796    0.10
9797}
9798
9799impl Default for MaterialityThresholdsConfig {
9800    fn default() -> Self {
9801        Self {
9802            trivial: default_materiality_trivial(),
9803            immaterial: default_materiality_immaterial(),
9804            material: default_materiality_material(),
9805            highly_material: default_materiality_highly_material(),
9806        }
9807    }
9808}
9809
9810// =============================================================================
9811// Industry-Specific Configuration
9812// =============================================================================
9813
9814/// Industry-specific transaction and anomaly generation configuration.
9815///
9816/// This configuration enables generation of industry-authentic:
9817/// - Transaction types with appropriate terminology
9818/// - Master data (BOM, routings, clinical codes, etc.)
9819/// - Industry-specific anomaly patterns
9820/// - Regulatory framework compliance
9821#[derive(Debug, Clone, Serialize, Deserialize, Default)]
9822pub struct IndustrySpecificConfig {
9823    /// Enable industry-specific generation.
9824    #[serde(default)]
9825    pub enabled: bool,
9826
9827    /// Manufacturing industry settings.
9828    #[serde(default)]
9829    pub manufacturing: ManufacturingConfig,
9830
9831    /// Retail industry settings.
9832    #[serde(default)]
9833    pub retail: RetailConfig,
9834
9835    /// Healthcare industry settings.
9836    #[serde(default)]
9837    pub healthcare: HealthcareConfig,
9838
9839    /// Technology industry settings.
9840    #[serde(default)]
9841    pub technology: TechnologyConfig,
9842
9843    /// Financial services industry settings.
9844    #[serde(default)]
9845    pub financial_services: FinancialServicesConfig,
9846
9847    /// Professional services industry settings.
9848    #[serde(default)]
9849    pub professional_services: ProfessionalServicesConfig,
9850}
9851
9852/// Manufacturing industry configuration.
9853#[derive(Debug, Clone, Serialize, Deserialize)]
9854pub struct ManufacturingConfig {
9855    /// Enable manufacturing-specific generation.
9856    #[serde(default)]
9857    pub enabled: bool,
9858
9859    /// Bill of Materials depth (typical: 3-7).
9860    #[serde(default = "default_bom_depth")]
9861    pub bom_depth: u32,
9862
9863    /// Whether to use just-in-time inventory.
9864    #[serde(default)]
9865    pub just_in_time: bool,
9866
9867    /// Production order types to generate.
9868    #[serde(default = "default_production_order_types")]
9869    pub production_order_types: Vec<String>,
9870
9871    /// Quality framework (ISO_9001, Six_Sigma, etc.).
9872    #[serde(default)]
9873    pub quality_framework: Option<String>,
9874
9875    /// Number of supplier tiers to model (1-3).
9876    #[serde(default = "default_supplier_tiers")]
9877    pub supplier_tiers: u32,
9878
9879    /// Standard cost update frequency.
9880    #[serde(default = "default_cost_frequency")]
9881    pub standard_cost_frequency: String,
9882
9883    /// Target yield rate (0.95-0.99 typical).
9884    #[serde(default = "default_yield_rate")]
9885    pub target_yield_rate: f64,
9886
9887    /// Scrap percentage threshold for alerts.
9888    #[serde(default = "default_scrap_threshold")]
9889    pub scrap_alert_threshold: f64,
9890
9891    /// Manufacturing anomaly injection rates.
9892    #[serde(default)]
9893    pub anomaly_rates: ManufacturingAnomalyRates,
9894
9895    /// Cost accounting configuration (WIP → FG → COGS pipeline).
9896    #[serde(default)]
9897    pub cost_accounting: ManufacturingCostAccountingConfig,
9898}
9899
9900/// Configuration for manufacturing cost accounting JE generation.
9901#[derive(Debug, Clone, Serialize, Deserialize)]
9902pub struct ManufacturingCostAccountingConfig {
9903    /// Enable multi-stage cost flow (WIP → FG → COGS) instead of flat JEs.
9904    #[serde(default = "default_true")]
9905    pub enabled: bool,
9906
9907    /// Generate standard cost variance JEs.
9908    #[serde(default = "default_true")]
9909    pub variance_accounts_enabled: bool,
9910
9911    /// Generate warranty provisions from quality inspection failures.
9912    #[serde(default = "default_true")]
9913    pub warranty_provisions_enabled: bool,
9914
9915    /// Minimum defect rate (0.0-1.0) to trigger warranty provision generation.
9916    #[serde(default = "default_warranty_defect_threshold")]
9917    pub warranty_defect_threshold: f64,
9918}
9919
9920fn default_warranty_defect_threshold() -> f64 {
9921    0.01
9922}
9923
9924impl Default for ManufacturingCostAccountingConfig {
9925    fn default() -> Self {
9926        Self {
9927            enabled: true,
9928            variance_accounts_enabled: true,
9929            warranty_provisions_enabled: true,
9930            warranty_defect_threshold: 0.01,
9931        }
9932    }
9933}
9934
9935fn default_bom_depth() -> u32 {
9936    4
9937}
9938
9939fn default_production_order_types() -> Vec<String> {
9940    vec![
9941        "standard".to_string(),
9942        "rework".to_string(),
9943        "prototype".to_string(),
9944    ]
9945}
9946
9947fn default_supplier_tiers() -> u32 {
9948    2
9949}
9950
9951fn default_cost_frequency() -> String {
9952    "quarterly".to_string()
9953}
9954
9955fn default_yield_rate() -> f64 {
9956    0.97
9957}
9958
9959fn default_scrap_threshold() -> f64 {
9960    0.03
9961}
9962
9963impl Default for ManufacturingConfig {
9964    fn default() -> Self {
9965        Self {
9966            enabled: false,
9967            bom_depth: default_bom_depth(),
9968            just_in_time: false,
9969            production_order_types: default_production_order_types(),
9970            quality_framework: Some("ISO_9001".to_string()),
9971            supplier_tiers: default_supplier_tiers(),
9972            standard_cost_frequency: default_cost_frequency(),
9973            target_yield_rate: default_yield_rate(),
9974            scrap_alert_threshold: default_scrap_threshold(),
9975            anomaly_rates: ManufacturingAnomalyRates::default(),
9976            cost_accounting: ManufacturingCostAccountingConfig::default(),
9977        }
9978    }
9979}
9980
9981/// Manufacturing anomaly injection rates.
9982#[derive(Debug, Clone, Serialize, Deserialize)]
9983pub struct ManufacturingAnomalyRates {
9984    /// Yield manipulation rate.
9985    #[serde(default = "default_mfg_yield_rate")]
9986    pub yield_manipulation: f64,
9987
9988    /// Labor misallocation rate.
9989    #[serde(default = "default_mfg_labor_rate")]
9990    pub labor_misallocation: f64,
9991
9992    /// Phantom production rate.
9993    #[serde(default = "default_mfg_phantom_rate")]
9994    pub phantom_production: f64,
9995
9996    /// Standard cost manipulation rate.
9997    #[serde(default = "default_mfg_cost_rate")]
9998    pub standard_cost_manipulation: f64,
9999
10000    /// Inventory fraud rate.
10001    #[serde(default = "default_mfg_inventory_rate")]
10002    pub inventory_fraud: f64,
10003}
10004
10005fn default_mfg_yield_rate() -> f64 {
10006    0.015
10007}
10008
10009fn default_mfg_labor_rate() -> f64 {
10010    0.02
10011}
10012
10013fn default_mfg_phantom_rate() -> f64 {
10014    0.005
10015}
10016
10017fn default_mfg_cost_rate() -> f64 {
10018    0.01
10019}
10020
10021fn default_mfg_inventory_rate() -> f64 {
10022    0.008
10023}
10024
10025impl Default for ManufacturingAnomalyRates {
10026    fn default() -> Self {
10027        Self {
10028            yield_manipulation: default_mfg_yield_rate(),
10029            labor_misallocation: default_mfg_labor_rate(),
10030            phantom_production: default_mfg_phantom_rate(),
10031            standard_cost_manipulation: default_mfg_cost_rate(),
10032            inventory_fraud: default_mfg_inventory_rate(),
10033        }
10034    }
10035}
10036
10037/// Retail industry configuration.
10038#[derive(Debug, Clone, Serialize, Deserialize)]
10039pub struct RetailConfig {
10040    /// Enable retail-specific generation.
10041    #[serde(default)]
10042    pub enabled: bool,
10043
10044    /// Store type distribution.
10045    #[serde(default)]
10046    pub store_types: RetailStoreTypeConfig,
10047
10048    /// Average daily transactions per store.
10049    #[serde(default = "default_retail_daily_txns")]
10050    pub avg_daily_transactions: u32,
10051
10052    /// Enable loss prevention tracking.
10053    #[serde(default = "default_true")]
10054    pub loss_prevention: bool,
10055
10056    /// Shrinkage rate (0.01-0.03 typical).
10057    #[serde(default = "default_shrinkage_rate")]
10058    pub shrinkage_rate: f64,
10059
10060    /// Retail anomaly injection rates.
10061    #[serde(default)]
10062    pub anomaly_rates: RetailAnomalyRates,
10063}
10064
10065fn default_retail_daily_txns() -> u32 {
10066    500
10067}
10068
10069fn default_shrinkage_rate() -> f64 {
10070    0.015
10071}
10072
10073impl Default for RetailConfig {
10074    fn default() -> Self {
10075        Self {
10076            enabled: false,
10077            store_types: RetailStoreTypeConfig::default(),
10078            avg_daily_transactions: default_retail_daily_txns(),
10079            loss_prevention: true,
10080            shrinkage_rate: default_shrinkage_rate(),
10081            anomaly_rates: RetailAnomalyRates::default(),
10082        }
10083    }
10084}
10085
10086/// Retail store type distribution.
10087#[derive(Debug, Clone, Serialize, Deserialize)]
10088pub struct RetailStoreTypeConfig {
10089    /// Percentage of flagship stores.
10090    #[serde(default = "default_flagship_pct")]
10091    pub flagship: f64,
10092
10093    /// Percentage of regional stores.
10094    #[serde(default = "default_regional_pct")]
10095    pub regional: f64,
10096
10097    /// Percentage of outlet stores.
10098    #[serde(default = "default_outlet_pct")]
10099    pub outlet: f64,
10100
10101    /// Percentage of e-commerce.
10102    #[serde(default = "default_ecommerce_pct")]
10103    pub ecommerce: f64,
10104}
10105
10106fn default_flagship_pct() -> f64 {
10107    0.10
10108}
10109
10110fn default_regional_pct() -> f64 {
10111    0.50
10112}
10113
10114fn default_outlet_pct() -> f64 {
10115    0.25
10116}
10117
10118fn default_ecommerce_pct() -> f64 {
10119    0.15
10120}
10121
10122impl Default for RetailStoreTypeConfig {
10123    fn default() -> Self {
10124        Self {
10125            flagship: default_flagship_pct(),
10126            regional: default_regional_pct(),
10127            outlet: default_outlet_pct(),
10128            ecommerce: default_ecommerce_pct(),
10129        }
10130    }
10131}
10132
10133/// Retail anomaly injection rates.
10134#[derive(Debug, Clone, Serialize, Deserialize)]
10135pub struct RetailAnomalyRates {
10136    /// Sweethearting rate.
10137    #[serde(default = "default_sweethearting_rate")]
10138    pub sweethearting: f64,
10139
10140    /// Skimming rate.
10141    #[serde(default = "default_skimming_rate")]
10142    pub skimming: f64,
10143
10144    /// Refund fraud rate.
10145    #[serde(default = "default_refund_fraud_rate")]
10146    pub refund_fraud: f64,
10147
10148    /// Void abuse rate.
10149    #[serde(default = "default_void_abuse_rate")]
10150    pub void_abuse: f64,
10151
10152    /// Gift card fraud rate.
10153    #[serde(default = "default_gift_card_rate")]
10154    pub gift_card_fraud: f64,
10155
10156    /// Vendor kickback rate.
10157    #[serde(default = "default_retail_kickback_rate")]
10158    pub vendor_kickback: f64,
10159}
10160
10161fn default_sweethearting_rate() -> f64 {
10162    0.02
10163}
10164
10165fn default_skimming_rate() -> f64 {
10166    0.005
10167}
10168
10169fn default_refund_fraud_rate() -> f64 {
10170    0.015
10171}
10172
10173fn default_void_abuse_rate() -> f64 {
10174    0.01
10175}
10176
10177fn default_gift_card_rate() -> f64 {
10178    0.008
10179}
10180
10181fn default_retail_kickback_rate() -> f64 {
10182    0.003
10183}
10184
10185impl Default for RetailAnomalyRates {
10186    fn default() -> Self {
10187        Self {
10188            sweethearting: default_sweethearting_rate(),
10189            skimming: default_skimming_rate(),
10190            refund_fraud: default_refund_fraud_rate(),
10191            void_abuse: default_void_abuse_rate(),
10192            gift_card_fraud: default_gift_card_rate(),
10193            vendor_kickback: default_retail_kickback_rate(),
10194        }
10195    }
10196}
10197
10198/// Healthcare industry configuration.
10199#[derive(Debug, Clone, Serialize, Deserialize)]
10200pub struct HealthcareConfig {
10201    /// Enable healthcare-specific generation.
10202    #[serde(default)]
10203    pub enabled: bool,
10204
10205    /// Healthcare facility type.
10206    #[serde(default = "default_facility_type")]
10207    pub facility_type: String,
10208
10209    /// Payer mix distribution.
10210    #[serde(default)]
10211    pub payer_mix: HealthcarePayerMix,
10212
10213    /// Coding systems enabled.
10214    #[serde(default)]
10215    pub coding_systems: HealthcareCodingSystems,
10216
10217    /// Healthcare compliance settings.
10218    #[serde(default)]
10219    pub compliance: HealthcareComplianceConfig,
10220
10221    /// Average daily encounters.
10222    #[serde(default = "default_daily_encounters")]
10223    pub avg_daily_encounters: u32,
10224
10225    /// Average charges per encounter.
10226    #[serde(default = "default_charges_per_encounter")]
10227    pub avg_charges_per_encounter: u32,
10228
10229    /// Denial rate (0.0-1.0).
10230    #[serde(default = "default_hc_denial_rate")]
10231    pub denial_rate: f64,
10232
10233    /// Bad debt rate (0.0-1.0).
10234    #[serde(default = "default_hc_bad_debt_rate")]
10235    pub bad_debt_rate: f64,
10236
10237    /// Charity care rate (0.0-1.0).
10238    #[serde(default = "default_hc_charity_care_rate")]
10239    pub charity_care_rate: f64,
10240
10241    /// Healthcare anomaly injection rates.
10242    #[serde(default)]
10243    pub anomaly_rates: HealthcareAnomalyRates,
10244}
10245
10246fn default_facility_type() -> String {
10247    "hospital".to_string()
10248}
10249
10250fn default_daily_encounters() -> u32 {
10251    150
10252}
10253
10254fn default_charges_per_encounter() -> u32 {
10255    8
10256}
10257
10258fn default_hc_denial_rate() -> f64 {
10259    0.05
10260}
10261
10262fn default_hc_bad_debt_rate() -> f64 {
10263    0.03
10264}
10265
10266fn default_hc_charity_care_rate() -> f64 {
10267    0.02
10268}
10269
10270impl Default for HealthcareConfig {
10271    fn default() -> Self {
10272        Self {
10273            enabled: false,
10274            facility_type: default_facility_type(),
10275            payer_mix: HealthcarePayerMix::default(),
10276            coding_systems: HealthcareCodingSystems::default(),
10277            compliance: HealthcareComplianceConfig::default(),
10278            avg_daily_encounters: default_daily_encounters(),
10279            avg_charges_per_encounter: default_charges_per_encounter(),
10280            denial_rate: default_hc_denial_rate(),
10281            bad_debt_rate: default_hc_bad_debt_rate(),
10282            charity_care_rate: default_hc_charity_care_rate(),
10283            anomaly_rates: HealthcareAnomalyRates::default(),
10284        }
10285    }
10286}
10287
10288/// Healthcare payer mix distribution.
10289#[derive(Debug, Clone, Serialize, Deserialize)]
10290pub struct HealthcarePayerMix {
10291    /// Medicare percentage.
10292    #[serde(default = "default_medicare_pct")]
10293    pub medicare: f64,
10294
10295    /// Medicaid percentage.
10296    #[serde(default = "default_medicaid_pct")]
10297    pub medicaid: f64,
10298
10299    /// Commercial insurance percentage.
10300    #[serde(default = "default_commercial_pct")]
10301    pub commercial: f64,
10302
10303    /// Self-pay percentage.
10304    #[serde(default = "default_self_pay_pct")]
10305    pub self_pay: f64,
10306}
10307
10308fn default_medicare_pct() -> f64 {
10309    0.40
10310}
10311
10312fn default_medicaid_pct() -> f64 {
10313    0.20
10314}
10315
10316fn default_commercial_pct() -> f64 {
10317    0.30
10318}
10319
10320fn default_self_pay_pct() -> f64 {
10321    0.10
10322}
10323
10324impl Default for HealthcarePayerMix {
10325    fn default() -> Self {
10326        Self {
10327            medicare: default_medicare_pct(),
10328            medicaid: default_medicaid_pct(),
10329            commercial: default_commercial_pct(),
10330            self_pay: default_self_pay_pct(),
10331        }
10332    }
10333}
10334
10335/// Healthcare coding systems configuration.
10336#[derive(Debug, Clone, Serialize, Deserialize)]
10337pub struct HealthcareCodingSystems {
10338    /// Enable ICD-10 diagnosis coding.
10339    #[serde(default = "default_true")]
10340    pub icd10: bool,
10341
10342    /// Enable CPT procedure coding.
10343    #[serde(default = "default_true")]
10344    pub cpt: bool,
10345
10346    /// Enable DRG grouping.
10347    #[serde(default = "default_true")]
10348    pub drg: bool,
10349
10350    /// Enable HCPCS Level II coding.
10351    #[serde(default = "default_true")]
10352    pub hcpcs: bool,
10353
10354    /// Enable revenue codes.
10355    #[serde(default = "default_true")]
10356    pub revenue_codes: bool,
10357}
10358
10359impl Default for HealthcareCodingSystems {
10360    fn default() -> Self {
10361        Self {
10362            icd10: true,
10363            cpt: true,
10364            drg: true,
10365            hcpcs: true,
10366            revenue_codes: true,
10367        }
10368    }
10369}
10370
10371/// Healthcare compliance configuration.
10372#[derive(Debug, Clone, Serialize, Deserialize)]
10373pub struct HealthcareComplianceConfig {
10374    /// Enable HIPAA compliance.
10375    #[serde(default = "default_true")]
10376    pub hipaa: bool,
10377
10378    /// Enable Stark Law compliance.
10379    #[serde(default = "default_true")]
10380    pub stark_law: bool,
10381
10382    /// Enable Anti-Kickback Statute compliance.
10383    #[serde(default = "default_true")]
10384    pub anti_kickback: bool,
10385
10386    /// Enable False Claims Act compliance.
10387    #[serde(default = "default_true")]
10388    pub false_claims_act: bool,
10389
10390    /// Enable EMTALA compliance (for hospitals).
10391    #[serde(default = "default_true")]
10392    pub emtala: bool,
10393}
10394
10395impl Default for HealthcareComplianceConfig {
10396    fn default() -> Self {
10397        Self {
10398            hipaa: true,
10399            stark_law: true,
10400            anti_kickback: true,
10401            false_claims_act: true,
10402            emtala: true,
10403        }
10404    }
10405}
10406
10407/// Healthcare anomaly injection rates.
10408#[derive(Debug, Clone, Serialize, Deserialize)]
10409pub struct HealthcareAnomalyRates {
10410    /// Upcoding rate.
10411    #[serde(default = "default_upcoding_rate")]
10412    pub upcoding: f64,
10413
10414    /// Unbundling rate.
10415    #[serde(default = "default_unbundling_rate")]
10416    pub unbundling: f64,
10417
10418    /// Phantom billing rate.
10419    #[serde(default = "default_phantom_billing_rate")]
10420    pub phantom_billing: f64,
10421
10422    /// Kickback rate.
10423    #[serde(default = "default_healthcare_kickback_rate")]
10424    pub kickbacks: f64,
10425
10426    /// Duplicate billing rate.
10427    #[serde(default = "default_duplicate_billing_rate")]
10428    pub duplicate_billing: f64,
10429
10430    /// Medical necessity abuse rate.
10431    #[serde(default = "default_med_necessity_rate")]
10432    pub medical_necessity_abuse: f64,
10433}
10434
10435fn default_upcoding_rate() -> f64 {
10436    0.02
10437}
10438
10439fn default_unbundling_rate() -> f64 {
10440    0.015
10441}
10442
10443fn default_phantom_billing_rate() -> f64 {
10444    0.005
10445}
10446
10447fn default_healthcare_kickback_rate() -> f64 {
10448    0.003
10449}
10450
10451fn default_duplicate_billing_rate() -> f64 {
10452    0.008
10453}
10454
10455fn default_med_necessity_rate() -> f64 {
10456    0.01
10457}
10458
10459impl Default for HealthcareAnomalyRates {
10460    fn default() -> Self {
10461        Self {
10462            upcoding: default_upcoding_rate(),
10463            unbundling: default_unbundling_rate(),
10464            phantom_billing: default_phantom_billing_rate(),
10465            kickbacks: default_healthcare_kickback_rate(),
10466            duplicate_billing: default_duplicate_billing_rate(),
10467            medical_necessity_abuse: default_med_necessity_rate(),
10468        }
10469    }
10470}
10471
10472/// Technology industry configuration.
10473#[derive(Debug, Clone, Serialize, Deserialize)]
10474pub struct TechnologyConfig {
10475    /// Enable technology-specific generation.
10476    #[serde(default)]
10477    pub enabled: bool,
10478
10479    /// Revenue model type.
10480    #[serde(default = "default_revenue_model")]
10481    pub revenue_model: String,
10482
10483    /// Subscription revenue percentage (for SaaS).
10484    #[serde(default = "default_subscription_pct")]
10485    pub subscription_revenue_pct: f64,
10486
10487    /// License revenue percentage.
10488    #[serde(default = "default_license_pct")]
10489    pub license_revenue_pct: f64,
10490
10491    /// Services revenue percentage.
10492    #[serde(default = "default_services_pct")]
10493    pub services_revenue_pct: f64,
10494
10495    /// R&D capitalization settings.
10496    #[serde(default)]
10497    pub rd_capitalization: RdCapitalizationConfig,
10498
10499    /// Technology anomaly injection rates.
10500    #[serde(default)]
10501    pub anomaly_rates: TechnologyAnomalyRates,
10502}
10503
10504fn default_revenue_model() -> String {
10505    "saas".to_string()
10506}
10507
10508fn default_subscription_pct() -> f64 {
10509    0.60
10510}
10511
10512fn default_license_pct() -> f64 {
10513    0.25
10514}
10515
10516fn default_services_pct() -> f64 {
10517    0.15
10518}
10519
10520impl Default for TechnologyConfig {
10521    fn default() -> Self {
10522        Self {
10523            enabled: false,
10524            revenue_model: default_revenue_model(),
10525            subscription_revenue_pct: default_subscription_pct(),
10526            license_revenue_pct: default_license_pct(),
10527            services_revenue_pct: default_services_pct(),
10528            rd_capitalization: RdCapitalizationConfig::default(),
10529            anomaly_rates: TechnologyAnomalyRates::default(),
10530        }
10531    }
10532}
10533
10534/// R&D capitalization configuration.
10535#[derive(Debug, Clone, Serialize, Deserialize)]
10536pub struct RdCapitalizationConfig {
10537    /// Enable R&D capitalization.
10538    #[serde(default = "default_true")]
10539    pub enabled: bool,
10540
10541    /// Capitalization rate (0.0-1.0).
10542    #[serde(default = "default_cap_rate")]
10543    pub capitalization_rate: f64,
10544
10545    /// Useful life in years.
10546    #[serde(default = "default_useful_life")]
10547    pub useful_life_years: u32,
10548}
10549
10550fn default_cap_rate() -> f64 {
10551    0.30
10552}
10553
10554fn default_useful_life() -> u32 {
10555    3
10556}
10557
10558impl Default for RdCapitalizationConfig {
10559    fn default() -> Self {
10560        Self {
10561            enabled: true,
10562            capitalization_rate: default_cap_rate(),
10563            useful_life_years: default_useful_life(),
10564        }
10565    }
10566}
10567
10568/// Technology anomaly injection rates.
10569#[derive(Debug, Clone, Serialize, Deserialize)]
10570pub struct TechnologyAnomalyRates {
10571    /// Premature revenue recognition rate.
10572    #[serde(default = "default_premature_rev_rate")]
10573    pub premature_revenue: f64,
10574
10575    /// Side letter abuse rate.
10576    #[serde(default = "default_side_letter_rate")]
10577    pub side_letter_abuse: f64,
10578
10579    /// Channel stuffing rate.
10580    #[serde(default = "default_channel_stuffing_rate")]
10581    pub channel_stuffing: f64,
10582
10583    /// Improper capitalization rate.
10584    #[serde(default = "default_improper_cap_rate")]
10585    pub improper_capitalization: f64,
10586}
10587
10588fn default_premature_rev_rate() -> f64 {
10589    0.015
10590}
10591
10592fn default_side_letter_rate() -> f64 {
10593    0.008
10594}
10595
10596fn default_channel_stuffing_rate() -> f64 {
10597    0.01
10598}
10599
10600fn default_improper_cap_rate() -> f64 {
10601    0.012
10602}
10603
10604impl Default for TechnologyAnomalyRates {
10605    fn default() -> Self {
10606        Self {
10607            premature_revenue: default_premature_rev_rate(),
10608            side_letter_abuse: default_side_letter_rate(),
10609            channel_stuffing: default_channel_stuffing_rate(),
10610            improper_capitalization: default_improper_cap_rate(),
10611        }
10612    }
10613}
10614
10615/// Financial services industry configuration.
10616#[derive(Debug, Clone, Serialize, Deserialize)]
10617pub struct FinancialServicesConfig {
10618    /// Enable financial services-specific generation.
10619    #[serde(default)]
10620    pub enabled: bool,
10621
10622    /// Financial institution type.
10623    #[serde(default = "default_fi_type")]
10624    pub institution_type: String,
10625
10626    /// Regulatory framework.
10627    #[serde(default = "default_fi_regulatory")]
10628    pub regulatory_framework: String,
10629
10630    /// Financial services anomaly injection rates.
10631    #[serde(default)]
10632    pub anomaly_rates: FinancialServicesAnomalyRates,
10633}
10634
10635fn default_fi_type() -> String {
10636    "commercial_bank".to_string()
10637}
10638
10639fn default_fi_regulatory() -> String {
10640    "us_banking".to_string()
10641}
10642
10643impl Default for FinancialServicesConfig {
10644    fn default() -> Self {
10645        Self {
10646            enabled: false,
10647            institution_type: default_fi_type(),
10648            regulatory_framework: default_fi_regulatory(),
10649            anomaly_rates: FinancialServicesAnomalyRates::default(),
10650        }
10651    }
10652}
10653
10654/// Financial services anomaly injection rates.
10655#[derive(Debug, Clone, Serialize, Deserialize)]
10656pub struct FinancialServicesAnomalyRates {
10657    /// Loan fraud rate.
10658    #[serde(default = "default_loan_fraud_rate")]
10659    pub loan_fraud: f64,
10660
10661    /// Trading fraud rate.
10662    #[serde(default = "default_trading_fraud_rate")]
10663    pub trading_fraud: f64,
10664
10665    /// Insurance fraud rate.
10666    #[serde(default = "default_insurance_fraud_rate")]
10667    pub insurance_fraud: f64,
10668
10669    /// Account manipulation rate.
10670    #[serde(default = "default_account_manip_rate")]
10671    pub account_manipulation: f64,
10672}
10673
10674fn default_loan_fraud_rate() -> f64 {
10675    0.01
10676}
10677
10678fn default_trading_fraud_rate() -> f64 {
10679    0.008
10680}
10681
10682fn default_insurance_fraud_rate() -> f64 {
10683    0.012
10684}
10685
10686fn default_account_manip_rate() -> f64 {
10687    0.005
10688}
10689
10690impl Default for FinancialServicesAnomalyRates {
10691    fn default() -> Self {
10692        Self {
10693            loan_fraud: default_loan_fraud_rate(),
10694            trading_fraud: default_trading_fraud_rate(),
10695            insurance_fraud: default_insurance_fraud_rate(),
10696            account_manipulation: default_account_manip_rate(),
10697        }
10698    }
10699}
10700
10701/// Professional services industry configuration.
10702#[derive(Debug, Clone, Serialize, Deserialize)]
10703pub struct ProfessionalServicesConfig {
10704    /// Enable professional services-specific generation.
10705    #[serde(default)]
10706    pub enabled: bool,
10707
10708    /// Firm type.
10709    #[serde(default = "default_firm_type")]
10710    pub firm_type: String,
10711
10712    /// Billing model.
10713    #[serde(default = "default_billing_model")]
10714    pub billing_model: String,
10715
10716    /// Average hourly rate.
10717    #[serde(default = "default_hourly_rate")]
10718    pub avg_hourly_rate: f64,
10719
10720    /// Trust account settings (for law firms).
10721    #[serde(default)]
10722    pub trust_accounting: TrustAccountingConfig,
10723
10724    /// Professional services anomaly injection rates.
10725    #[serde(default)]
10726    pub anomaly_rates: ProfessionalServicesAnomalyRates,
10727}
10728
10729fn default_firm_type() -> String {
10730    "consulting".to_string()
10731}
10732
10733fn default_billing_model() -> String {
10734    "time_and_materials".to_string()
10735}
10736
10737fn default_hourly_rate() -> f64 {
10738    250.0
10739}
10740
10741impl Default for ProfessionalServicesConfig {
10742    fn default() -> Self {
10743        Self {
10744            enabled: false,
10745            firm_type: default_firm_type(),
10746            billing_model: default_billing_model(),
10747            avg_hourly_rate: default_hourly_rate(),
10748            trust_accounting: TrustAccountingConfig::default(),
10749            anomaly_rates: ProfessionalServicesAnomalyRates::default(),
10750        }
10751    }
10752}
10753
10754/// Trust accounting configuration for law firms.
10755#[derive(Debug, Clone, Serialize, Deserialize)]
10756pub struct TrustAccountingConfig {
10757    /// Enable trust accounting.
10758    #[serde(default)]
10759    pub enabled: bool,
10760
10761    /// Require three-way reconciliation.
10762    #[serde(default = "default_true")]
10763    pub require_three_way_reconciliation: bool,
10764}
10765
10766impl Default for TrustAccountingConfig {
10767    fn default() -> Self {
10768        Self {
10769            enabled: false,
10770            require_three_way_reconciliation: true,
10771        }
10772    }
10773}
10774
10775/// Professional services anomaly injection rates.
10776#[derive(Debug, Clone, Serialize, Deserialize)]
10777pub struct ProfessionalServicesAnomalyRates {
10778    /// Time billing fraud rate.
10779    #[serde(default = "default_time_fraud_rate")]
10780    pub time_billing_fraud: f64,
10781
10782    /// Expense report fraud rate.
10783    #[serde(default = "default_expense_fraud_rate")]
10784    pub expense_fraud: f64,
10785
10786    /// Trust misappropriation rate.
10787    #[serde(default = "default_trust_misappropriation_rate")]
10788    pub trust_misappropriation: f64,
10789}
10790
10791fn default_time_fraud_rate() -> f64 {
10792    0.02
10793}
10794
10795fn default_expense_fraud_rate() -> f64 {
10796    0.015
10797}
10798
10799fn default_trust_misappropriation_rate() -> f64 {
10800    0.003
10801}
10802
10803impl Default for ProfessionalServicesAnomalyRates {
10804    fn default() -> Self {
10805        Self {
10806            time_billing_fraud: default_time_fraud_rate(),
10807            expense_fraud: default_expense_fraud_rate(),
10808            trust_misappropriation: default_trust_misappropriation_rate(),
10809        }
10810    }
10811}
10812
10813/// Fingerprint privacy configuration for extraction and synthesis.
10814///
10815/// Controls the privacy parameters used when extracting fingerprints
10816/// from sensitive data. Supports predefined levels or custom (epsilon, delta) tuples.
10817///
10818/// ```yaml
10819/// fingerprint_privacy:
10820///   level: custom
10821///   epsilon: 0.5
10822///   delta: 1.0e-5
10823///   k_anonymity: 10
10824///   composition_method: renyi_dp
10825/// ```
10826#[derive(Debug, Clone, Serialize, Deserialize)]
10827pub struct FingerprintPrivacyConfig {
10828    /// Privacy level preset. Use "custom" for user-specified epsilon/delta.
10829    #[serde(default)]
10830    pub level: String,
10831    /// Custom epsilon value (only used when level = "custom").
10832    #[serde(default = "default_epsilon")]
10833    pub epsilon: f64,
10834    /// Custom delta value for (epsilon, delta)-DP (only used with RDP/zCDP).
10835    #[serde(default = "default_delta")]
10836    pub delta: f64,
10837    /// K-anonymity threshold.
10838    #[serde(default = "default_k_anonymity")]
10839    pub k_anonymity: u32,
10840    /// Composition method: "naive", "advanced", "renyi_dp", "zcdp".
10841    #[serde(default)]
10842    pub composition_method: String,
10843}
10844
10845fn default_epsilon() -> f64 {
10846    1.0
10847}
10848
10849fn default_delta() -> f64 {
10850    1e-5
10851}
10852
10853fn default_k_anonymity() -> u32 {
10854    5
10855}
10856
10857impl Default for FingerprintPrivacyConfig {
10858    fn default() -> Self {
10859        Self {
10860            level: "standard".to_string(),
10861            epsilon: default_epsilon(),
10862            delta: default_delta(),
10863            k_anonymity: default_k_anonymity(),
10864            composition_method: "naive".to_string(),
10865        }
10866    }
10867}
10868
10869/// Quality gates configuration for pass/fail thresholds on generation runs.
10870///
10871/// ```yaml
10872/// quality_gates:
10873///   enabled: true
10874///   profile: strict  # strict, default, lenient, custom
10875///   fail_on_violation: true
10876///   custom_gates:
10877///     - name: benford_compliance
10878///       metric: benford_mad
10879///       threshold: 0.015
10880///       comparison: lte
10881/// ```
10882#[derive(Debug, Clone, Serialize, Deserialize)]
10883pub struct QualityGatesSchemaConfig {
10884    /// Enable quality gate evaluation.
10885    #[serde(default)]
10886    pub enabled: bool,
10887    /// Gate profile: "strict", "default", "lenient", or "custom".
10888    #[serde(default = "default_gate_profile_name")]
10889    pub profile: String,
10890    /// Whether to fail the generation on gate violations.
10891    #[serde(default)]
10892    pub fail_on_violation: bool,
10893    /// Custom gate definitions (used when profile = "custom").
10894    #[serde(default)]
10895    pub custom_gates: Vec<QualityGateEntry>,
10896}
10897
10898fn default_gate_profile_name() -> String {
10899    "default".to_string()
10900}
10901
10902impl Default for QualityGatesSchemaConfig {
10903    fn default() -> Self {
10904        Self {
10905            enabled: false,
10906            profile: default_gate_profile_name(),
10907            fail_on_violation: false,
10908            custom_gates: Vec::new(),
10909        }
10910    }
10911}
10912
10913/// A single quality gate entry in configuration.
10914#[derive(Debug, Clone, Serialize, Deserialize)]
10915pub struct QualityGateEntry {
10916    /// Gate name.
10917    pub name: String,
10918    /// Metric to check: benford_mad, balance_coherence, document_chain_integrity,
10919    /// correlation_preservation, temporal_consistency, privacy_mia_auc,
10920    /// completion_rate, duplicate_rate, referential_integrity, ic_match_rate.
10921    pub metric: String,
10922    /// Threshold value.
10923    pub threshold: f64,
10924    /// Upper threshold for "between" comparison.
10925    #[serde(default)]
10926    pub upper_threshold: Option<f64>,
10927    /// Comparison operator: "gte", "lte", "eq", "between".
10928    #[serde(default = "default_gate_comparison")]
10929    pub comparison: String,
10930}
10931
10932fn default_gate_comparison() -> String {
10933    "gte".to_string()
10934}
10935
10936/// Compliance configuration for regulatory requirements.
10937///
10938/// ```yaml
10939/// compliance:
10940///   content_marking:
10941///     enabled: true
10942///     format: embedded  # embedded, sidecar, both
10943///   article10_report: true
10944/// ```
10945#[derive(Debug, Clone, Default, Serialize, Deserialize)]
10946pub struct ComplianceSchemaConfig {
10947    /// Synthetic content marking configuration (EU AI Act Article 50).
10948    #[serde(default)]
10949    pub content_marking: ContentMarkingSchemaConfig,
10950    /// Generate Article 10 data governance report.
10951    #[serde(default)]
10952    pub article10_report: bool,
10953    /// Certificate configuration for proving DP guarantees.
10954    #[serde(default)]
10955    pub certificates: CertificateSchemaConfig,
10956}
10957
10958/// Configuration for synthetic data certificates.
10959#[derive(Debug, Clone, Default, Serialize, Deserialize)]
10960pub struct CertificateSchemaConfig {
10961    /// Whether certificate generation is enabled.
10962    #[serde(default)]
10963    pub enabled: bool,
10964    /// Environment variable name for the signing key.
10965    #[serde(default)]
10966    pub signing_key_env: Option<String>,
10967    /// Whether to include quality metrics in the certificate.
10968    #[serde(default)]
10969    pub include_quality_metrics: bool,
10970}
10971
10972/// Content marking configuration for synthetic data output.
10973#[derive(Debug, Clone, Serialize, Deserialize)]
10974pub struct ContentMarkingSchemaConfig {
10975    /// Whether content marking is enabled.
10976    #[serde(default = "default_true")]
10977    pub enabled: bool,
10978    /// Marking format: "embedded", "sidecar", or "both".
10979    #[serde(default = "default_marking_format")]
10980    pub format: String,
10981}
10982
10983fn default_marking_format() -> String {
10984    "embedded".to_string()
10985}
10986
10987impl Default for ContentMarkingSchemaConfig {
10988    fn default() -> Self {
10989        Self {
10990            enabled: true,
10991            format: default_marking_format(),
10992        }
10993    }
10994}
10995
10996/// Webhook notification configuration.
10997#[derive(Debug, Clone, Default, Serialize, Deserialize)]
10998pub struct WebhookSchemaConfig {
10999    /// Whether webhooks are enabled.
11000    #[serde(default)]
11001    pub enabled: bool,
11002    /// Webhook endpoint configurations.
11003    #[serde(default)]
11004    pub endpoints: Vec<WebhookEndpointConfig>,
11005}
11006
11007/// Configuration for a single webhook endpoint.
11008#[derive(Debug, Clone, Serialize, Deserialize)]
11009pub struct WebhookEndpointConfig {
11010    /// Target URL for the webhook.
11011    pub url: String,
11012    /// Event types this endpoint subscribes to.
11013    #[serde(default)]
11014    pub events: Vec<String>,
11015    /// Optional secret for HMAC-SHA256 signature.
11016    #[serde(default)]
11017    pub secret: Option<String>,
11018    /// Maximum retry attempts (default: 3).
11019    #[serde(default = "default_webhook_retries")]
11020    pub max_retries: u32,
11021    /// Timeout in seconds (default: 10).
11022    #[serde(default = "default_webhook_timeout")]
11023    pub timeout_secs: u64,
11024}
11025
11026fn default_webhook_retries() -> u32 {
11027    3
11028}
11029fn default_webhook_timeout() -> u64 {
11030    10
11031}
11032
11033// ===== Enterprise Process Chain Config Structs =====
11034
11035// ----- Source-to-Pay (S2C/S2P) -----
11036
11037/// Source-to-Pay configuration covering the entire sourcing lifecycle.
11038#[derive(Debug, Clone, Default, Serialize, Deserialize)]
11039pub struct SourceToPayConfig {
11040    /// Enable source-to-pay generation
11041    #[serde(default)]
11042    pub enabled: bool,
11043    /// Spend analysis configuration
11044    #[serde(default)]
11045    pub spend_analysis: SpendAnalysisConfig,
11046    /// Sourcing project configuration
11047    #[serde(default)]
11048    pub sourcing: SourcingConfig,
11049    /// Supplier qualification configuration
11050    #[serde(default)]
11051    pub qualification: QualificationConfig,
11052    /// RFx event configuration
11053    #[serde(default)]
11054    pub rfx: RfxConfig,
11055    /// Contract configuration
11056    #[serde(default)]
11057    pub contracts: ContractConfig,
11058    /// Catalog configuration
11059    #[serde(default)]
11060    pub catalog: CatalogConfig,
11061    /// Scorecard configuration
11062    #[serde(default)]
11063    pub scorecards: ScorecardConfig,
11064    /// P2P integration settings
11065    #[serde(default)]
11066    pub p2p_integration: P2PIntegrationConfig,
11067}
11068
11069/// Spend analysis configuration.
11070#[derive(Debug, Clone, Serialize, Deserialize)]
11071pub struct SpendAnalysisConfig {
11072    /// HHI threshold for triggering sourcing project
11073    #[serde(default = "default_hhi_threshold")]
11074    pub hhi_threshold: f64,
11075    /// Target spend coverage under contracts
11076    #[serde(default = "default_contract_coverage_target")]
11077    pub contract_coverage_target: f64,
11078}
11079
11080impl Default for SpendAnalysisConfig {
11081    fn default() -> Self {
11082        Self {
11083            hhi_threshold: default_hhi_threshold(),
11084            contract_coverage_target: default_contract_coverage_target(),
11085        }
11086    }
11087}
11088
11089fn default_hhi_threshold() -> f64 {
11090    2500.0
11091}
11092fn default_contract_coverage_target() -> f64 {
11093    0.80
11094}
11095
11096/// Sourcing project configuration.
11097#[derive(Debug, Clone, Serialize, Deserialize)]
11098pub struct SourcingConfig {
11099    /// Number of sourcing projects per year
11100    #[serde(default = "default_sourcing_projects_per_year")]
11101    pub projects_per_year: u32,
11102    /// Months before expiry to trigger renewal project
11103    #[serde(default = "default_renewal_horizon_months")]
11104    pub renewal_horizon_months: u32,
11105    /// Average project duration in months
11106    #[serde(default = "default_project_duration_months")]
11107    pub project_duration_months: u32,
11108}
11109
11110impl Default for SourcingConfig {
11111    fn default() -> Self {
11112        Self {
11113            projects_per_year: default_sourcing_projects_per_year(),
11114            renewal_horizon_months: default_renewal_horizon_months(),
11115            project_duration_months: default_project_duration_months(),
11116        }
11117    }
11118}
11119
11120fn default_sourcing_projects_per_year() -> u32 {
11121    10
11122}
11123fn default_renewal_horizon_months() -> u32 {
11124    3
11125}
11126fn default_project_duration_months() -> u32 {
11127    4
11128}
11129
11130/// Supplier qualification configuration.
11131#[derive(Debug, Clone, Serialize, Deserialize)]
11132pub struct QualificationConfig {
11133    /// Pass rate for qualification
11134    #[serde(default = "default_qualification_pass_rate")]
11135    pub pass_rate: f64,
11136    /// Qualification validity in days
11137    #[serde(default = "default_qualification_validity_days")]
11138    pub validity_days: u32,
11139    /// Financial stability weight
11140    #[serde(default = "default_financial_weight")]
11141    pub financial_weight: f64,
11142    /// Quality management weight
11143    #[serde(default = "default_quality_weight")]
11144    pub quality_weight: f64,
11145    /// Delivery performance weight
11146    #[serde(default = "default_delivery_weight")]
11147    pub delivery_weight: f64,
11148    /// Compliance weight
11149    #[serde(default = "default_compliance_weight")]
11150    pub compliance_weight: f64,
11151}
11152
11153impl Default for QualificationConfig {
11154    fn default() -> Self {
11155        Self {
11156            pass_rate: default_qualification_pass_rate(),
11157            validity_days: default_qualification_validity_days(),
11158            financial_weight: default_financial_weight(),
11159            quality_weight: default_quality_weight(),
11160            delivery_weight: default_delivery_weight(),
11161            compliance_weight: default_compliance_weight(),
11162        }
11163    }
11164}
11165
11166fn default_qualification_pass_rate() -> f64 {
11167    0.75
11168}
11169fn default_qualification_validity_days() -> u32 {
11170    365
11171}
11172fn default_financial_weight() -> f64 {
11173    0.25
11174}
11175fn default_quality_weight() -> f64 {
11176    0.30
11177}
11178fn default_delivery_weight() -> f64 {
11179    0.25
11180}
11181fn default_compliance_weight() -> f64 {
11182    0.20
11183}
11184
11185/// RFx event configuration.
11186#[derive(Debug, Clone, Serialize, Deserialize)]
11187pub struct RfxConfig {
11188    /// Spend threshold above which RFI is required before RFP
11189    #[serde(default = "default_rfi_threshold")]
11190    pub rfi_threshold: f64,
11191    /// Minimum vendors invited per RFx
11192    #[serde(default = "default_min_invited_vendors")]
11193    pub min_invited_vendors: u32,
11194    /// Maximum vendors invited per RFx
11195    #[serde(default = "default_max_invited_vendors")]
11196    pub max_invited_vendors: u32,
11197    /// Response rate (% of invited vendors that submit bids)
11198    #[serde(default = "default_response_rate")]
11199    pub response_rate: f64,
11200    /// Default price weight in evaluation
11201    #[serde(default = "default_price_weight")]
11202    pub default_price_weight: f64,
11203    /// Default quality weight in evaluation
11204    #[serde(default = "default_rfx_quality_weight")]
11205    pub default_quality_weight: f64,
11206    /// Default delivery weight in evaluation
11207    #[serde(default = "default_rfx_delivery_weight")]
11208    pub default_delivery_weight: f64,
11209}
11210
11211impl Default for RfxConfig {
11212    fn default() -> Self {
11213        Self {
11214            rfi_threshold: default_rfi_threshold(),
11215            min_invited_vendors: default_min_invited_vendors(),
11216            max_invited_vendors: default_max_invited_vendors(),
11217            response_rate: default_response_rate(),
11218            default_price_weight: default_price_weight(),
11219            default_quality_weight: default_rfx_quality_weight(),
11220            default_delivery_weight: default_rfx_delivery_weight(),
11221        }
11222    }
11223}
11224
11225fn default_rfi_threshold() -> f64 {
11226    100_000.0
11227}
11228fn default_min_invited_vendors() -> u32 {
11229    3
11230}
11231fn default_max_invited_vendors() -> u32 {
11232    8
11233}
11234fn default_response_rate() -> f64 {
11235    0.70
11236}
11237fn default_price_weight() -> f64 {
11238    0.40
11239}
11240fn default_rfx_quality_weight() -> f64 {
11241    0.35
11242}
11243fn default_rfx_delivery_weight() -> f64 {
11244    0.25
11245}
11246
11247/// Contract configuration.
11248#[derive(Debug, Clone, Serialize, Deserialize)]
11249pub struct ContractConfig {
11250    /// Minimum contract duration in months
11251    #[serde(default = "default_min_contract_months")]
11252    pub min_duration_months: u32,
11253    /// Maximum contract duration in months
11254    #[serde(default = "default_max_contract_months")]
11255    pub max_duration_months: u32,
11256    /// Auto-renewal rate
11257    #[serde(default = "default_auto_renewal_rate")]
11258    pub auto_renewal_rate: f64,
11259    /// Amendment rate (% of contracts with at least one amendment)
11260    #[serde(default = "default_amendment_rate")]
11261    pub amendment_rate: f64,
11262    /// Distribution of contract types
11263    #[serde(default)]
11264    pub type_distribution: ContractTypeDistribution,
11265}
11266
11267impl Default for ContractConfig {
11268    fn default() -> Self {
11269        Self {
11270            min_duration_months: default_min_contract_months(),
11271            max_duration_months: default_max_contract_months(),
11272            auto_renewal_rate: default_auto_renewal_rate(),
11273            amendment_rate: default_amendment_rate(),
11274            type_distribution: ContractTypeDistribution::default(),
11275        }
11276    }
11277}
11278
11279fn default_min_contract_months() -> u32 {
11280    12
11281}
11282fn default_max_contract_months() -> u32 {
11283    36
11284}
11285fn default_auto_renewal_rate() -> f64 {
11286    0.40
11287}
11288fn default_amendment_rate() -> f64 {
11289    0.20
11290}
11291
11292/// Distribution of contract types.
11293#[derive(Debug, Clone, Serialize, Deserialize)]
11294pub struct ContractTypeDistribution {
11295    /// Fixed price percentage
11296    #[serde(default = "default_fixed_price_pct")]
11297    pub fixed_price: f64,
11298    /// Blanket/framework percentage
11299    #[serde(default = "default_blanket_pct")]
11300    pub blanket: f64,
11301    /// Time and materials percentage
11302    #[serde(default = "default_time_materials_pct")]
11303    pub time_and_materials: f64,
11304    /// Service agreement percentage
11305    #[serde(default = "default_service_agreement_pct")]
11306    pub service_agreement: f64,
11307}
11308
11309impl Default for ContractTypeDistribution {
11310    fn default() -> Self {
11311        Self {
11312            fixed_price: default_fixed_price_pct(),
11313            blanket: default_blanket_pct(),
11314            time_and_materials: default_time_materials_pct(),
11315            service_agreement: default_service_agreement_pct(),
11316        }
11317    }
11318}
11319
11320fn default_fixed_price_pct() -> f64 {
11321    0.40
11322}
11323fn default_blanket_pct() -> f64 {
11324    0.30
11325}
11326fn default_time_materials_pct() -> f64 {
11327    0.15
11328}
11329fn default_service_agreement_pct() -> f64 {
11330    0.15
11331}
11332
11333/// Catalog configuration.
11334#[derive(Debug, Clone, Serialize, Deserialize)]
11335pub struct CatalogConfig {
11336    /// Percentage of catalog items marked as preferred
11337    #[serde(default = "default_preferred_vendor_flag_rate")]
11338    pub preferred_vendor_flag_rate: f64,
11339    /// Rate of materials with multiple sources in catalog
11340    #[serde(default = "default_multi_source_rate")]
11341    pub multi_source_rate: f64,
11342}
11343
11344impl Default for CatalogConfig {
11345    fn default() -> Self {
11346        Self {
11347            preferred_vendor_flag_rate: default_preferred_vendor_flag_rate(),
11348            multi_source_rate: default_multi_source_rate(),
11349        }
11350    }
11351}
11352
11353fn default_preferred_vendor_flag_rate() -> f64 {
11354    0.70
11355}
11356fn default_multi_source_rate() -> f64 {
11357    0.25
11358}
11359
11360/// Scorecard configuration.
11361#[derive(Debug, Clone, Serialize, Deserialize)]
11362pub struct ScorecardConfig {
11363    /// Scorecard review frequency (quarterly, monthly)
11364    #[serde(default = "default_scorecard_frequency")]
11365    pub frequency: String,
11366    /// On-time delivery weight in overall score
11367    #[serde(default = "default_otd_weight")]
11368    pub on_time_delivery_weight: f64,
11369    /// Quality weight in overall score
11370    #[serde(default = "default_quality_score_weight")]
11371    pub quality_weight: f64,
11372    /// Price competitiveness weight
11373    #[serde(default = "default_price_score_weight")]
11374    pub price_weight: f64,
11375    /// Responsiveness weight
11376    #[serde(default = "default_responsiveness_weight")]
11377    pub responsiveness_weight: f64,
11378    /// Grade A threshold (score >= this)
11379    #[serde(default = "default_grade_a_threshold")]
11380    pub grade_a_threshold: f64,
11381    /// Grade B threshold
11382    #[serde(default = "default_grade_b_threshold")]
11383    pub grade_b_threshold: f64,
11384    /// Grade C threshold
11385    #[serde(default = "default_grade_c_threshold")]
11386    pub grade_c_threshold: f64,
11387}
11388
11389impl Default for ScorecardConfig {
11390    fn default() -> Self {
11391        Self {
11392            frequency: default_scorecard_frequency(),
11393            on_time_delivery_weight: default_otd_weight(),
11394            quality_weight: default_quality_score_weight(),
11395            price_weight: default_price_score_weight(),
11396            responsiveness_weight: default_responsiveness_weight(),
11397            grade_a_threshold: default_grade_a_threshold(),
11398            grade_b_threshold: default_grade_b_threshold(),
11399            grade_c_threshold: default_grade_c_threshold(),
11400        }
11401    }
11402}
11403
11404fn default_scorecard_frequency() -> String {
11405    "quarterly".to_string()
11406}
11407fn default_otd_weight() -> f64 {
11408    0.30
11409}
11410fn default_quality_score_weight() -> f64 {
11411    0.30
11412}
11413fn default_price_score_weight() -> f64 {
11414    0.25
11415}
11416fn default_responsiveness_weight() -> f64 {
11417    0.15
11418}
11419fn default_grade_a_threshold() -> f64 {
11420    90.0
11421}
11422fn default_grade_b_threshold() -> f64 {
11423    75.0
11424}
11425fn default_grade_c_threshold() -> f64 {
11426    60.0
11427}
11428
11429/// P2P integration settings for contract enforcement.
11430#[derive(Debug, Clone, Serialize, Deserialize)]
11431pub struct P2PIntegrationConfig {
11432    /// Rate of off-contract (maverick) purchases
11433    #[serde(default = "default_off_contract_rate")]
11434    pub off_contract_rate: f64,
11435    /// Price tolerance for contract price validation
11436    #[serde(default = "default_price_tolerance")]
11437    pub price_tolerance: f64,
11438    /// Whether to enforce catalog ordering
11439    #[serde(default)]
11440    pub catalog_enforcement: bool,
11441}
11442
11443impl Default for P2PIntegrationConfig {
11444    fn default() -> Self {
11445        Self {
11446            off_contract_rate: default_off_contract_rate(),
11447            price_tolerance: default_price_tolerance(),
11448            catalog_enforcement: false,
11449        }
11450    }
11451}
11452
11453fn default_off_contract_rate() -> f64 {
11454    0.15
11455}
11456fn default_price_tolerance() -> f64 {
11457    0.02
11458}
11459
11460// ----- Financial Reporting -----
11461
11462/// Financial reporting configuration.
11463#[derive(Debug, Clone, Serialize, Deserialize)]
11464pub struct FinancialReportingConfig {
11465    /// Enable financial reporting generation
11466    #[serde(default)]
11467    pub enabled: bool,
11468    /// Generate balance sheet
11469    #[serde(default = "default_true")]
11470    pub generate_balance_sheet: bool,
11471    /// Generate income statement
11472    #[serde(default = "default_true")]
11473    pub generate_income_statement: bool,
11474    /// Generate cash flow statement
11475    #[serde(default = "default_true")]
11476    pub generate_cash_flow: bool,
11477    /// Generate changes in equity statement
11478    #[serde(default = "default_true")]
11479    pub generate_changes_in_equity: bool,
11480    /// Number of comparative periods
11481    #[serde(default = "default_comparative_periods")]
11482    pub comparative_periods: u32,
11483    /// Management KPIs configuration
11484    #[serde(default)]
11485    pub management_kpis: ManagementKpisConfig,
11486    /// Budget configuration
11487    #[serde(default)]
11488    pub budgets: BudgetConfig,
11489}
11490
11491impl Default for FinancialReportingConfig {
11492    fn default() -> Self {
11493        Self {
11494            enabled: false,
11495            generate_balance_sheet: true,
11496            generate_income_statement: true,
11497            generate_cash_flow: true,
11498            generate_changes_in_equity: true,
11499            comparative_periods: default_comparative_periods(),
11500            management_kpis: ManagementKpisConfig::default(),
11501            budgets: BudgetConfig::default(),
11502        }
11503    }
11504}
11505
11506fn default_comparative_periods() -> u32 {
11507    1
11508}
11509
11510/// Management KPIs configuration.
11511#[derive(Debug, Clone, Default, Serialize, Deserialize)]
11512pub struct ManagementKpisConfig {
11513    /// Enable KPI generation
11514    #[serde(default)]
11515    pub enabled: bool,
11516    /// KPI calculation frequency (monthly, quarterly)
11517    #[serde(default = "default_kpi_frequency")]
11518    pub frequency: String,
11519}
11520
11521fn default_kpi_frequency() -> String {
11522    "monthly".to_string()
11523}
11524
11525/// Budget configuration.
11526#[derive(Debug, Clone, Serialize, Deserialize)]
11527pub struct BudgetConfig {
11528    /// Enable budget generation
11529    #[serde(default)]
11530    pub enabled: bool,
11531    /// Expected revenue growth rate for budgeting
11532    #[serde(default = "default_revenue_growth_rate")]
11533    pub revenue_growth_rate: f64,
11534    /// Expected expense inflation rate
11535    #[serde(default = "default_expense_inflation_rate")]
11536    pub expense_inflation_rate: f64,
11537    /// Random noise to add to budget vs actual
11538    #[serde(default = "default_variance_noise")]
11539    pub variance_noise: f64,
11540}
11541
11542impl Default for BudgetConfig {
11543    fn default() -> Self {
11544        Self {
11545            enabled: false,
11546            revenue_growth_rate: default_revenue_growth_rate(),
11547            expense_inflation_rate: default_expense_inflation_rate(),
11548            variance_noise: default_variance_noise(),
11549        }
11550    }
11551}
11552
11553fn default_revenue_growth_rate() -> f64 {
11554    0.05
11555}
11556fn default_expense_inflation_rate() -> f64 {
11557    0.03
11558}
11559fn default_variance_noise() -> f64 {
11560    0.10
11561}
11562
11563// ----- HR Configuration -----
11564
11565/// HR (Hire-to-Retire) process configuration.
11566#[derive(Debug, Clone, Default, Serialize, Deserialize)]
11567pub struct HrConfig {
11568    /// Enable HR generation
11569    #[serde(default)]
11570    pub enabled: bool,
11571    /// Payroll configuration
11572    #[serde(default)]
11573    pub payroll: PayrollConfig,
11574    /// Time and attendance configuration
11575    #[serde(default)]
11576    pub time_attendance: TimeAttendanceConfig,
11577    /// Expense management configuration
11578    #[serde(default)]
11579    pub expenses: ExpenseConfig,
11580}
11581
11582/// Payroll configuration.
11583#[derive(Debug, Clone, Serialize, Deserialize)]
11584pub struct PayrollConfig {
11585    /// Enable payroll generation
11586    #[serde(default = "default_true")]
11587    pub enabled: bool,
11588    /// Pay frequency (monthly, biweekly, weekly)
11589    #[serde(default = "default_pay_frequency")]
11590    pub pay_frequency: String,
11591    /// Salary ranges by job level
11592    #[serde(default)]
11593    pub salary_ranges: PayrollSalaryRanges,
11594    /// Effective tax rates
11595    #[serde(default)]
11596    pub tax_rates: PayrollTaxRates,
11597    /// Benefits enrollment rate
11598    #[serde(default = "default_benefits_enrollment_rate")]
11599    pub benefits_enrollment_rate: f64,
11600    /// Retirement plan participation rate
11601    #[serde(default = "default_retirement_participation_rate")]
11602    pub retirement_participation_rate: f64,
11603}
11604
11605impl Default for PayrollConfig {
11606    fn default() -> Self {
11607        Self {
11608            enabled: true,
11609            pay_frequency: default_pay_frequency(),
11610            salary_ranges: PayrollSalaryRanges::default(),
11611            tax_rates: PayrollTaxRates::default(),
11612            benefits_enrollment_rate: default_benefits_enrollment_rate(),
11613            retirement_participation_rate: default_retirement_participation_rate(),
11614        }
11615    }
11616}
11617
11618fn default_pay_frequency() -> String {
11619    "monthly".to_string()
11620}
11621fn default_benefits_enrollment_rate() -> f64 {
11622    0.60
11623}
11624fn default_retirement_participation_rate() -> f64 {
11625    0.45
11626}
11627
11628/// Salary ranges by job level.
11629#[derive(Debug, Clone, Serialize, Deserialize)]
11630pub struct PayrollSalaryRanges {
11631    /// Staff level min/max
11632    #[serde(default = "default_staff_min")]
11633    pub staff_min: f64,
11634    #[serde(default = "default_staff_max")]
11635    pub staff_max: f64,
11636    /// Manager level min/max
11637    #[serde(default = "default_manager_min")]
11638    pub manager_min: f64,
11639    #[serde(default = "default_manager_max")]
11640    pub manager_max: f64,
11641    /// Director level min/max
11642    #[serde(default = "default_director_min")]
11643    pub director_min: f64,
11644    #[serde(default = "default_director_max")]
11645    pub director_max: f64,
11646    /// Executive level min/max
11647    #[serde(default = "default_executive_min")]
11648    pub executive_min: f64,
11649    #[serde(default = "default_executive_max")]
11650    pub executive_max: f64,
11651}
11652
11653impl Default for PayrollSalaryRanges {
11654    fn default() -> Self {
11655        Self {
11656            staff_min: default_staff_min(),
11657            staff_max: default_staff_max(),
11658            manager_min: default_manager_min(),
11659            manager_max: default_manager_max(),
11660            director_min: default_director_min(),
11661            director_max: default_director_max(),
11662            executive_min: default_executive_min(),
11663            executive_max: default_executive_max(),
11664        }
11665    }
11666}
11667
11668fn default_staff_min() -> f64 {
11669    50_000.0
11670}
11671fn default_staff_max() -> f64 {
11672    70_000.0
11673}
11674fn default_manager_min() -> f64 {
11675    80_000.0
11676}
11677fn default_manager_max() -> f64 {
11678    120_000.0
11679}
11680fn default_director_min() -> f64 {
11681    120_000.0
11682}
11683fn default_director_max() -> f64 {
11684    180_000.0
11685}
11686fn default_executive_min() -> f64 {
11687    180_000.0
11688}
11689fn default_executive_max() -> f64 {
11690    350_000.0
11691}
11692
11693/// Effective tax rates for payroll.
11694#[derive(Debug, Clone, Serialize, Deserialize)]
11695pub struct PayrollTaxRates {
11696    /// Federal effective tax rate
11697    #[serde(default = "default_federal_rate")]
11698    pub federal_effective: f64,
11699    /// State effective tax rate
11700    #[serde(default = "default_state_rate")]
11701    pub state_effective: f64,
11702    /// FICA/social security rate
11703    #[serde(default = "default_fica_rate")]
11704    pub fica: f64,
11705}
11706
11707impl Default for PayrollTaxRates {
11708    fn default() -> Self {
11709        Self {
11710            federal_effective: default_federal_rate(),
11711            state_effective: default_state_rate(),
11712            fica: default_fica_rate(),
11713        }
11714    }
11715}
11716
11717fn default_federal_rate() -> f64 {
11718    0.22
11719}
11720fn default_state_rate() -> f64 {
11721    0.05
11722}
11723fn default_fica_rate() -> f64 {
11724    0.0765
11725}
11726
11727/// Time and attendance configuration.
11728#[derive(Debug, Clone, Serialize, Deserialize)]
11729pub struct TimeAttendanceConfig {
11730    /// Enable time tracking
11731    #[serde(default = "default_true")]
11732    pub enabled: bool,
11733    /// Overtime rate (% of employees with overtime in a period)
11734    #[serde(default = "default_overtime_rate")]
11735    pub overtime_rate: f64,
11736}
11737
11738impl Default for TimeAttendanceConfig {
11739    fn default() -> Self {
11740        Self {
11741            enabled: true,
11742            overtime_rate: default_overtime_rate(),
11743        }
11744    }
11745}
11746
11747fn default_overtime_rate() -> f64 {
11748    0.10
11749}
11750
11751/// Expense management configuration.
11752#[derive(Debug, Clone, Serialize, Deserialize)]
11753pub struct ExpenseConfig {
11754    /// Enable expense report generation
11755    #[serde(default = "default_true")]
11756    pub enabled: bool,
11757    /// Rate of employees submitting expenses per month
11758    #[serde(default = "default_expense_submission_rate")]
11759    pub submission_rate: f64,
11760    /// Rate of policy violations
11761    #[serde(default = "default_policy_violation_rate")]
11762    pub policy_violation_rate: f64,
11763}
11764
11765impl Default for ExpenseConfig {
11766    fn default() -> Self {
11767        Self {
11768            enabled: true,
11769            submission_rate: default_expense_submission_rate(),
11770            policy_violation_rate: default_policy_violation_rate(),
11771        }
11772    }
11773}
11774
11775fn default_expense_submission_rate() -> f64 {
11776    0.30
11777}
11778fn default_policy_violation_rate() -> f64 {
11779    0.08
11780}
11781
11782// ----- Manufacturing Configuration -----
11783
11784/// Manufacturing process configuration (production orders, WIP, routing).
11785#[derive(Debug, Clone, Default, Serialize, Deserialize)]
11786pub struct ManufacturingProcessConfig {
11787    /// Enable manufacturing generation
11788    #[serde(default)]
11789    pub enabled: bool,
11790    /// Production order configuration
11791    #[serde(default)]
11792    pub production_orders: ProductionOrderConfig,
11793    /// Costing configuration
11794    #[serde(default)]
11795    pub costing: ManufacturingCostingConfig,
11796    /// Routing configuration
11797    #[serde(default)]
11798    pub routing: RoutingConfig,
11799}
11800
11801/// Production order configuration.
11802#[derive(Debug, Clone, Serialize, Deserialize)]
11803pub struct ProductionOrderConfig {
11804    /// Orders per month
11805    #[serde(default = "default_prod_orders_per_month")]
11806    pub orders_per_month: u32,
11807    /// Average batch size
11808    #[serde(default = "default_prod_avg_batch_size")]
11809    pub avg_batch_size: u32,
11810    /// Yield rate
11811    #[serde(default = "default_prod_yield_rate")]
11812    pub yield_rate: f64,
11813    /// Make-to-order rate (vs make-to-stock)
11814    #[serde(default = "default_prod_make_to_order_rate")]
11815    pub make_to_order_rate: f64,
11816    /// Rework rate
11817    #[serde(default = "default_prod_rework_rate")]
11818    pub rework_rate: f64,
11819}
11820
11821impl Default for ProductionOrderConfig {
11822    fn default() -> Self {
11823        Self {
11824            orders_per_month: default_prod_orders_per_month(),
11825            avg_batch_size: default_prod_avg_batch_size(),
11826            yield_rate: default_prod_yield_rate(),
11827            make_to_order_rate: default_prod_make_to_order_rate(),
11828            rework_rate: default_prod_rework_rate(),
11829        }
11830    }
11831}
11832
11833fn default_prod_orders_per_month() -> u32 {
11834    50
11835}
11836fn default_prod_avg_batch_size() -> u32 {
11837    100
11838}
11839fn default_prod_yield_rate() -> f64 {
11840    0.97
11841}
11842fn default_prod_make_to_order_rate() -> f64 {
11843    0.20
11844}
11845fn default_prod_rework_rate() -> f64 {
11846    0.03
11847}
11848
11849/// Manufacturing costing configuration.
11850#[derive(Debug, Clone, Serialize, Deserialize)]
11851pub struct ManufacturingCostingConfig {
11852    /// Labor rate per hour
11853    #[serde(default = "default_labor_rate")]
11854    pub labor_rate_per_hour: f64,
11855    /// Overhead application rate (multiplier on direct labor)
11856    #[serde(default = "default_overhead_rate")]
11857    pub overhead_rate: f64,
11858    /// Standard cost update frequency
11859    #[serde(default = "default_cost_update_frequency")]
11860    pub standard_cost_update_frequency: String,
11861}
11862
11863impl Default for ManufacturingCostingConfig {
11864    fn default() -> Self {
11865        Self {
11866            labor_rate_per_hour: default_labor_rate(),
11867            overhead_rate: default_overhead_rate(),
11868            standard_cost_update_frequency: default_cost_update_frequency(),
11869        }
11870    }
11871}
11872
11873fn default_labor_rate() -> f64 {
11874    35.0
11875}
11876fn default_overhead_rate() -> f64 {
11877    1.50
11878}
11879fn default_cost_update_frequency() -> String {
11880    "quarterly".to_string()
11881}
11882
11883/// Routing configuration for production operations.
11884#[derive(Debug, Clone, Serialize, Deserialize)]
11885pub struct RoutingConfig {
11886    /// Average number of operations per routing
11887    #[serde(default = "default_avg_operations")]
11888    pub avg_operations: u32,
11889    /// Average setup time in hours
11890    #[serde(default = "default_setup_time")]
11891    pub setup_time_hours: f64,
11892    /// Run time variation coefficient
11893    #[serde(default = "default_run_time_variation")]
11894    pub run_time_variation: f64,
11895}
11896
11897impl Default for RoutingConfig {
11898    fn default() -> Self {
11899        Self {
11900            avg_operations: default_avg_operations(),
11901            setup_time_hours: default_setup_time(),
11902            run_time_variation: default_run_time_variation(),
11903        }
11904    }
11905}
11906
11907fn default_avg_operations() -> u32 {
11908    4
11909}
11910fn default_setup_time() -> f64 {
11911    1.5
11912}
11913fn default_run_time_variation() -> f64 {
11914    0.15
11915}
11916
11917// ----- Sales Quote Configuration -----
11918
11919/// Sales quote (quote-to-order) pipeline configuration.
11920#[derive(Debug, Clone, Serialize, Deserialize)]
11921pub struct SalesQuoteConfig {
11922    /// Enable sales quote generation
11923    #[serde(default)]
11924    pub enabled: bool,
11925    /// Quotes per month
11926    #[serde(default = "default_quotes_per_month")]
11927    pub quotes_per_month: u32,
11928    /// Win rate (fraction of quotes that convert to orders)
11929    #[serde(default = "default_quote_win_rate")]
11930    pub win_rate: f64,
11931    /// Average quote validity in days
11932    #[serde(default = "default_quote_validity_days")]
11933    pub validity_days: u32,
11934}
11935
11936impl Default for SalesQuoteConfig {
11937    fn default() -> Self {
11938        Self {
11939            enabled: false,
11940            quotes_per_month: default_quotes_per_month(),
11941            win_rate: default_quote_win_rate(),
11942            validity_days: default_quote_validity_days(),
11943        }
11944    }
11945}
11946
11947fn default_quotes_per_month() -> u32 {
11948    30
11949}
11950fn default_quote_win_rate() -> f64 {
11951    0.35
11952}
11953fn default_quote_validity_days() -> u32 {
11954    30
11955}
11956
11957// =============================================================================
11958// Tax Accounting Configuration
11959// =============================================================================
11960
11961/// Tax accounting configuration.
11962///
11963/// Controls generation of tax-related data including VAT/GST, sales tax,
11964/// withholding tax, tax provisions, and payroll tax across multiple jurisdictions.
11965#[derive(Debug, Clone, Serialize, Deserialize)]
11966pub struct TaxConfig {
11967    /// Whether tax generation is enabled.
11968    #[serde(default)]
11969    pub enabled: bool,
11970    /// Tax jurisdiction configuration.
11971    #[serde(default)]
11972    pub jurisdictions: TaxJurisdictionConfig,
11973    /// VAT/GST configuration.
11974    #[serde(default)]
11975    pub vat_gst: VatGstConfig,
11976    /// Sales tax configuration.
11977    #[serde(default)]
11978    pub sales_tax: SalesTaxConfig,
11979    /// Withholding tax configuration.
11980    #[serde(default)]
11981    pub withholding: WithholdingTaxSchemaConfig,
11982    /// Tax provision configuration.
11983    #[serde(default)]
11984    pub provisions: TaxProvisionSchemaConfig,
11985    /// Payroll tax configuration.
11986    #[serde(default)]
11987    pub payroll_tax: PayrollTaxSchemaConfig,
11988    /// Anomaly injection rate for tax data (0.0 to 1.0).
11989    #[serde(default = "default_tax_anomaly_rate")]
11990    pub anomaly_rate: f64,
11991}
11992
11993fn default_tax_anomaly_rate() -> f64 {
11994    0.03
11995}
11996
11997impl Default for TaxConfig {
11998    fn default() -> Self {
11999        Self {
12000            enabled: false,
12001            jurisdictions: TaxJurisdictionConfig::default(),
12002            vat_gst: VatGstConfig::default(),
12003            sales_tax: SalesTaxConfig::default(),
12004            withholding: WithholdingTaxSchemaConfig::default(),
12005            provisions: TaxProvisionSchemaConfig::default(),
12006            payroll_tax: PayrollTaxSchemaConfig::default(),
12007            anomaly_rate: default_tax_anomaly_rate(),
12008        }
12009    }
12010}
12011
12012/// Tax jurisdiction configuration.
12013///
12014/// Specifies which countries and subnational jurisdictions to include
12015/// when generating tax data.
12016#[derive(Debug, Clone, Default, Serialize, Deserialize)]
12017pub struct TaxJurisdictionConfig {
12018    /// List of country codes to include (e.g., ["US", "DE", "GB"]).
12019    #[serde(default)]
12020    pub countries: Vec<String>,
12021    /// Whether to include subnational jurisdictions (e.g., US states, Canadian provinces).
12022    #[serde(default)]
12023    pub include_subnational: bool,
12024}
12025
12026/// VAT/GST configuration.
12027///
12028/// Controls generation of Value Added Tax / Goods and Services Tax data,
12029/// including standard and reduced rates, exempt categories, and reverse charge.
12030#[derive(Debug, Clone, Serialize, Deserialize)]
12031pub struct VatGstConfig {
12032    /// Whether VAT/GST generation is enabled.
12033    #[serde(default)]
12034    pub enabled: bool,
12035    /// Standard VAT/GST rates by country code (e.g., {"DE": 0.19, "GB": 0.20}).
12036    #[serde(default)]
12037    pub standard_rates: std::collections::HashMap<String, f64>,
12038    /// Reduced VAT/GST rates by country code (e.g., {"DE": 0.07, "GB": 0.05}).
12039    #[serde(default)]
12040    pub reduced_rates: std::collections::HashMap<String, f64>,
12041    /// Categories exempt from VAT/GST (e.g., ["financial_services", "healthcare"]).
12042    #[serde(default)]
12043    pub exempt_categories: Vec<String>,
12044    /// Whether to apply reverse charge mechanism for cross-border B2B transactions.
12045    #[serde(default = "default_true")]
12046    pub reverse_charge: bool,
12047}
12048
12049impl Default for VatGstConfig {
12050    fn default() -> Self {
12051        Self {
12052            enabled: false,
12053            standard_rates: std::collections::HashMap::new(),
12054            reduced_rates: std::collections::HashMap::new(),
12055            exempt_categories: Vec::new(),
12056            reverse_charge: true,
12057        }
12058    }
12059}
12060
12061/// Sales tax configuration.
12062///
12063/// Controls generation of US-style sales tax data including nexus determination.
12064#[derive(Debug, Clone, Default, Serialize, Deserialize)]
12065pub struct SalesTaxConfig {
12066    /// Whether sales tax generation is enabled.
12067    #[serde(default)]
12068    pub enabled: bool,
12069    /// US states where the company has nexus (e.g., ["CA", "NY", "TX"]).
12070    #[serde(default)]
12071    pub nexus_states: Vec<String>,
12072}
12073
12074/// Withholding tax configuration.
12075///
12076/// Controls generation of withholding tax data for cross-border payments,
12077/// including treaty network and rate overrides.
12078#[derive(Debug, Clone, Serialize, Deserialize)]
12079pub struct WithholdingTaxSchemaConfig {
12080    /// Whether withholding tax generation is enabled.
12081    #[serde(default)]
12082    pub enabled: bool,
12083    /// Whether to simulate a treaty network with reduced rates.
12084    #[serde(default = "default_true")]
12085    pub treaty_network: bool,
12086    /// Default withholding tax rate for non-treaty countries (0.0 to 1.0).
12087    #[serde(default = "default_withholding_rate")]
12088    pub default_rate: f64,
12089    /// Reduced withholding tax rate for treaty countries (0.0 to 1.0).
12090    #[serde(default = "default_treaty_reduced_rate")]
12091    pub treaty_reduced_rate: f64,
12092}
12093
12094fn default_withholding_rate() -> f64 {
12095    0.30
12096}
12097
12098fn default_treaty_reduced_rate() -> f64 {
12099    0.15
12100}
12101
12102impl Default for WithholdingTaxSchemaConfig {
12103    fn default() -> Self {
12104        Self {
12105            enabled: false,
12106            treaty_network: true,
12107            default_rate: default_withholding_rate(),
12108            treaty_reduced_rate: default_treaty_reduced_rate(),
12109        }
12110    }
12111}
12112
12113/// Tax provision configuration.
12114///
12115/// Controls generation of tax provision data including statutory rates
12116/// and uncertain tax positions (ASC 740 / IAS 12).
12117#[derive(Debug, Clone, Serialize, Deserialize)]
12118pub struct TaxProvisionSchemaConfig {
12119    /// Whether tax provision generation is enabled.
12120    /// Defaults to true when tax is enabled, as provisions are typically required.
12121    #[serde(default = "default_true")]
12122    pub enabled: bool,
12123    /// Statutory corporate tax rate (0.0 to 1.0).
12124    #[serde(default = "default_statutory_rate")]
12125    pub statutory_rate: f64,
12126    /// Whether to generate uncertain tax positions (FIN 48 / IFRIC 23).
12127    #[serde(default = "default_true")]
12128    pub uncertain_positions: bool,
12129}
12130
12131fn default_statutory_rate() -> f64 {
12132    0.21
12133}
12134
12135impl Default for TaxProvisionSchemaConfig {
12136    fn default() -> Self {
12137        Self {
12138            enabled: true,
12139            statutory_rate: default_statutory_rate(),
12140            uncertain_positions: true,
12141        }
12142    }
12143}
12144
12145/// Payroll tax configuration.
12146///
12147/// Controls generation of payroll tax data (employer/employee contributions,
12148/// social security, Medicare, etc.).
12149#[derive(Debug, Clone, Default, Serialize, Deserialize)]
12150pub struct PayrollTaxSchemaConfig {
12151    /// Whether payroll tax generation is enabled.
12152    #[serde(default)]
12153    pub enabled: bool,
12154}
12155
12156// ---------------------------------------------------------------------------
12157// Treasury & Cash Management Configuration
12158// ---------------------------------------------------------------------------
12159
12160/// Treasury and cash management configuration.
12161///
12162/// Controls generation of cash positions, forecasts, pooling, hedging
12163/// instruments (ASC 815 / IFRS 9), debt instruments with covenants,
12164/// bank guarantees, and intercompany netting runs.
12165#[derive(Debug, Clone, Serialize, Deserialize)]
12166pub struct TreasuryConfig {
12167    /// Whether treasury generation is enabled.
12168    #[serde(default)]
12169    pub enabled: bool,
12170    /// Cash positioning configuration.
12171    #[serde(default)]
12172    pub cash_positioning: CashPositioningConfig,
12173    /// Cash forecasting configuration.
12174    #[serde(default)]
12175    pub cash_forecasting: CashForecastingConfig,
12176    /// Cash pooling configuration.
12177    #[serde(default)]
12178    pub cash_pooling: CashPoolingConfig,
12179    /// Hedging configuration (FX forwards, IR swaps, etc.).
12180    #[serde(default)]
12181    pub hedging: HedgingSchemaConfig,
12182    /// Debt instrument and covenant configuration.
12183    #[serde(default)]
12184    pub debt: DebtSchemaConfig,
12185    /// Intercompany netting configuration.
12186    #[serde(default)]
12187    pub netting: NettingSchemaConfig,
12188    /// Bank guarantee / letter of credit configuration.
12189    #[serde(default)]
12190    pub bank_guarantees: BankGuaranteeSchemaConfig,
12191    /// Anomaly injection rate for treasury data (0.0 to 1.0).
12192    #[serde(default = "default_treasury_anomaly_rate")]
12193    pub anomaly_rate: f64,
12194}
12195
12196fn default_treasury_anomaly_rate() -> f64 {
12197    0.02
12198}
12199
12200impl Default for TreasuryConfig {
12201    fn default() -> Self {
12202        Self {
12203            enabled: false,
12204            cash_positioning: CashPositioningConfig::default(),
12205            cash_forecasting: CashForecastingConfig::default(),
12206            cash_pooling: CashPoolingConfig::default(),
12207            hedging: HedgingSchemaConfig::default(),
12208            debt: DebtSchemaConfig::default(),
12209            netting: NettingSchemaConfig::default(),
12210            bank_guarantees: BankGuaranteeSchemaConfig::default(),
12211            anomaly_rate: default_treasury_anomaly_rate(),
12212        }
12213    }
12214}
12215
12216/// Cash positioning configuration.
12217///
12218/// Controls daily cash position generation per entity/bank account.
12219#[derive(Debug, Clone, Serialize, Deserialize)]
12220pub struct CashPositioningConfig {
12221    /// Whether cash positioning is enabled.
12222    #[serde(default = "default_true")]
12223    pub enabled: bool,
12224    /// Position generation frequency.
12225    #[serde(default = "default_cash_frequency")]
12226    pub frequency: String,
12227    /// Minimum cash balance policy threshold.
12228    #[serde(default = "default_minimum_balance_policy")]
12229    pub minimum_balance_policy: f64,
12230}
12231
12232fn default_cash_frequency() -> String {
12233    "daily".to_string()
12234}
12235
12236fn default_minimum_balance_policy() -> f64 {
12237    100_000.0
12238}
12239
12240impl Default for CashPositioningConfig {
12241    fn default() -> Self {
12242        Self {
12243            enabled: true,
12244            frequency: default_cash_frequency(),
12245            minimum_balance_policy: default_minimum_balance_policy(),
12246        }
12247    }
12248}
12249
12250/// Cash forecasting configuration.
12251///
12252/// Controls forward-looking cash forecast generation with probability-weighted items.
12253#[derive(Debug, Clone, Serialize, Deserialize)]
12254pub struct CashForecastingConfig {
12255    /// Whether cash forecasting is enabled.
12256    #[serde(default = "default_true")]
12257    pub enabled: bool,
12258    /// Number of days to forecast into the future.
12259    #[serde(default = "default_horizon_days")]
12260    pub horizon_days: u32,
12261    /// AR collection probability curve type ("aging" or "flat").
12262    #[serde(default = "default_ar_probability_curve")]
12263    pub ar_collection_probability_curve: String,
12264    /// Confidence interval for the forecast (0.0 to 1.0).
12265    #[serde(default = "default_confidence_interval")]
12266    pub confidence_interval: f64,
12267}
12268
12269fn default_horizon_days() -> u32 {
12270    90
12271}
12272
12273fn default_ar_probability_curve() -> String {
12274    "aging".to_string()
12275}
12276
12277fn default_confidence_interval() -> f64 {
12278    0.90
12279}
12280
12281impl Default for CashForecastingConfig {
12282    fn default() -> Self {
12283        Self {
12284            enabled: true,
12285            horizon_days: default_horizon_days(),
12286            ar_collection_probability_curve: default_ar_probability_curve(),
12287            confidence_interval: default_confidence_interval(),
12288        }
12289    }
12290}
12291
12292/// Cash pooling configuration.
12293///
12294/// Controls cash pool structure generation (physical, notional, zero-balancing).
12295#[derive(Debug, Clone, Serialize, Deserialize)]
12296pub struct CashPoolingConfig {
12297    /// Whether cash pooling is enabled.
12298    #[serde(default)]
12299    pub enabled: bool,
12300    /// Pool type: "physical_pooling", "notional_pooling", or "zero_balancing".
12301    #[serde(default = "default_pool_type")]
12302    pub pool_type: String,
12303    /// Time of day when sweeps occur (HH:MM format).
12304    #[serde(default = "default_sweep_time")]
12305    pub sweep_time: String,
12306}
12307
12308fn default_pool_type() -> String {
12309    "zero_balancing".to_string()
12310}
12311
12312fn default_sweep_time() -> String {
12313    "16:00".to_string()
12314}
12315
12316impl Default for CashPoolingConfig {
12317    fn default() -> Self {
12318        Self {
12319            enabled: false,
12320            pool_type: default_pool_type(),
12321            sweep_time: default_sweep_time(),
12322        }
12323    }
12324}
12325
12326/// Hedging configuration.
12327///
12328/// Controls generation of hedging instruments and hedge relationship designations
12329/// under ASC 815 / IFRS 9.
12330#[derive(Debug, Clone, Serialize, Deserialize)]
12331pub struct HedgingSchemaConfig {
12332    /// Whether hedging generation is enabled.
12333    #[serde(default)]
12334    pub enabled: bool,
12335    /// Target hedge ratio (0.0 to 1.0). Proportion of FX exposure to hedge.
12336    #[serde(default = "default_hedge_ratio")]
12337    pub hedge_ratio: f64,
12338    /// Types of instruments to generate (e.g., ["fx_forward", "interest_rate_swap"]).
12339    #[serde(default = "default_hedge_instruments")]
12340    pub instruments: Vec<String>,
12341    /// Whether to designate formal hedge accounting relationships.
12342    #[serde(default = "default_true")]
12343    pub hedge_accounting: bool,
12344    /// Effectiveness testing method: "dollar_offset", "regression", or "critical_terms".
12345    #[serde(default = "default_effectiveness_method")]
12346    pub effectiveness_method: String,
12347}
12348
12349fn default_hedge_ratio() -> f64 {
12350    0.75
12351}
12352
12353fn default_hedge_instruments() -> Vec<String> {
12354    vec!["fx_forward".to_string(), "interest_rate_swap".to_string()]
12355}
12356
12357fn default_effectiveness_method() -> String {
12358    "regression".to_string()
12359}
12360
12361impl Default for HedgingSchemaConfig {
12362    fn default() -> Self {
12363        Self {
12364            enabled: false,
12365            hedge_ratio: default_hedge_ratio(),
12366            instruments: default_hedge_instruments(),
12367            hedge_accounting: true,
12368            effectiveness_method: default_effectiveness_method(),
12369        }
12370    }
12371}
12372
12373/// Debt instrument configuration.
12374///
12375/// Controls generation of debt instruments (term loans, revolving credit, bonds)
12376/// with amortization schedules and financial covenants.
12377#[derive(Debug, Clone, Default, Serialize, Deserialize)]
12378pub struct DebtSchemaConfig {
12379    /// Whether debt instrument generation is enabled.
12380    #[serde(default)]
12381    pub enabled: bool,
12382    /// Debt instrument definitions.
12383    #[serde(default)]
12384    pub instruments: Vec<DebtInstrumentDef>,
12385    /// Covenant definitions.
12386    #[serde(default)]
12387    pub covenants: Vec<CovenantDef>,
12388}
12389
12390/// Definition of a debt instrument in configuration.
12391#[derive(Debug, Clone, Serialize, Deserialize)]
12392pub struct DebtInstrumentDef {
12393    /// Instrument type: "term_loan", "revolving_credit", "bond", "commercial_paper", "bridge_loan".
12394    #[serde(rename = "type")]
12395    pub instrument_type: String,
12396    /// Principal amount (for term loans, bonds).
12397    #[serde(default)]
12398    pub principal: Option<f64>,
12399    /// Interest rate (annual, as decimal fraction).
12400    #[serde(default)]
12401    pub rate: Option<f64>,
12402    /// Maturity in months.
12403    #[serde(default)]
12404    pub maturity_months: Option<u32>,
12405    /// Facility limit (for revolving credit).
12406    #[serde(default)]
12407    pub facility: Option<f64>,
12408}
12409
12410/// Definition of a debt covenant in configuration.
12411#[derive(Debug, Clone, Serialize, Deserialize)]
12412pub struct CovenantDef {
12413    /// Covenant type: "debt_to_equity", "interest_coverage", "current_ratio",
12414    /// "net_worth", "debt_to_ebitda", "fixed_charge_coverage".
12415    #[serde(rename = "type")]
12416    pub covenant_type: String,
12417    /// Covenant threshold value.
12418    pub threshold: f64,
12419}
12420
12421/// Intercompany netting configuration.
12422///
12423/// Controls generation of multilateral netting runs.
12424#[derive(Debug, Clone, Serialize, Deserialize)]
12425pub struct NettingSchemaConfig {
12426    /// Whether netting generation is enabled.
12427    #[serde(default)]
12428    pub enabled: bool,
12429    /// Netting cycle: "daily", "weekly", or "monthly".
12430    #[serde(default = "default_netting_cycle")]
12431    pub cycle: String,
12432}
12433
12434fn default_netting_cycle() -> String {
12435    "monthly".to_string()
12436}
12437
12438impl Default for NettingSchemaConfig {
12439    fn default() -> Self {
12440        Self {
12441            enabled: false,
12442            cycle: default_netting_cycle(),
12443        }
12444    }
12445}
12446
12447/// Bank guarantee and letter of credit configuration.
12448///
12449/// Controls generation of bank guarantees, standby LCs, and performance bonds.
12450#[derive(Debug, Clone, Serialize, Deserialize)]
12451pub struct BankGuaranteeSchemaConfig {
12452    /// Whether bank guarantee generation is enabled.
12453    #[serde(default)]
12454    pub enabled: bool,
12455    /// Number of guarantees to generate.
12456    #[serde(default = "default_guarantee_count")]
12457    pub count: u32,
12458}
12459
12460fn default_guarantee_count() -> u32 {
12461    5
12462}
12463
12464impl Default for BankGuaranteeSchemaConfig {
12465    fn default() -> Self {
12466        Self {
12467            enabled: false,
12468            count: default_guarantee_count(),
12469        }
12470    }
12471}
12472
12473// ===========================================================================
12474// Project Accounting Configuration
12475// ===========================================================================
12476
12477/// Project accounting configuration.
12478///
12479/// Controls generation of project cost lines, revenue recognition,
12480/// milestones, change orders, retainage, and earned value metrics.
12481#[derive(Debug, Clone, Serialize, Deserialize)]
12482pub struct ProjectAccountingConfig {
12483    /// Whether project accounting is enabled.
12484    #[serde(default)]
12485    pub enabled: bool,
12486    /// Number of projects to generate.
12487    #[serde(default = "default_project_count")]
12488    pub project_count: u32,
12489    /// Distribution of project types (capital, internal, customer, r_and_d, maintenance, technology).
12490    #[serde(default)]
12491    pub project_types: ProjectTypeDistribution,
12492    /// WBS structure configuration.
12493    #[serde(default)]
12494    pub wbs: WbsSchemaConfig,
12495    /// Cost allocation rates (what % of source documents get project-tagged).
12496    #[serde(default)]
12497    pub cost_allocation: CostAllocationConfig,
12498    /// Revenue recognition configuration for project accounting.
12499    #[serde(default)]
12500    pub revenue_recognition: ProjectRevenueRecognitionConfig,
12501    /// Milestone configuration.
12502    #[serde(default)]
12503    pub milestones: MilestoneSchemaConfig,
12504    /// Change order configuration.
12505    #[serde(default)]
12506    pub change_orders: ChangeOrderSchemaConfig,
12507    /// Retainage configuration.
12508    #[serde(default)]
12509    pub retainage: RetainageSchemaConfig,
12510    /// Earned value management configuration.
12511    #[serde(default)]
12512    pub earned_value: EarnedValueSchemaConfig,
12513    /// Anomaly injection rate for project accounting data (0.0 to 1.0).
12514    #[serde(default = "default_project_anomaly_rate")]
12515    pub anomaly_rate: f64,
12516}
12517
12518fn default_project_count() -> u32 {
12519    10
12520}
12521
12522fn default_project_anomaly_rate() -> f64 {
12523    0.03
12524}
12525
12526impl Default for ProjectAccountingConfig {
12527    fn default() -> Self {
12528        Self {
12529            enabled: false,
12530            project_count: default_project_count(),
12531            project_types: ProjectTypeDistribution::default(),
12532            wbs: WbsSchemaConfig::default(),
12533            cost_allocation: CostAllocationConfig::default(),
12534            revenue_recognition: ProjectRevenueRecognitionConfig::default(),
12535            milestones: MilestoneSchemaConfig::default(),
12536            change_orders: ChangeOrderSchemaConfig::default(),
12537            retainage: RetainageSchemaConfig::default(),
12538            earned_value: EarnedValueSchemaConfig::default(),
12539            anomaly_rate: default_project_anomaly_rate(),
12540        }
12541    }
12542}
12543
12544/// Distribution of project types by weight.
12545#[derive(Debug, Clone, Serialize, Deserialize)]
12546pub struct ProjectTypeDistribution {
12547    /// Weight for capital projects (default 0.25).
12548    #[serde(default = "default_capital_weight")]
12549    pub capital: f64,
12550    /// Weight for internal projects (default 0.20).
12551    #[serde(default = "default_internal_weight")]
12552    pub internal: f64,
12553    /// Weight for customer projects (default 0.30).
12554    #[serde(default = "default_customer_weight")]
12555    pub customer: f64,
12556    /// Weight for R&D projects (default 0.10).
12557    #[serde(default = "default_rnd_weight")]
12558    pub r_and_d: f64,
12559    /// Weight for maintenance projects (default 0.10).
12560    #[serde(default = "default_maintenance_weight")]
12561    pub maintenance: f64,
12562    /// Weight for technology projects (default 0.05).
12563    #[serde(default = "default_technology_weight")]
12564    pub technology: f64,
12565}
12566
12567fn default_capital_weight() -> f64 {
12568    0.25
12569}
12570fn default_internal_weight() -> f64 {
12571    0.20
12572}
12573fn default_customer_weight() -> f64 {
12574    0.30
12575}
12576fn default_rnd_weight() -> f64 {
12577    0.10
12578}
12579fn default_maintenance_weight() -> f64 {
12580    0.10
12581}
12582fn default_technology_weight() -> f64 {
12583    0.05
12584}
12585
12586impl Default for ProjectTypeDistribution {
12587    fn default() -> Self {
12588        Self {
12589            capital: default_capital_weight(),
12590            internal: default_internal_weight(),
12591            customer: default_customer_weight(),
12592            r_and_d: default_rnd_weight(),
12593            maintenance: default_maintenance_weight(),
12594            technology: default_technology_weight(),
12595        }
12596    }
12597}
12598
12599/// WBS structure configuration.
12600#[derive(Debug, Clone, Serialize, Deserialize)]
12601pub struct WbsSchemaConfig {
12602    /// Maximum depth of WBS hierarchy (default 3).
12603    #[serde(default = "default_wbs_max_depth")]
12604    pub max_depth: u32,
12605    /// Minimum elements per level-1 WBS (default 2).
12606    #[serde(default = "default_wbs_min_elements")]
12607    pub min_elements_per_level: u32,
12608    /// Maximum elements per level-1 WBS (default 6).
12609    #[serde(default = "default_wbs_max_elements")]
12610    pub max_elements_per_level: u32,
12611}
12612
12613fn default_wbs_max_depth() -> u32 {
12614    3
12615}
12616fn default_wbs_min_elements() -> u32 {
12617    2
12618}
12619fn default_wbs_max_elements() -> u32 {
12620    6
12621}
12622
12623impl Default for WbsSchemaConfig {
12624    fn default() -> Self {
12625        Self {
12626            max_depth: default_wbs_max_depth(),
12627            min_elements_per_level: default_wbs_min_elements(),
12628            max_elements_per_level: default_wbs_max_elements(),
12629        }
12630    }
12631}
12632
12633/// Cost allocation rates — what fraction of each document type gets linked to a project.
12634#[derive(Debug, Clone, Serialize, Deserialize)]
12635pub struct CostAllocationConfig {
12636    /// Fraction of time entries assigned to projects (0.0 to 1.0).
12637    #[serde(default = "default_time_entry_rate")]
12638    pub time_entry_project_rate: f64,
12639    /// Fraction of expense reports assigned to projects (0.0 to 1.0).
12640    #[serde(default = "default_expense_rate")]
12641    pub expense_project_rate: f64,
12642    /// Fraction of purchase orders assigned to projects (0.0 to 1.0).
12643    #[serde(default = "default_po_rate")]
12644    pub purchase_order_project_rate: f64,
12645    /// Fraction of vendor invoices assigned to projects (0.0 to 1.0).
12646    #[serde(default = "default_vi_rate")]
12647    pub vendor_invoice_project_rate: f64,
12648}
12649
12650fn default_time_entry_rate() -> f64 {
12651    0.60
12652}
12653fn default_expense_rate() -> f64 {
12654    0.30
12655}
12656fn default_po_rate() -> f64 {
12657    0.40
12658}
12659fn default_vi_rate() -> f64 {
12660    0.35
12661}
12662
12663impl Default for CostAllocationConfig {
12664    fn default() -> Self {
12665        Self {
12666            time_entry_project_rate: default_time_entry_rate(),
12667            expense_project_rate: default_expense_rate(),
12668            purchase_order_project_rate: default_po_rate(),
12669            vendor_invoice_project_rate: default_vi_rate(),
12670        }
12671    }
12672}
12673
12674/// Revenue recognition configuration for project accounting.
12675#[derive(Debug, Clone, Serialize, Deserialize)]
12676pub struct ProjectRevenueRecognitionConfig {
12677    /// Whether revenue recognition is enabled for customer projects.
12678    #[serde(default = "default_true")]
12679    pub enabled: bool,
12680    /// Default method: "percentage_of_completion", "completed_contract", "milestone_based".
12681    #[serde(default = "default_revenue_method")]
12682    pub method: String,
12683    /// Default completion measure: "cost_to_cost", "labor_hours", "physical_completion".
12684    #[serde(default = "default_completion_measure")]
12685    pub completion_measure: String,
12686    /// Average contract value for customer projects.
12687    #[serde(default = "default_avg_contract_value")]
12688    pub avg_contract_value: f64,
12689}
12690
12691fn default_revenue_method() -> String {
12692    "percentage_of_completion".to_string()
12693}
12694fn default_completion_measure() -> String {
12695    "cost_to_cost".to_string()
12696}
12697fn default_avg_contract_value() -> f64 {
12698    500_000.0
12699}
12700
12701impl Default for ProjectRevenueRecognitionConfig {
12702    fn default() -> Self {
12703        Self {
12704            enabled: true,
12705            method: default_revenue_method(),
12706            completion_measure: default_completion_measure(),
12707            avg_contract_value: default_avg_contract_value(),
12708        }
12709    }
12710}
12711
12712/// Milestone configuration.
12713#[derive(Debug, Clone, Serialize, Deserialize)]
12714pub struct MilestoneSchemaConfig {
12715    /// Whether milestone generation is enabled.
12716    #[serde(default = "default_true")]
12717    pub enabled: bool,
12718    /// Average number of milestones per project.
12719    #[serde(default = "default_milestones_per_project")]
12720    pub avg_per_project: u32,
12721    /// Fraction of milestones that are payment milestones (0.0 to 1.0).
12722    #[serde(default = "default_payment_milestone_rate")]
12723    pub payment_milestone_rate: f64,
12724}
12725
12726fn default_milestones_per_project() -> u32 {
12727    4
12728}
12729fn default_payment_milestone_rate() -> f64 {
12730    0.50
12731}
12732
12733impl Default for MilestoneSchemaConfig {
12734    fn default() -> Self {
12735        Self {
12736            enabled: true,
12737            avg_per_project: default_milestones_per_project(),
12738            payment_milestone_rate: default_payment_milestone_rate(),
12739        }
12740    }
12741}
12742
12743/// Change order configuration.
12744#[derive(Debug, Clone, Serialize, Deserialize)]
12745pub struct ChangeOrderSchemaConfig {
12746    /// Whether change order generation is enabled.
12747    #[serde(default = "default_true")]
12748    pub enabled: bool,
12749    /// Probability that a project will have at least one change order (0.0 to 1.0).
12750    #[serde(default = "default_change_order_probability")]
12751    pub probability: f64,
12752    /// Maximum change orders per project.
12753    #[serde(default = "default_max_change_orders")]
12754    pub max_per_project: u32,
12755    /// Approval rate for change orders (0.0 to 1.0).
12756    #[serde(default = "default_change_order_approval_rate")]
12757    pub approval_rate: f64,
12758}
12759
12760fn default_change_order_probability() -> f64 {
12761    0.40
12762}
12763fn default_max_change_orders() -> u32 {
12764    3
12765}
12766fn default_change_order_approval_rate() -> f64 {
12767    0.75
12768}
12769
12770impl Default for ChangeOrderSchemaConfig {
12771    fn default() -> Self {
12772        Self {
12773            enabled: true,
12774            probability: default_change_order_probability(),
12775            max_per_project: default_max_change_orders(),
12776            approval_rate: default_change_order_approval_rate(),
12777        }
12778    }
12779}
12780
12781/// Retainage configuration.
12782#[derive(Debug, Clone, Serialize, Deserialize)]
12783pub struct RetainageSchemaConfig {
12784    /// Whether retainage is enabled.
12785    #[serde(default)]
12786    pub enabled: bool,
12787    /// Default retainage percentage (0.0 to 1.0, e.g., 0.10 for 10%).
12788    #[serde(default = "default_retainage_pct")]
12789    pub default_percentage: f64,
12790}
12791
12792fn default_retainage_pct() -> f64 {
12793    0.10
12794}
12795
12796impl Default for RetainageSchemaConfig {
12797    fn default() -> Self {
12798        Self {
12799            enabled: false,
12800            default_percentage: default_retainage_pct(),
12801        }
12802    }
12803}
12804
12805/// Earned value management (EVM) configuration.
12806#[derive(Debug, Clone, Serialize, Deserialize)]
12807pub struct EarnedValueSchemaConfig {
12808    /// Whether EVM metrics are generated.
12809    #[serde(default = "default_true")]
12810    pub enabled: bool,
12811    /// Measurement frequency: "weekly", "biweekly", "monthly".
12812    #[serde(default = "default_evm_frequency")]
12813    pub frequency: String,
12814}
12815
12816fn default_evm_frequency() -> String {
12817    "monthly".to_string()
12818}
12819
12820impl Default for EarnedValueSchemaConfig {
12821    fn default() -> Self {
12822        Self {
12823            enabled: true,
12824            frequency: default_evm_frequency(),
12825        }
12826    }
12827}
12828
12829// =============================================================================
12830// ESG / Sustainability Configuration
12831// =============================================================================
12832
12833/// Top-level ESG / sustainability reporting configuration.
12834#[derive(Debug, Clone, Serialize, Deserialize)]
12835pub struct EsgConfig {
12836    /// Whether ESG generation is enabled.
12837    #[serde(default)]
12838    pub enabled: bool,
12839    /// Environmental metrics (emissions, energy, water, waste).
12840    #[serde(default)]
12841    pub environmental: EnvironmentalConfig,
12842    /// Social metrics (diversity, pay equity, safety).
12843    #[serde(default)]
12844    pub social: SocialConfig,
12845    /// Governance metrics (board composition, ethics, compliance).
12846    #[serde(default)]
12847    pub governance: GovernanceSchemaConfig,
12848    /// Supply-chain ESG assessment settings.
12849    #[serde(default)]
12850    pub supply_chain_esg: SupplyChainEsgConfig,
12851    /// ESG reporting / disclosure framework settings.
12852    #[serde(default)]
12853    pub reporting: EsgReportingConfig,
12854    /// Climate scenario analysis settings.
12855    #[serde(default)]
12856    pub climate_scenarios: ClimateScenarioConfig,
12857    /// Anomaly injection rate for ESG data (0.0 to 1.0).
12858    #[serde(default = "default_esg_anomaly_rate")]
12859    pub anomaly_rate: f64,
12860}
12861
12862fn default_esg_anomaly_rate() -> f64 {
12863    0.02
12864}
12865
12866impl Default for EsgConfig {
12867    fn default() -> Self {
12868        Self {
12869            enabled: false,
12870            environmental: EnvironmentalConfig::default(),
12871            social: SocialConfig::default(),
12872            governance: GovernanceSchemaConfig::default(),
12873            supply_chain_esg: SupplyChainEsgConfig::default(),
12874            reporting: EsgReportingConfig::default(),
12875            climate_scenarios: ClimateScenarioConfig::default(),
12876            anomaly_rate: default_esg_anomaly_rate(),
12877        }
12878    }
12879}
12880
12881/// Country pack configuration.
12882///
12883/// Controls where to load additional country packs and per-country overrides.
12884/// When omitted, only the built-in packs (_default, US, DE, GB) are used.
12885#[derive(Debug, Clone, Serialize, Deserialize, Default)]
12886pub struct CountryPacksSchemaConfig {
12887    /// Optional directory containing additional `*.json` country packs.
12888    #[serde(default)]
12889    pub external_dir: Option<PathBuf>,
12890    /// Per-country overrides applied after loading.
12891    /// Keys are ISO 3166-1 alpha-2 codes; values are partial JSON objects
12892    /// that are deep-merged on top of the loaded pack.
12893    #[serde(default)]
12894    pub overrides: std::collections::HashMap<String, serde_json::Value>,
12895}
12896
12897/// Environmental metrics configuration.
12898#[derive(Debug, Clone, Serialize, Deserialize)]
12899pub struct EnvironmentalConfig {
12900    /// Whether environmental metrics are generated.
12901    #[serde(default = "default_true")]
12902    pub enabled: bool,
12903    /// Scope 1 (direct) emission generation settings.
12904    #[serde(default)]
12905    pub scope1: EmissionScopeConfig,
12906    /// Scope 2 (purchased energy) emission generation settings.
12907    #[serde(default)]
12908    pub scope2: EmissionScopeConfig,
12909    /// Scope 3 (value chain) emission generation settings.
12910    #[serde(default)]
12911    pub scope3: Scope3Config,
12912    /// Energy consumption tracking settings.
12913    #[serde(default)]
12914    pub energy: EnergySchemaConfig,
12915    /// Water usage tracking settings.
12916    #[serde(default)]
12917    pub water: WaterSchemaConfig,
12918    /// Waste management tracking settings.
12919    #[serde(default)]
12920    pub waste: WasteSchemaConfig,
12921}
12922
12923impl Default for EnvironmentalConfig {
12924    fn default() -> Self {
12925        Self {
12926            enabled: true,
12927            scope1: EmissionScopeConfig::default(),
12928            scope2: EmissionScopeConfig::default(),
12929            scope3: Scope3Config::default(),
12930            energy: EnergySchemaConfig::default(),
12931            water: WaterSchemaConfig::default(),
12932            waste: WasteSchemaConfig::default(),
12933        }
12934    }
12935}
12936
12937/// Configuration for a single emission scope (Scope 1 or 2).
12938#[derive(Debug, Clone, Serialize, Deserialize)]
12939pub struct EmissionScopeConfig {
12940    /// Whether this scope is enabled.
12941    #[serde(default = "default_true")]
12942    pub enabled: bool,
12943    /// Emission factor region (e.g., "US", "EU", "global").
12944    #[serde(default = "default_emission_region")]
12945    pub factor_region: String,
12946}
12947
12948fn default_emission_region() -> String {
12949    "US".to_string()
12950}
12951
12952impl Default for EmissionScopeConfig {
12953    fn default() -> Self {
12954        Self {
12955            enabled: true,
12956            factor_region: default_emission_region(),
12957        }
12958    }
12959}
12960
12961/// Scope 3 (value chain) emission configuration.
12962#[derive(Debug, Clone, Serialize, Deserialize)]
12963pub struct Scope3Config {
12964    /// Whether Scope 3 emissions are generated.
12965    #[serde(default = "default_true")]
12966    pub enabled: bool,
12967    /// Categories to include (e.g., "purchased_goods", "business_travel", "commuting").
12968    #[serde(default = "default_scope3_categories")]
12969    pub categories: Vec<String>,
12970    /// Spend-based emission intensity (kg CO2e per USD).
12971    #[serde(default = "default_spend_intensity")]
12972    pub default_spend_intensity_kg_per_usd: f64,
12973}
12974
12975fn default_scope3_categories() -> Vec<String> {
12976    vec![
12977        "purchased_goods".to_string(),
12978        "business_travel".to_string(),
12979        "employee_commuting".to_string(),
12980    ]
12981}
12982
12983fn default_spend_intensity() -> f64 {
12984    0.5
12985}
12986
12987impl Default for Scope3Config {
12988    fn default() -> Self {
12989        Self {
12990            enabled: true,
12991            categories: default_scope3_categories(),
12992            default_spend_intensity_kg_per_usd: default_spend_intensity(),
12993        }
12994    }
12995}
12996
12997/// Energy consumption configuration.
12998#[derive(Debug, Clone, Serialize, Deserialize)]
12999pub struct EnergySchemaConfig {
13000    /// Whether energy consumption tracking is enabled.
13001    #[serde(default = "default_true")]
13002    pub enabled: bool,
13003    /// Number of facilities to generate.
13004    #[serde(default = "default_facility_count")]
13005    pub facility_count: u32,
13006    /// Target percentage of energy from renewable sources (0.0 to 1.0).
13007    #[serde(default = "default_renewable_target")]
13008    pub renewable_target: f64,
13009}
13010
13011fn default_facility_count() -> u32 {
13012    5
13013}
13014
13015fn default_renewable_target() -> f64 {
13016    0.30
13017}
13018
13019impl Default for EnergySchemaConfig {
13020    fn default() -> Self {
13021        Self {
13022            enabled: true,
13023            facility_count: default_facility_count(),
13024            renewable_target: default_renewable_target(),
13025        }
13026    }
13027}
13028
13029/// Water usage configuration.
13030#[derive(Debug, Clone, Serialize, Deserialize)]
13031pub struct WaterSchemaConfig {
13032    /// Whether water usage tracking is enabled.
13033    #[serde(default = "default_true")]
13034    pub enabled: bool,
13035    /// Number of facilities with water tracking.
13036    #[serde(default = "default_water_facility_count")]
13037    pub facility_count: u32,
13038}
13039
13040fn default_water_facility_count() -> u32 {
13041    3
13042}
13043
13044impl Default for WaterSchemaConfig {
13045    fn default() -> Self {
13046        Self {
13047            enabled: true,
13048            facility_count: default_water_facility_count(),
13049        }
13050    }
13051}
13052
13053/// Waste management configuration.
13054#[derive(Debug, Clone, Serialize, Deserialize)]
13055pub struct WasteSchemaConfig {
13056    /// Whether waste tracking is enabled.
13057    #[serde(default = "default_true")]
13058    pub enabled: bool,
13059    /// Target diversion rate (0.0 to 1.0).
13060    #[serde(default = "default_diversion_target")]
13061    pub diversion_target: f64,
13062}
13063
13064fn default_diversion_target() -> f64 {
13065    0.50
13066}
13067
13068impl Default for WasteSchemaConfig {
13069    fn default() -> Self {
13070        Self {
13071            enabled: true,
13072            diversion_target: default_diversion_target(),
13073        }
13074    }
13075}
13076
13077/// Social metrics configuration.
13078#[derive(Debug, Clone, Serialize, Deserialize)]
13079pub struct SocialConfig {
13080    /// Whether social metrics are generated.
13081    #[serde(default = "default_true")]
13082    pub enabled: bool,
13083    /// Workforce diversity tracking settings.
13084    #[serde(default)]
13085    pub diversity: DiversitySchemaConfig,
13086    /// Pay equity analysis settings.
13087    #[serde(default)]
13088    pub pay_equity: PayEquitySchemaConfig,
13089    /// Safety incident and metrics settings.
13090    #[serde(default)]
13091    pub safety: SafetySchemaConfig,
13092}
13093
13094impl Default for SocialConfig {
13095    fn default() -> Self {
13096        Self {
13097            enabled: true,
13098            diversity: DiversitySchemaConfig::default(),
13099            pay_equity: PayEquitySchemaConfig::default(),
13100            safety: SafetySchemaConfig::default(),
13101        }
13102    }
13103}
13104
13105/// Workforce diversity configuration.
13106#[derive(Debug, Clone, Serialize, Deserialize)]
13107pub struct DiversitySchemaConfig {
13108    /// Whether diversity metrics are generated.
13109    #[serde(default = "default_true")]
13110    pub enabled: bool,
13111    /// Dimensions to track (e.g., "gender", "ethnicity", "age_group").
13112    #[serde(default = "default_diversity_dimensions")]
13113    pub dimensions: Vec<String>,
13114}
13115
13116fn default_diversity_dimensions() -> Vec<String> {
13117    vec![
13118        "gender".to_string(),
13119        "ethnicity".to_string(),
13120        "age_group".to_string(),
13121    ]
13122}
13123
13124impl Default for DiversitySchemaConfig {
13125    fn default() -> Self {
13126        Self {
13127            enabled: true,
13128            dimensions: default_diversity_dimensions(),
13129        }
13130    }
13131}
13132
13133/// Pay equity analysis configuration.
13134#[derive(Debug, Clone, Serialize, Deserialize)]
13135pub struct PayEquitySchemaConfig {
13136    /// Whether pay equity analysis is generated.
13137    #[serde(default = "default_true")]
13138    pub enabled: bool,
13139    /// Target pay gap threshold for flagging (e.g., 0.05 = 5% gap).
13140    #[serde(default = "default_pay_gap_threshold")]
13141    pub gap_threshold: f64,
13142}
13143
13144fn default_pay_gap_threshold() -> f64 {
13145    0.05
13146}
13147
13148impl Default for PayEquitySchemaConfig {
13149    fn default() -> Self {
13150        Self {
13151            enabled: true,
13152            gap_threshold: default_pay_gap_threshold(),
13153        }
13154    }
13155}
13156
13157/// Safety metrics configuration.
13158#[derive(Debug, Clone, Serialize, Deserialize)]
13159pub struct SafetySchemaConfig {
13160    /// Whether safety metrics are generated.
13161    #[serde(default = "default_true")]
13162    pub enabled: bool,
13163    /// Average annual recordable incidents per 200,000 hours.
13164    #[serde(default = "default_trir_target")]
13165    pub target_trir: f64,
13166    /// Number of safety incidents to generate.
13167    #[serde(default = "default_incident_count")]
13168    pub incident_count: u32,
13169}
13170
13171fn default_trir_target() -> f64 {
13172    2.5
13173}
13174
13175fn default_incident_count() -> u32 {
13176    20
13177}
13178
13179impl Default for SafetySchemaConfig {
13180    fn default() -> Self {
13181        Self {
13182            enabled: true,
13183            target_trir: default_trir_target(),
13184            incident_count: default_incident_count(),
13185        }
13186    }
13187}
13188
13189/// Governance metrics configuration.
13190#[derive(Debug, Clone, Serialize, Deserialize)]
13191pub struct GovernanceSchemaConfig {
13192    /// Whether governance metrics are generated.
13193    #[serde(default = "default_true")]
13194    pub enabled: bool,
13195    /// Number of board members.
13196    #[serde(default = "default_board_size")]
13197    pub board_size: u32,
13198    /// Target independent director ratio (0.0 to 1.0).
13199    #[serde(default = "default_independence_target")]
13200    pub independence_target: f64,
13201}
13202
13203fn default_board_size() -> u32 {
13204    11
13205}
13206
13207fn default_independence_target() -> f64 {
13208    0.67
13209}
13210
13211impl Default for GovernanceSchemaConfig {
13212    fn default() -> Self {
13213        Self {
13214            enabled: true,
13215            board_size: default_board_size(),
13216            independence_target: default_independence_target(),
13217        }
13218    }
13219}
13220
13221/// Supply-chain ESG assessment configuration.
13222#[derive(Debug, Clone, Serialize, Deserialize)]
13223pub struct SupplyChainEsgConfig {
13224    /// Whether supply chain ESG assessments are generated.
13225    #[serde(default = "default_true")]
13226    pub enabled: bool,
13227    /// Proportion of vendors to assess (0.0 to 1.0).
13228    #[serde(default = "default_assessment_coverage")]
13229    pub assessment_coverage: f64,
13230    /// High-risk country codes for automatic flagging.
13231    #[serde(default = "default_high_risk_countries")]
13232    pub high_risk_countries: Vec<String>,
13233}
13234
13235fn default_assessment_coverage() -> f64 {
13236    0.80
13237}
13238
13239fn default_high_risk_countries() -> Vec<String> {
13240    vec!["CN".to_string(), "BD".to_string(), "MM".to_string()]
13241}
13242
13243impl Default for SupplyChainEsgConfig {
13244    fn default() -> Self {
13245        Self {
13246            enabled: true,
13247            assessment_coverage: default_assessment_coverage(),
13248            high_risk_countries: default_high_risk_countries(),
13249        }
13250    }
13251}
13252
13253/// ESG reporting / disclosure framework configuration.
13254#[derive(Debug, Clone, Serialize, Deserialize)]
13255pub struct EsgReportingConfig {
13256    /// Whether ESG disclosures are generated.
13257    #[serde(default = "default_true")]
13258    pub enabled: bool,
13259    /// Frameworks to generate disclosures for.
13260    #[serde(default = "default_esg_frameworks")]
13261    pub frameworks: Vec<String>,
13262    /// Whether materiality assessment is performed.
13263    #[serde(default = "default_true")]
13264    pub materiality_assessment: bool,
13265    /// Materiality threshold for impact dimension (0.0 to 1.0).
13266    #[serde(default = "default_materiality_threshold")]
13267    pub impact_threshold: f64,
13268    /// Materiality threshold for financial dimension (0.0 to 1.0).
13269    #[serde(default = "default_materiality_threshold")]
13270    pub financial_threshold: f64,
13271}
13272
13273fn default_esg_frameworks() -> Vec<String> {
13274    vec!["GRI".to_string(), "ESRS".to_string()]
13275}
13276
13277fn default_materiality_threshold() -> f64 {
13278    0.6
13279}
13280
13281impl Default for EsgReportingConfig {
13282    fn default() -> Self {
13283        Self {
13284            enabled: true,
13285            frameworks: default_esg_frameworks(),
13286            materiality_assessment: true,
13287            impact_threshold: default_materiality_threshold(),
13288            financial_threshold: default_materiality_threshold(),
13289        }
13290    }
13291}
13292
13293/// Climate scenario analysis configuration.
13294#[derive(Debug, Clone, Serialize, Deserialize)]
13295pub struct ClimateScenarioConfig {
13296    /// Whether climate scenario analysis is generated.
13297    #[serde(default)]
13298    pub enabled: bool,
13299    /// Scenarios to model (e.g., "net_zero_2050", "stated_policies", "current_trajectory").
13300    #[serde(default = "default_climate_scenarios")]
13301    pub scenarios: Vec<String>,
13302    /// Time horizons in years to project.
13303    #[serde(default = "default_time_horizons")]
13304    pub time_horizons: Vec<u32>,
13305}
13306
13307fn default_climate_scenarios() -> Vec<String> {
13308    vec![
13309        "net_zero_2050".to_string(),
13310        "stated_policies".to_string(),
13311        "current_trajectory".to_string(),
13312    ]
13313}
13314
13315fn default_time_horizons() -> Vec<u32> {
13316    vec![5, 10, 30]
13317}
13318
13319impl Default for ClimateScenarioConfig {
13320    fn default() -> Self {
13321        Self {
13322            enabled: false,
13323            scenarios: default_climate_scenarios(),
13324            time_horizons: default_time_horizons(),
13325        }
13326    }
13327}
13328
13329// ===== Counterfactual Simulation Scenarios =====
13330
13331/// Configuration for counterfactual simulation scenarios.
13332#[derive(Debug, Clone, Serialize, Deserialize, Default)]
13333pub struct ScenariosConfig {
13334    /// Whether scenario generation is enabled.
13335    #[serde(default)]
13336    pub enabled: bool,
13337    /// List of scenario definitions.
13338    #[serde(default)]
13339    pub scenarios: Vec<ScenarioSchemaConfig>,
13340    /// Causal model configuration.
13341    #[serde(default)]
13342    pub causal_model: CausalModelSchemaConfig,
13343    /// Default settings applied to all scenarios.
13344    #[serde(default)]
13345    pub defaults: ScenarioDefaultsConfig,
13346    /// Generate counterfactual (original, mutated) JE pairs for ML training.
13347    /// When true, the orchestrator produces paired clean/anomalous journal entries.
13348    #[serde(default)]
13349    pub generate_counterfactuals: bool,
13350}
13351
13352/// A single scenario definition in the config.
13353#[derive(Debug, Clone, Serialize, Deserialize)]
13354pub struct ScenarioSchemaConfig {
13355    /// Scenario name (must be unique).
13356    pub name: String,
13357    /// Human-readable description.
13358    #[serde(default)]
13359    pub description: String,
13360    /// Tags for categorization.
13361    #[serde(default)]
13362    pub tags: Vec<String>,
13363    /// Base scenario name (None = default config).
13364    pub base: Option<String>,
13365    /// IFRS 9-style probability weight.
13366    pub probability_weight: Option<f64>,
13367    /// List of interventions to apply.
13368    #[serde(default)]
13369    pub interventions: Vec<InterventionSchemaConfig>,
13370    /// Constraint overrides for this scenario.
13371    #[serde(default)]
13372    pub constraints: ScenarioConstraintsSchemaConfig,
13373    /// Output configuration for this scenario.
13374    #[serde(default)]
13375    pub output: ScenarioOutputSchemaConfig,
13376    /// Arbitrary metadata.
13377    #[serde(default)]
13378    pub metadata: std::collections::HashMap<String, String>,
13379}
13380
13381/// An intervention definition in the config.
13382#[derive(Debug, Clone, Serialize, Deserialize)]
13383pub struct InterventionSchemaConfig {
13384    /// Intervention type and parameters (flattened tagged enum).
13385    #[serde(flatten)]
13386    pub intervention_type: serde_json::Value,
13387    /// Timing configuration.
13388    #[serde(default)]
13389    pub timing: InterventionTimingSchemaConfig,
13390    /// Human-readable label.
13391    pub label: Option<String>,
13392    /// Priority for conflict resolution (higher wins).
13393    #[serde(default)]
13394    pub priority: u32,
13395}
13396
13397/// Timing configuration for an intervention.
13398#[derive(Debug, Clone, Serialize, Deserialize)]
13399pub struct InterventionTimingSchemaConfig {
13400    /// Month offset from start (1-indexed).
13401    #[serde(default = "default_start_month")]
13402    pub start_month: u32,
13403    /// Duration in months.
13404    pub duration_months: Option<u32>,
13405    /// Onset type: "sudden", "gradual", "oscillating", "custom".
13406    #[serde(default = "default_onset")]
13407    pub onset: String,
13408    /// Ramp period in months.
13409    pub ramp_months: Option<u32>,
13410}
13411
13412fn default_start_month() -> u32 {
13413    1
13414}
13415
13416fn default_onset() -> String {
13417    "sudden".to_string()
13418}
13419
13420impl Default for InterventionTimingSchemaConfig {
13421    fn default() -> Self {
13422        Self {
13423            start_month: 1,
13424            duration_months: None,
13425            onset: "sudden".to_string(),
13426            ramp_months: None,
13427        }
13428    }
13429}
13430
13431/// Scenario constraint overrides.
13432#[derive(Debug, Clone, Serialize, Deserialize)]
13433pub struct ScenarioConstraintsSchemaConfig {
13434    #[serde(default = "default_true")]
13435    pub preserve_accounting_identity: bool,
13436    #[serde(default = "default_true")]
13437    pub preserve_document_chains: bool,
13438    #[serde(default = "default_true")]
13439    pub preserve_period_close: bool,
13440    #[serde(default = "default_true")]
13441    pub preserve_balance_coherence: bool,
13442    #[serde(default)]
13443    pub custom: Vec<CustomConstraintSchemaConfig>,
13444}
13445
13446impl Default for ScenarioConstraintsSchemaConfig {
13447    fn default() -> Self {
13448        Self {
13449            preserve_accounting_identity: true,
13450            preserve_document_chains: true,
13451            preserve_period_close: true,
13452            preserve_balance_coherence: true,
13453            custom: Vec::new(),
13454        }
13455    }
13456}
13457
13458/// Custom constraint in config.
13459#[derive(Debug, Clone, Serialize, Deserialize)]
13460pub struct CustomConstraintSchemaConfig {
13461    pub config_path: String,
13462    pub min: Option<f64>,
13463    pub max: Option<f64>,
13464    #[serde(default)]
13465    pub description: String,
13466}
13467
13468/// Output configuration for a scenario.
13469#[derive(Debug, Clone, Serialize, Deserialize)]
13470pub struct ScenarioOutputSchemaConfig {
13471    #[serde(default = "default_true")]
13472    pub paired: bool,
13473    #[serde(default = "default_diff_formats_schema")]
13474    pub diff_formats: Vec<String>,
13475    #[serde(default)]
13476    pub diff_scope: Vec<String>,
13477}
13478
13479fn default_diff_formats_schema() -> Vec<String> {
13480    vec!["summary".to_string(), "aggregate".to_string()]
13481}
13482
13483impl Default for ScenarioOutputSchemaConfig {
13484    fn default() -> Self {
13485        Self {
13486            paired: true,
13487            diff_formats: default_diff_formats_schema(),
13488            diff_scope: Vec::new(),
13489        }
13490    }
13491}
13492
13493/// Causal model configuration.
13494#[derive(Debug, Clone, Serialize, Deserialize)]
13495pub struct CausalModelSchemaConfig {
13496    /// Preset name: "default", "minimal", or "custom".
13497    #[serde(default = "default_causal_preset")]
13498    pub preset: String,
13499    /// Custom nodes (merged with preset).
13500    #[serde(default)]
13501    pub nodes: Vec<serde_json::Value>,
13502    /// Custom edges (merged with preset).
13503    #[serde(default)]
13504    pub edges: Vec<serde_json::Value>,
13505}
13506
13507fn default_causal_preset() -> String {
13508    "default".to_string()
13509}
13510
13511impl Default for CausalModelSchemaConfig {
13512    fn default() -> Self {
13513        Self {
13514            preset: "default".to_string(),
13515            nodes: Vec::new(),
13516            edges: Vec::new(),
13517        }
13518    }
13519}
13520
13521/// Default settings applied to all scenarios.
13522#[derive(Debug, Clone, Serialize, Deserialize, Default)]
13523pub struct ScenarioDefaultsConfig {
13524    #[serde(default)]
13525    pub constraints: ScenarioConstraintsSchemaConfig,
13526    #[serde(default)]
13527    pub output: ScenarioOutputSchemaConfig,
13528}
13529
13530// =====================================================================
13531// Compliance Regulations Framework Configuration
13532// =====================================================================
13533
13534/// Top-level configuration for the compliance regulations framework.
13535///
13536/// Controls standards registry, jurisdiction profiles, temporal versioning,
13537/// audit procedure templates, compliance graph integration, and output settings.
13538///
13539/// # Example
13540///
13541/// ```yaml
13542/// compliance_regulations:
13543///   enabled: true
13544///   jurisdictions: [US, DE, GB]
13545///   reference_date: "2025-06-30"
13546///   standards_selection:
13547///     categories: [accounting, auditing, regulatory]
13548///     include: ["IFRS-16", "ASC-606"]
13549///   audit_procedures:
13550///     enabled: true
13551///     procedures_per_standard: 3
13552///   findings:
13553///     enabled: true
13554///     finding_rate: 0.05
13555///   filings:
13556///     enabled: true
13557///   graph:
13558///     enabled: true
13559///     include_compliance_nodes: true
13560///     include_compliance_edges: true
13561/// ```
13562#[derive(Debug, Clone, Default, Serialize, Deserialize)]
13563pub struct ComplianceRegulationsConfig {
13564    /// Master switch for the compliance regulations framework.
13565    #[serde(default)]
13566    pub enabled: bool,
13567    /// Jurisdictions to generate compliance data for (ISO 3166-1 alpha-2 codes).
13568    /// If empty, inferred from company countries in the config.
13569    #[serde(default)]
13570    pub jurisdictions: Vec<String>,
13571    /// Reference date for temporal standard resolution (YYYY-MM-DD).
13572    /// Defaults to the global start_date if not set.
13573    #[serde(default)]
13574    pub reference_date: Option<String>,
13575    /// Standards selection filters.
13576    #[serde(default)]
13577    pub standards_selection: StandardsSelectionConfig,
13578    /// Audit procedure generation settings.
13579    #[serde(default)]
13580    pub audit_procedures: AuditProcedureGenConfig,
13581    /// Compliance finding generation settings.
13582    #[serde(default)]
13583    pub findings: ComplianceFindingGenConfig,
13584    /// Regulatory filing generation settings.
13585    #[serde(default)]
13586    pub filings: ComplianceFilingGenConfig,
13587    /// Compliance graph integration settings.
13588    #[serde(default)]
13589    pub graph: ComplianceGraphConfig,
13590    /// Output settings for compliance-specific files.
13591    #[serde(default)]
13592    pub output: ComplianceOutputConfig,
13593    /// v3.3.0: legal-document generation (engagement letters,
13594    /// management reps, legal opinions, regulatory filings, board
13595    /// resolutions). Requires `compliance_regulations.enabled = true`
13596    /// AND `legal_documents.enabled = true` to take effect.
13597    #[serde(default)]
13598    pub legal_documents: LegalDocumentsConfig,
13599}
13600
13601/// Legal-document generation settings (v3.3.0+).
13602///
13603/// Wires `LegalDocumentGenerator` into the orchestrator. Generates one
13604/// batch per audit engagement when enabled.
13605#[derive(Debug, Clone, Serialize, Deserialize)]
13606pub struct LegalDocumentsConfig {
13607    /// Master switch.
13608    #[serde(default)]
13609    pub enabled: bool,
13610    /// Probability of including a legal-opinion document in an engagement.
13611    #[serde(default = "default_legal_opinion_probability")]
13612    pub legal_opinion_probability: f64,
13613}
13614
13615fn default_legal_opinion_probability() -> f64 {
13616    0.40
13617}
13618
13619impl Default for LegalDocumentsConfig {
13620    fn default() -> Self {
13621        Self {
13622            enabled: false,
13623            legal_opinion_probability: default_legal_opinion_probability(),
13624        }
13625    }
13626}
13627
13628/// Filters which standards are included in the generation.
13629#[derive(Debug, Clone, Default, Serialize, Deserialize)]
13630pub struct StandardsSelectionConfig {
13631    /// Standard categories to include (accounting, auditing, regulatory, tax, esg).
13632    /// Empty = all categories.
13633    #[serde(default)]
13634    pub categories: Vec<String>,
13635    /// Explicit standard IDs to include (e.g., ["IFRS-16", "ASC-606"]).
13636    /// When non-empty, only these standards (plus mandatory ones for selected jurisdictions) are used.
13637    #[serde(default)]
13638    pub include: Vec<String>,
13639    /// Standard IDs to exclude.
13640    #[serde(default)]
13641    pub exclude: Vec<String>,
13642    /// Include superseded standards in the output (for historical analysis).
13643    #[serde(default)]
13644    pub include_superseded: bool,
13645}
13646
13647/// Configuration for audit procedure template generation.
13648#[derive(Debug, Clone, Serialize, Deserialize)]
13649pub struct AuditProcedureGenConfig {
13650    /// Whether audit procedure generation is enabled.
13651    #[serde(default)]
13652    pub enabled: bool,
13653    /// Number of procedures to generate per applicable standard.
13654    #[serde(default = "default_procedures_per_standard")]
13655    pub procedures_per_standard: usize,
13656    /// Sampling methodology: "statistical", "non_statistical", "mixed".
13657    #[serde(default = "default_sampling_method")]
13658    pub sampling_method: String,
13659    /// Confidence level for statistical sampling (0.0-1.0).
13660    #[serde(default = "default_confidence_level")]
13661    pub confidence_level: f64,
13662    /// Tolerable misstatement rate for sampling (0.0-1.0).
13663    #[serde(default = "default_tolerable_misstatement")]
13664    pub tolerable_misstatement: f64,
13665}
13666
13667fn default_procedures_per_standard() -> usize {
13668    3
13669}
13670
13671fn default_sampling_method() -> String {
13672    "statistical".to_string()
13673}
13674
13675fn default_confidence_level() -> f64 {
13676    0.95
13677}
13678
13679fn default_tolerable_misstatement() -> f64 {
13680    0.05
13681}
13682
13683impl Default for AuditProcedureGenConfig {
13684    fn default() -> Self {
13685        Self {
13686            enabled: false,
13687            procedures_per_standard: default_procedures_per_standard(),
13688            sampling_method: default_sampling_method(),
13689            confidence_level: default_confidence_level(),
13690            tolerable_misstatement: default_tolerable_misstatement(),
13691        }
13692    }
13693}
13694
13695/// Configuration for compliance finding generation.
13696#[derive(Debug, Clone, Serialize, Deserialize)]
13697pub struct ComplianceFindingGenConfig {
13698    /// Whether finding generation is enabled.
13699    #[serde(default)]
13700    pub enabled: bool,
13701    /// Rate of findings per audit procedure (0.0-1.0).
13702    #[serde(default = "default_finding_rate")]
13703    pub finding_rate: f64,
13704    /// Rate of material weakness findings among all findings (0.0-1.0).
13705    #[serde(default = "default_cr_material_weakness_rate")]
13706    pub material_weakness_rate: f64,
13707    /// Rate of significant deficiency findings among all findings (0.0-1.0).
13708    #[serde(default = "default_cr_significant_deficiency_rate")]
13709    pub significant_deficiency_rate: f64,
13710    /// Whether to generate remediation plans for findings.
13711    #[serde(default = "default_true")]
13712    pub generate_remediation: bool,
13713}
13714
13715fn default_finding_rate() -> f64 {
13716    0.05
13717}
13718
13719fn default_cr_material_weakness_rate() -> f64 {
13720    0.02
13721}
13722
13723fn default_cr_significant_deficiency_rate() -> f64 {
13724    0.08
13725}
13726
13727impl Default for ComplianceFindingGenConfig {
13728    fn default() -> Self {
13729        Self {
13730            enabled: false,
13731            finding_rate: default_finding_rate(),
13732            material_weakness_rate: default_cr_material_weakness_rate(),
13733            significant_deficiency_rate: default_cr_significant_deficiency_rate(),
13734            generate_remediation: true,
13735        }
13736    }
13737}
13738
13739/// Configuration for regulatory filing generation.
13740#[derive(Debug, Clone, Serialize, Deserialize)]
13741pub struct ComplianceFilingGenConfig {
13742    /// Whether filing generation is enabled.
13743    #[serde(default)]
13744    pub enabled: bool,
13745    /// Filing types to include (e.g., ["10-K", "10-Q", "Jahresabschluss"]).
13746    /// Empty = all applicable filings for the selected jurisdictions.
13747    #[serde(default)]
13748    pub filing_types: Vec<String>,
13749    /// Generate filing status progression (draft → filed → accepted).
13750    #[serde(default = "default_true")]
13751    pub generate_status_progression: bool,
13752}
13753
13754impl Default for ComplianceFilingGenConfig {
13755    fn default() -> Self {
13756        Self {
13757            enabled: false,
13758            filing_types: Vec::new(),
13759            generate_status_progression: true,
13760        }
13761    }
13762}
13763
13764/// Configuration for compliance graph integration.
13765#[derive(Debug, Clone, Serialize, Deserialize)]
13766pub struct ComplianceGraphConfig {
13767    /// Whether compliance graph integration is enabled.
13768    #[serde(default)]
13769    pub enabled: bool,
13770    /// Include compliance nodes (Standard, Regulation, Jurisdiction, etc.).
13771    #[serde(default = "default_true")]
13772    pub include_compliance_nodes: bool,
13773    /// Include compliance edges (MapsToStandard, TestsControl, etc.).
13774    #[serde(default = "default_true")]
13775    pub include_compliance_edges: bool,
13776    /// Include cross-reference edges between standards.
13777    #[serde(default = "default_true")]
13778    pub include_cross_references: bool,
13779    /// Include temporal supersession edges.
13780    #[serde(default)]
13781    pub include_supersession_edges: bool,
13782    /// Include edges linking standards to the GL account types they govern.
13783    #[serde(default = "default_true")]
13784    pub include_account_links: bool,
13785    /// Include edges linking standards to the internal controls that implement them.
13786    #[serde(default = "default_true")]
13787    pub include_control_links: bool,
13788    /// Include edges linking filings and jurisdictions to the originating company.
13789    #[serde(default = "default_true")]
13790    pub include_company_links: bool,
13791}
13792
13793impl Default for ComplianceGraphConfig {
13794    fn default() -> Self {
13795        Self {
13796            enabled: false,
13797            include_compliance_nodes: true,
13798            include_compliance_edges: true,
13799            include_cross_references: true,
13800            include_supersession_edges: false,
13801            include_account_links: true,
13802            include_control_links: true,
13803            include_company_links: true,
13804        }
13805    }
13806}
13807
13808/// Output settings for compliance-specific data files.
13809#[derive(Debug, Clone, Serialize, Deserialize)]
13810pub struct ComplianceOutputConfig {
13811    /// Export the standards registry catalog.
13812    #[serde(default = "default_true")]
13813    pub export_registry: bool,
13814    /// Export jurisdiction profiles.
13815    #[serde(default = "default_true")]
13816    pub export_jurisdictions: bool,
13817    /// Export cross-reference map.
13818    #[serde(default = "default_true")]
13819    pub export_cross_references: bool,
13820    /// Export temporal version history.
13821    #[serde(default)]
13822    pub export_version_history: bool,
13823}
13824
13825impl Default for ComplianceOutputConfig {
13826    fn default() -> Self {
13827        Self {
13828            export_registry: true,
13829            export_jurisdictions: true,
13830            export_cross_references: true,
13831            export_version_history: false,
13832        }
13833    }
13834}
13835
13836#[cfg(test)]
13837#[allow(clippy::unwrap_used)]
13838mod tests {
13839    use super::*;
13840    use crate::presets::demo_preset;
13841
13842    // ==========================================================================
13843    // Serialization/Deserialization Tests
13844    // ==========================================================================
13845
13846    #[test]
13847    fn test_config_yaml_roundtrip() {
13848        let config = demo_preset();
13849        let yaml = serde_yaml::to_string(&config).expect("Failed to serialize to YAML");
13850        let deserialized: GeneratorConfig =
13851            serde_yaml::from_str(&yaml).expect("Failed to deserialize from YAML");
13852
13853        assert_eq!(
13854            config.global.period_months,
13855            deserialized.global.period_months
13856        );
13857        assert_eq!(config.global.industry, deserialized.global.industry);
13858        assert_eq!(config.companies.len(), deserialized.companies.len());
13859        assert_eq!(config.companies[0].code, deserialized.companies[0].code);
13860    }
13861
13862    #[test]
13863    fn test_config_json_roundtrip() {
13864        // Create a config without infinity values (JSON can't serialize f64::INFINITY)
13865        let mut config = demo_preset();
13866        // Replace infinity with a large but finite value for JSON compatibility
13867        config.master_data.employees.approval_limits.executive = 1e12;
13868
13869        let json = serde_json::to_string(&config).expect("Failed to serialize to JSON");
13870        let deserialized: GeneratorConfig =
13871            serde_json::from_str(&json).expect("Failed to deserialize from JSON");
13872
13873        assert_eq!(
13874            config.global.period_months,
13875            deserialized.global.period_months
13876        );
13877        assert_eq!(config.global.industry, deserialized.global.industry);
13878        assert_eq!(config.companies.len(), deserialized.companies.len());
13879    }
13880
13881    #[test]
13882    fn test_transaction_volume_serialization() {
13883        // Test various transaction volumes serialize correctly
13884        let volumes = vec![
13885            (TransactionVolume::TenK, "ten_k"),
13886            (TransactionVolume::HundredK, "hundred_k"),
13887            (TransactionVolume::OneM, "one_m"),
13888            (TransactionVolume::TenM, "ten_m"),
13889            (TransactionVolume::HundredM, "hundred_m"),
13890        ];
13891
13892        for (volume, expected_key) in volumes {
13893            let json = serde_json::to_string(&volume).expect("Failed to serialize");
13894            assert!(
13895                json.contains(expected_key),
13896                "Expected {} in JSON: {}",
13897                expected_key,
13898                json
13899            );
13900        }
13901    }
13902
13903    #[test]
13904    fn test_transaction_volume_custom_serialization() {
13905        let volume = TransactionVolume::Custom(12345);
13906        let json = serde_json::to_string(&volume).expect("Failed to serialize");
13907        let deserialized: TransactionVolume =
13908            serde_json::from_str(&json).expect("Failed to deserialize");
13909        assert_eq!(deserialized.count(), 12345);
13910    }
13911
13912    #[test]
13913    fn test_output_mode_serialization() {
13914        let modes = vec![
13915            OutputMode::Streaming,
13916            OutputMode::FlatFile,
13917            OutputMode::Both,
13918        ];
13919
13920        for mode in modes {
13921            let json = serde_json::to_string(&mode).expect("Failed to serialize");
13922            let deserialized: OutputMode =
13923                serde_json::from_str(&json).expect("Failed to deserialize");
13924            assert!(format!("{:?}", mode) == format!("{:?}", deserialized));
13925        }
13926    }
13927
13928    #[test]
13929    fn test_file_format_serialization() {
13930        let formats = vec![
13931            FileFormat::Csv,
13932            FileFormat::Parquet,
13933            FileFormat::Json,
13934            FileFormat::JsonLines,
13935        ];
13936
13937        for format in formats {
13938            let json = serde_json::to_string(&format).expect("Failed to serialize");
13939            let deserialized: FileFormat =
13940                serde_json::from_str(&json).expect("Failed to deserialize");
13941            assert!(format!("{:?}", format) == format!("{:?}", deserialized));
13942        }
13943    }
13944
13945    #[test]
13946    fn test_compression_algorithm_serialization() {
13947        let algos = vec![
13948            CompressionAlgorithm::Gzip,
13949            CompressionAlgorithm::Zstd,
13950            CompressionAlgorithm::Lz4,
13951            CompressionAlgorithm::Snappy,
13952        ];
13953
13954        for algo in algos {
13955            let json = serde_json::to_string(&algo).expect("Failed to serialize");
13956            let deserialized: CompressionAlgorithm =
13957                serde_json::from_str(&json).expect("Failed to deserialize");
13958            assert!(format!("{:?}", algo) == format!("{:?}", deserialized));
13959        }
13960    }
13961
13962    #[test]
13963    fn test_transfer_pricing_method_serialization() {
13964        let methods = vec![
13965            TransferPricingMethod::CostPlus,
13966            TransferPricingMethod::ComparableUncontrolled,
13967            TransferPricingMethod::ResalePrice,
13968            TransferPricingMethod::TransactionalNetMargin,
13969            TransferPricingMethod::ProfitSplit,
13970        ];
13971
13972        for method in methods {
13973            let json = serde_json::to_string(&method).expect("Failed to serialize");
13974            let deserialized: TransferPricingMethod =
13975                serde_json::from_str(&json).expect("Failed to deserialize");
13976            assert!(format!("{:?}", method) == format!("{:?}", deserialized));
13977        }
13978    }
13979
13980    #[test]
13981    fn test_benford_exemption_serialization() {
13982        let exemptions = vec![
13983            BenfordExemption::Recurring,
13984            BenfordExemption::Payroll,
13985            BenfordExemption::FixedFees,
13986            BenfordExemption::RoundAmounts,
13987        ];
13988
13989        for exemption in exemptions {
13990            let json = serde_json::to_string(&exemption).expect("Failed to serialize");
13991            let deserialized: BenfordExemption =
13992                serde_json::from_str(&json).expect("Failed to deserialize");
13993            assert!(format!("{:?}", exemption) == format!("{:?}", deserialized));
13994        }
13995    }
13996
13997    // ==========================================================================
13998    // Default Value Tests
13999    // ==========================================================================
14000
14001    #[test]
14002    fn test_global_config_defaults() {
14003        let yaml = r#"
14004            industry: manufacturing
14005            start_date: "2024-01-01"
14006            period_months: 6
14007        "#;
14008        let config: GlobalConfig = serde_yaml::from_str(yaml).expect("Failed to parse");
14009        assert_eq!(config.group_currency, "USD");
14010        assert!(config.parallel);
14011        assert_eq!(config.worker_threads, 0);
14012        assert_eq!(config.memory_limit_mb, 0);
14013    }
14014
14015    #[test]
14016    fn test_fraud_config_defaults() {
14017        let config = FraudConfig::default();
14018        assert!(!config.enabled);
14019        assert_eq!(config.fraud_rate, 0.005);
14020        assert!(!config.clustering_enabled);
14021    }
14022
14023    #[test]
14024    fn test_internal_controls_config_defaults() {
14025        let config = InternalControlsConfig::default();
14026        assert!(!config.enabled);
14027        assert_eq!(config.exception_rate, 0.02);
14028        assert_eq!(config.sod_violation_rate, 0.01);
14029        assert!(config.export_control_master_data);
14030        assert_eq!(config.sox_materiality_threshold, 10000.0);
14031        // COSO fields
14032        assert!(config.coso_enabled);
14033        assert!(!config.include_entity_level_controls);
14034        assert_eq!(config.target_maturity_level, "mixed");
14035    }
14036
14037    #[test]
14038    fn test_output_config_defaults() {
14039        let config = OutputConfig::default();
14040        assert!(matches!(config.mode, OutputMode::FlatFile));
14041        assert_eq!(config.formats, vec![FileFormat::Parquet]);
14042        assert!(config.compression.enabled);
14043        assert!(matches!(
14044            config.compression.algorithm,
14045            CompressionAlgorithm::Zstd
14046        ));
14047        assert!(config.include_acdoca);
14048        assert!(!config.include_bseg);
14049        assert!(config.partition_by_period);
14050        assert!(!config.partition_by_company);
14051    }
14052
14053    #[test]
14054    fn test_approval_config_defaults() {
14055        let config = ApprovalConfig::default();
14056        assert!(!config.enabled);
14057        assert_eq!(config.auto_approve_threshold, 1000.0);
14058        assert_eq!(config.rejection_rate, 0.02);
14059        assert_eq!(config.revision_rate, 0.05);
14060        assert_eq!(config.average_approval_delay_hours, 4.0);
14061        assert_eq!(config.thresholds.len(), 4);
14062    }
14063
14064    #[test]
14065    fn test_p2p_flow_config_defaults() {
14066        let config = P2PFlowConfig::default();
14067        assert!(config.enabled);
14068        assert_eq!(config.three_way_match_rate, 0.95);
14069        assert_eq!(config.partial_delivery_rate, 0.15);
14070        assert_eq!(config.average_po_to_gr_days, 14);
14071    }
14072
14073    #[test]
14074    fn test_o2c_flow_config_defaults() {
14075        let config = O2CFlowConfig::default();
14076        assert!(config.enabled);
14077        assert_eq!(config.credit_check_failure_rate, 0.02);
14078        assert_eq!(config.return_rate, 0.03);
14079        assert_eq!(config.bad_debt_rate, 0.01);
14080    }
14081
14082    #[test]
14083    fn test_balance_config_defaults() {
14084        let config = BalanceConfig::default();
14085        assert!(!config.generate_opening_balances);
14086        assert!(config.generate_trial_balances);
14087        assert_eq!(config.target_gross_margin, 0.35);
14088        assert!(config.validate_balance_equation);
14089        assert!(config.reconcile_subledgers);
14090    }
14091
14092    // ==========================================================================
14093    // Partial Config Deserialization Tests
14094    // ==========================================================================
14095
14096    #[test]
14097    fn test_partial_config_with_defaults() {
14098        // Minimal config that should use all defaults
14099        let yaml = r#"
14100            global:
14101              industry: manufacturing
14102              start_date: "2024-01-01"
14103              period_months: 3
14104            companies:
14105              - code: "TEST"
14106                name: "Test Company"
14107                currency: "USD"
14108                country: "US"
14109                annual_transaction_volume: ten_k
14110            chart_of_accounts:
14111              complexity: small
14112            output:
14113              output_directory: "./output"
14114        "#;
14115
14116        let config: GeneratorConfig = serde_yaml::from_str(yaml).expect("Failed to parse");
14117        assert_eq!(config.global.period_months, 3);
14118        assert_eq!(config.companies.len(), 1);
14119        assert!(!config.fraud.enabled); // Default
14120        assert!(!config.internal_controls.enabled); // Default
14121    }
14122
14123    #[test]
14124    fn test_config_with_fraud_enabled() {
14125        let yaml = r#"
14126            global:
14127              industry: retail
14128              start_date: "2024-01-01"
14129              period_months: 12
14130            companies:
14131              - code: "RETAIL"
14132                name: "Retail Co"
14133                currency: "USD"
14134                country: "US"
14135                annual_transaction_volume: hundred_k
14136            chart_of_accounts:
14137              complexity: medium
14138            output:
14139              output_directory: "./output"
14140            fraud:
14141              enabled: true
14142              fraud_rate: 0.05
14143              clustering_enabled: true
14144        "#;
14145
14146        let config: GeneratorConfig = serde_yaml::from_str(yaml).expect("Failed to parse");
14147        assert!(config.fraud.enabled);
14148        assert_eq!(config.fraud.fraud_rate, 0.05);
14149        assert!(config.fraud.clustering_enabled);
14150    }
14151
14152    #[test]
14153    fn test_config_with_multiple_companies() {
14154        let yaml = r#"
14155            global:
14156              industry: manufacturing
14157              start_date: "2024-01-01"
14158              period_months: 6
14159            companies:
14160              - code: "HQ"
14161                name: "Headquarters"
14162                currency: "USD"
14163                country: "US"
14164                annual_transaction_volume: hundred_k
14165                volume_weight: 1.0
14166              - code: "EU"
14167                name: "European Subsidiary"
14168                currency: "EUR"
14169                country: "DE"
14170                annual_transaction_volume: hundred_k
14171                volume_weight: 0.5
14172              - code: "APAC"
14173                name: "Asia Pacific"
14174                currency: "JPY"
14175                country: "JP"
14176                annual_transaction_volume: ten_k
14177                volume_weight: 0.3
14178            chart_of_accounts:
14179              complexity: large
14180            output:
14181              output_directory: "./output"
14182        "#;
14183
14184        let config: GeneratorConfig = serde_yaml::from_str(yaml).expect("Failed to parse");
14185        assert_eq!(config.companies.len(), 3);
14186        assert_eq!(config.companies[0].code, "HQ");
14187        assert_eq!(config.companies[1].currency, "EUR");
14188        assert_eq!(config.companies[2].volume_weight, 0.3);
14189    }
14190
14191    #[test]
14192    fn test_intercompany_config() {
14193        let yaml = r#"
14194            enabled: true
14195            ic_transaction_rate: 0.20
14196            transfer_pricing_method: cost_plus
14197            markup_percent: 0.08
14198            generate_matched_pairs: true
14199            generate_eliminations: true
14200        "#;
14201
14202        let config: IntercompanyConfig = serde_yaml::from_str(yaml).expect("Failed to parse");
14203        assert!(config.enabled);
14204        assert_eq!(config.ic_transaction_rate, 0.20);
14205        assert!(matches!(
14206            config.transfer_pricing_method,
14207            TransferPricingMethod::CostPlus
14208        ));
14209        assert_eq!(config.markup_percent, 0.08);
14210        assert!(config.generate_eliminations);
14211    }
14212
14213    // ==========================================================================
14214    // Company Config Tests
14215    // ==========================================================================
14216
14217    #[test]
14218    fn test_company_config_defaults() {
14219        let yaml = r#"
14220            code: "TEST"
14221            name: "Test Company"
14222            currency: "USD"
14223            country: "US"
14224            annual_transaction_volume: ten_k
14225        "#;
14226
14227        let config: CompanyConfig = serde_yaml::from_str(yaml).expect("Failed to parse");
14228        assert_eq!(config.fiscal_year_variant, "K4"); // Default
14229        assert_eq!(config.volume_weight, 1.0); // Default
14230    }
14231
14232    // ==========================================================================
14233    // Chart of Accounts Config Tests
14234    // ==========================================================================
14235
14236    #[test]
14237    fn test_coa_config_defaults() {
14238        let yaml = r#"
14239            complexity: medium
14240        "#;
14241
14242        let config: ChartOfAccountsConfig = serde_yaml::from_str(yaml).expect("Failed to parse");
14243        assert!(config.industry_specific); // Default true
14244        assert!(config.custom_accounts.is_none());
14245        assert_eq!(config.min_hierarchy_depth, 2); // Default
14246        assert_eq!(config.max_hierarchy_depth, 5); // Default
14247    }
14248
14249    // ==========================================================================
14250    // Accounting Standards Config Tests
14251    // ==========================================================================
14252
14253    #[test]
14254    fn test_accounting_standards_config_defaults() {
14255        let config = AccountingStandardsConfig::default();
14256        assert!(!config.enabled);
14257        assert!(config.framework.is_none());
14258        assert!(!config.revenue_recognition.enabled);
14259        assert!(!config.leases.enabled);
14260        assert!(!config.fair_value.enabled);
14261        assert!(!config.impairment.enabled);
14262        assert!(!config.generate_differences);
14263    }
14264
14265    #[test]
14266    fn test_accounting_standards_config_yaml() {
14267        let yaml = r#"
14268            enabled: true
14269            framework: ifrs
14270            revenue_recognition:
14271              enabled: true
14272              generate_contracts: true
14273              avg_obligations_per_contract: 2.5
14274              variable_consideration_rate: 0.20
14275              over_time_recognition_rate: 0.35
14276              contract_count: 150
14277            leases:
14278              enabled: true
14279              lease_count: 75
14280              finance_lease_percent: 0.25
14281              avg_lease_term_months: 48
14282            generate_differences: true
14283        "#;
14284
14285        let config: AccountingStandardsConfig =
14286            serde_yaml::from_str(yaml).expect("Failed to parse");
14287        assert!(config.enabled);
14288        assert!(matches!(
14289            config.framework,
14290            Some(AccountingFrameworkConfig::Ifrs)
14291        ));
14292        assert!(config.revenue_recognition.enabled);
14293        assert_eq!(config.revenue_recognition.contract_count, 150);
14294        assert_eq!(config.revenue_recognition.avg_obligations_per_contract, 2.5);
14295        assert!(config.leases.enabled);
14296        assert_eq!(config.leases.lease_count, 75);
14297        assert_eq!(config.leases.finance_lease_percent, 0.25);
14298        assert!(config.generate_differences);
14299    }
14300
14301    #[test]
14302    fn test_accounting_framework_serialization() {
14303        let frameworks = [
14304            AccountingFrameworkConfig::UsGaap,
14305            AccountingFrameworkConfig::Ifrs,
14306            AccountingFrameworkConfig::DualReporting,
14307            AccountingFrameworkConfig::FrenchGaap,
14308            AccountingFrameworkConfig::GermanGaap,
14309        ];
14310
14311        for framework in frameworks {
14312            let json = serde_json::to_string(&framework).expect("Failed to serialize");
14313            let deserialized: AccountingFrameworkConfig =
14314                serde_json::from_str(&json).expect("Failed to deserialize");
14315            assert!(format!("{:?}", framework) == format!("{:?}", deserialized));
14316        }
14317    }
14318
14319    #[test]
14320    fn test_revenue_recognition_config_defaults() {
14321        let config = RevenueRecognitionConfig::default();
14322        assert!(!config.enabled);
14323        assert!(config.generate_contracts);
14324        assert_eq!(config.avg_obligations_per_contract, 2.0);
14325        assert_eq!(config.variable_consideration_rate, 0.15);
14326        assert_eq!(config.over_time_recognition_rate, 0.30);
14327        assert_eq!(config.contract_count, 100);
14328    }
14329
14330    #[test]
14331    fn test_lease_accounting_config_defaults() {
14332        let config = LeaseAccountingConfig::default();
14333        assert!(!config.enabled);
14334        assert_eq!(config.lease_count, 50);
14335        assert_eq!(config.finance_lease_percent, 0.30);
14336        assert_eq!(config.avg_lease_term_months, 60);
14337        assert!(config.generate_amortization);
14338        assert_eq!(config.real_estate_percent, 0.40);
14339    }
14340
14341    #[test]
14342    fn test_fair_value_config_defaults() {
14343        let config = FairValueConfig::default();
14344        assert!(!config.enabled);
14345        assert_eq!(config.measurement_count, 25);
14346        assert_eq!(config.level1_percent, 0.40);
14347        assert_eq!(config.level2_percent, 0.35);
14348        assert_eq!(config.level3_percent, 0.25);
14349        assert!(!config.include_sensitivity_analysis);
14350    }
14351
14352    #[test]
14353    fn test_impairment_config_defaults() {
14354        let config = ImpairmentConfig::default();
14355        assert!(!config.enabled);
14356        assert_eq!(config.test_count, 15);
14357        assert_eq!(config.impairment_rate, 0.10);
14358        assert!(config.generate_projections);
14359        assert!(!config.include_goodwill);
14360    }
14361
14362    // ==========================================================================
14363    // Audit Standards Config Tests
14364    // ==========================================================================
14365
14366    #[test]
14367    fn test_audit_standards_config_defaults() {
14368        let config = AuditStandardsConfig::default();
14369        assert!(!config.enabled);
14370        assert!(!config.isa_compliance.enabled);
14371        assert!(!config.analytical_procedures.enabled);
14372        assert!(!config.confirmations.enabled);
14373        assert!(!config.opinion.enabled);
14374        assert!(!config.generate_audit_trail);
14375        assert!(!config.sox.enabled);
14376        assert!(!config.pcaob.enabled);
14377    }
14378
14379    #[test]
14380    fn test_audit_standards_config_yaml() {
14381        let yaml = r#"
14382            enabled: true
14383            isa_compliance:
14384              enabled: true
14385              compliance_level: comprehensive
14386              generate_isa_mappings: true
14387              include_pcaob: true
14388              framework: dual
14389            analytical_procedures:
14390              enabled: true
14391              procedures_per_account: 5
14392              variance_probability: 0.25
14393            confirmations:
14394              enabled: true
14395              confirmation_count: 75
14396              positive_response_rate: 0.90
14397              exception_rate: 0.08
14398            opinion:
14399              enabled: true
14400              generate_kam: true
14401              average_kam_count: 4
14402            sox:
14403              enabled: true
14404              generate_302_certifications: true
14405              generate_404_assessments: true
14406              material_weakness_rate: 0.03
14407            pcaob:
14408              enabled: true
14409              is_pcaob_audit: true
14410              include_icfr_opinion: true
14411            generate_audit_trail: true
14412        "#;
14413
14414        let config: AuditStandardsConfig = serde_yaml::from_str(yaml).expect("Failed to parse");
14415        assert!(config.enabled);
14416        assert!(config.isa_compliance.enabled);
14417        assert_eq!(config.isa_compliance.compliance_level, "comprehensive");
14418        assert!(config.isa_compliance.include_pcaob);
14419        assert_eq!(config.isa_compliance.framework, "dual");
14420        assert!(config.analytical_procedures.enabled);
14421        assert_eq!(config.analytical_procedures.procedures_per_account, 5);
14422        assert!(config.confirmations.enabled);
14423        assert_eq!(config.confirmations.confirmation_count, 75);
14424        assert!(config.opinion.enabled);
14425        assert_eq!(config.opinion.average_kam_count, 4);
14426        assert!(config.sox.enabled);
14427        assert!(config.sox.generate_302_certifications);
14428        assert_eq!(config.sox.material_weakness_rate, 0.03);
14429        assert!(config.pcaob.enabled);
14430        assert!(config.pcaob.is_pcaob_audit);
14431        assert!(config.pcaob.include_icfr_opinion);
14432        assert!(config.generate_audit_trail);
14433    }
14434
14435    #[test]
14436    fn test_isa_compliance_config_defaults() {
14437        let config = IsaComplianceConfig::default();
14438        assert!(!config.enabled);
14439        assert_eq!(config.compliance_level, "standard");
14440        assert!(config.generate_isa_mappings);
14441        assert!(config.generate_coverage_summary);
14442        assert!(!config.include_pcaob);
14443        assert_eq!(config.framework, "isa");
14444    }
14445
14446    #[test]
14447    fn test_sox_compliance_config_defaults() {
14448        let config = SoxComplianceConfig::default();
14449        assert!(!config.enabled);
14450        assert!(config.generate_302_certifications);
14451        assert!(config.generate_404_assessments);
14452        assert_eq!(config.materiality_threshold, 10000.0);
14453        assert_eq!(config.material_weakness_rate, 0.02);
14454        assert_eq!(config.significant_deficiency_rate, 0.08);
14455    }
14456
14457    #[test]
14458    fn test_pcaob_config_defaults() {
14459        let config = PcaobConfig::default();
14460        assert!(!config.enabled);
14461        assert!(!config.is_pcaob_audit);
14462        assert!(config.generate_cam);
14463        assert!(!config.include_icfr_opinion);
14464        assert!(!config.generate_standard_mappings);
14465    }
14466
14467    #[test]
14468    fn test_config_with_standards_enabled() {
14469        let yaml = r#"
14470            global:
14471              industry: financial_services
14472              start_date: "2024-01-01"
14473              period_months: 12
14474            companies:
14475              - code: "BANK"
14476                name: "Test Bank"
14477                currency: "USD"
14478                country: "US"
14479                annual_transaction_volume: hundred_k
14480            chart_of_accounts:
14481              complexity: large
14482            output:
14483              output_directory: "./output"
14484            accounting_standards:
14485              enabled: true
14486              framework: us_gaap
14487              revenue_recognition:
14488                enabled: true
14489              leases:
14490                enabled: true
14491            audit_standards:
14492              enabled: true
14493              isa_compliance:
14494                enabled: true
14495              sox:
14496                enabled: true
14497        "#;
14498
14499        let config: GeneratorConfig = serde_yaml::from_str(yaml).expect("Failed to parse");
14500        assert!(config.accounting_standards.enabled);
14501        assert!(matches!(
14502            config.accounting_standards.framework,
14503            Some(AccountingFrameworkConfig::UsGaap)
14504        ));
14505        assert!(config.accounting_standards.revenue_recognition.enabled);
14506        assert!(config.accounting_standards.leases.enabled);
14507        assert!(config.audit_standards.enabled);
14508        assert!(config.audit_standards.isa_compliance.enabled);
14509        assert!(config.audit_standards.sox.enabled);
14510    }
14511
14512    // ==========================================================================
14513    // Industry-Specific Config Tests
14514    // ==========================================================================
14515
14516    #[test]
14517    fn test_industry_specific_config_defaults() {
14518        let config = IndustrySpecificConfig::default();
14519        assert!(!config.enabled);
14520        assert!(!config.manufacturing.enabled);
14521        assert!(!config.retail.enabled);
14522        assert!(!config.healthcare.enabled);
14523        assert!(!config.technology.enabled);
14524        assert!(!config.financial_services.enabled);
14525        assert!(!config.professional_services.enabled);
14526    }
14527
14528    #[test]
14529    fn test_manufacturing_config_defaults() {
14530        let config = ManufacturingConfig::default();
14531        assert!(!config.enabled);
14532        assert_eq!(config.bom_depth, 4);
14533        assert!(!config.just_in_time);
14534        assert_eq!(config.supplier_tiers, 2);
14535        assert_eq!(config.target_yield_rate, 0.97);
14536        assert_eq!(config.scrap_alert_threshold, 0.03);
14537    }
14538
14539    #[test]
14540    fn test_retail_config_defaults() {
14541        let config = RetailConfig::default();
14542        assert!(!config.enabled);
14543        assert_eq!(config.avg_daily_transactions, 500);
14544        assert!(config.loss_prevention);
14545        assert_eq!(config.shrinkage_rate, 0.015);
14546    }
14547
14548    #[test]
14549    fn test_healthcare_config_defaults() {
14550        let config = HealthcareConfig::default();
14551        assert!(!config.enabled);
14552        assert_eq!(config.facility_type, "hospital");
14553        assert_eq!(config.avg_daily_encounters, 150);
14554        assert!(config.compliance.hipaa);
14555        assert!(config.compliance.stark_law);
14556        assert!(config.coding_systems.icd10);
14557        assert!(config.coding_systems.cpt);
14558    }
14559
14560    #[test]
14561    fn test_technology_config_defaults() {
14562        let config = TechnologyConfig::default();
14563        assert!(!config.enabled);
14564        assert_eq!(config.revenue_model, "saas");
14565        assert_eq!(config.subscription_revenue_pct, 0.60);
14566        assert!(config.rd_capitalization.enabled);
14567    }
14568
14569    #[test]
14570    fn test_config_with_industry_specific() {
14571        let yaml = r#"
14572            global:
14573              industry: healthcare
14574              start_date: "2024-01-01"
14575              period_months: 12
14576            companies:
14577              - code: "HOSP"
14578                name: "Test Hospital"
14579                currency: "USD"
14580                country: "US"
14581                annual_transaction_volume: hundred_k
14582            chart_of_accounts:
14583              complexity: medium
14584            output:
14585              output_directory: "./output"
14586            industry_specific:
14587              enabled: true
14588              healthcare:
14589                enabled: true
14590                facility_type: hospital
14591                payer_mix:
14592                  medicare: 0.45
14593                  medicaid: 0.15
14594                  commercial: 0.35
14595                  self_pay: 0.05
14596                coding_systems:
14597                  icd10: true
14598                  cpt: true
14599                  drg: true
14600                compliance:
14601                  hipaa: true
14602                  stark_law: true
14603                anomaly_rates:
14604                  upcoding: 0.03
14605                  unbundling: 0.02
14606        "#;
14607
14608        let config: GeneratorConfig = serde_yaml::from_str(yaml).expect("Failed to parse");
14609        assert!(config.industry_specific.enabled);
14610        assert!(config.industry_specific.healthcare.enabled);
14611        assert_eq!(
14612            config.industry_specific.healthcare.facility_type,
14613            "hospital"
14614        );
14615        assert_eq!(config.industry_specific.healthcare.payer_mix.medicare, 0.45);
14616        assert_eq!(config.industry_specific.healthcare.payer_mix.self_pay, 0.05);
14617        assert!(config.industry_specific.healthcare.coding_systems.icd10);
14618        assert!(config.industry_specific.healthcare.compliance.hipaa);
14619        assert_eq!(
14620            config.industry_specific.healthcare.anomaly_rates.upcoding,
14621            0.03
14622        );
14623    }
14624
14625    #[test]
14626    fn test_config_with_manufacturing_specific() {
14627        let yaml = r#"
14628            global:
14629              industry: manufacturing
14630              start_date: "2024-01-01"
14631              period_months: 12
14632            companies:
14633              - code: "MFG"
14634                name: "Test Manufacturing"
14635                currency: "USD"
14636                country: "US"
14637                annual_transaction_volume: hundred_k
14638            chart_of_accounts:
14639              complexity: medium
14640            output:
14641              output_directory: "./output"
14642            industry_specific:
14643              enabled: true
14644              manufacturing:
14645                enabled: true
14646                bom_depth: 5
14647                just_in_time: true
14648                supplier_tiers: 3
14649                target_yield_rate: 0.98
14650                anomaly_rates:
14651                  yield_manipulation: 0.02
14652                  phantom_production: 0.01
14653        "#;
14654
14655        let config: GeneratorConfig = serde_yaml::from_str(yaml).expect("Failed to parse");
14656        assert!(config.industry_specific.enabled);
14657        assert!(config.industry_specific.manufacturing.enabled);
14658        assert_eq!(config.industry_specific.manufacturing.bom_depth, 5);
14659        assert!(config.industry_specific.manufacturing.just_in_time);
14660        assert_eq!(config.industry_specific.manufacturing.supplier_tiers, 3);
14661        assert_eq!(
14662            config.industry_specific.manufacturing.target_yield_rate,
14663            0.98
14664        );
14665        assert_eq!(
14666            config
14667                .industry_specific
14668                .manufacturing
14669                .anomaly_rates
14670                .yield_manipulation,
14671            0.02
14672        );
14673    }
14674
14675    // ==========================================================================
14676    // Tax Configuration Tests
14677    // ==========================================================================
14678
14679    #[test]
14680    fn test_tax_config_defaults() {
14681        let tax = TaxConfig::default();
14682        assert!(!tax.enabled);
14683        assert!(tax.jurisdictions.countries.is_empty());
14684        assert!(!tax.jurisdictions.include_subnational);
14685        assert!(!tax.vat_gst.enabled);
14686        assert!(tax.vat_gst.standard_rates.is_empty());
14687        assert!(tax.vat_gst.reduced_rates.is_empty());
14688        assert!(tax.vat_gst.exempt_categories.is_empty());
14689        assert!(tax.vat_gst.reverse_charge);
14690        assert!(!tax.sales_tax.enabled);
14691        assert!(tax.sales_tax.nexus_states.is_empty());
14692        assert!(!tax.withholding.enabled);
14693        assert!(tax.withholding.treaty_network);
14694        assert_eq!(tax.withholding.default_rate, 0.30);
14695        assert_eq!(tax.withholding.treaty_reduced_rate, 0.15);
14696        assert!(tax.provisions.enabled);
14697        assert_eq!(tax.provisions.statutory_rate, 0.21);
14698        assert!(tax.provisions.uncertain_positions);
14699        assert!(!tax.payroll_tax.enabled);
14700        assert_eq!(tax.anomaly_rate, 0.03);
14701    }
14702
14703    #[test]
14704    fn test_tax_config_from_yaml() {
14705        let yaml = r#"
14706            global:
14707              seed: 42
14708              start_date: "2024-01-01"
14709              period_months: 12
14710              industry: retail
14711            companies:
14712              - code: C001
14713                name: Test Corp
14714                currency: USD
14715                country: US
14716                annual_transaction_volume: ten_k
14717            chart_of_accounts:
14718              complexity: small
14719            output:
14720              output_directory: ./output
14721            tax:
14722              enabled: true
14723              anomaly_rate: 0.05
14724              jurisdictions:
14725                countries: ["US", "DE", "GB"]
14726                include_subnational: true
14727              vat_gst:
14728                enabled: true
14729                standard_rates:
14730                  DE: 0.19
14731                  GB: 0.20
14732                reduced_rates:
14733                  DE: 0.07
14734                  GB: 0.05
14735                exempt_categories:
14736                  - financial_services
14737                  - healthcare
14738                reverse_charge: false
14739              sales_tax:
14740                enabled: true
14741                nexus_states: ["CA", "NY", "TX"]
14742              withholding:
14743                enabled: true
14744                treaty_network: false
14745                default_rate: 0.25
14746                treaty_reduced_rate: 0.10
14747              provisions:
14748                enabled: false
14749                statutory_rate: 0.28
14750                uncertain_positions: false
14751              payroll_tax:
14752                enabled: true
14753        "#;
14754
14755        let config: GeneratorConfig = serde_yaml::from_str(yaml).expect("Failed to parse");
14756        assert!(config.tax.enabled);
14757        assert_eq!(config.tax.anomaly_rate, 0.05);
14758
14759        // Jurisdictions
14760        assert_eq!(config.tax.jurisdictions.countries.len(), 3);
14761        assert!(config
14762            .tax
14763            .jurisdictions
14764            .countries
14765            .contains(&"DE".to_string()));
14766        assert!(config.tax.jurisdictions.include_subnational);
14767
14768        // VAT/GST
14769        assert!(config.tax.vat_gst.enabled);
14770        assert_eq!(config.tax.vat_gst.standard_rates.get("DE"), Some(&0.19));
14771        assert_eq!(config.tax.vat_gst.standard_rates.get("GB"), Some(&0.20));
14772        assert_eq!(config.tax.vat_gst.reduced_rates.get("DE"), Some(&0.07));
14773        assert_eq!(config.tax.vat_gst.exempt_categories.len(), 2);
14774        assert!(!config.tax.vat_gst.reverse_charge);
14775
14776        // Sales tax
14777        assert!(config.tax.sales_tax.enabled);
14778        assert_eq!(config.tax.sales_tax.nexus_states.len(), 3);
14779        assert!(config
14780            .tax
14781            .sales_tax
14782            .nexus_states
14783            .contains(&"CA".to_string()));
14784
14785        // Withholding
14786        assert!(config.tax.withholding.enabled);
14787        assert!(!config.tax.withholding.treaty_network);
14788        assert_eq!(config.tax.withholding.default_rate, 0.25);
14789        assert_eq!(config.tax.withholding.treaty_reduced_rate, 0.10);
14790
14791        // Provisions
14792        assert!(!config.tax.provisions.enabled);
14793        assert_eq!(config.tax.provisions.statutory_rate, 0.28);
14794        assert!(!config.tax.provisions.uncertain_positions);
14795
14796        // Payroll tax
14797        assert!(config.tax.payroll_tax.enabled);
14798    }
14799
14800    #[test]
14801    fn test_generator_config_with_tax_default() {
14802        let yaml = r#"
14803            global:
14804              seed: 42
14805              start_date: "2024-01-01"
14806              period_months: 12
14807              industry: retail
14808            companies:
14809              - code: C001
14810                name: Test Corp
14811                currency: USD
14812                country: US
14813                annual_transaction_volume: ten_k
14814            chart_of_accounts:
14815              complexity: small
14816            output:
14817              output_directory: ./output
14818        "#;
14819
14820        let config: GeneratorConfig =
14821            serde_yaml::from_str(yaml).expect("Failed to parse config without tax section");
14822        // Tax should be present with defaults when not specified in YAML
14823        assert!(!config.tax.enabled);
14824        assert!(config.tax.jurisdictions.countries.is_empty());
14825        assert_eq!(config.tax.anomaly_rate, 0.03);
14826        assert!(config.tax.provisions.enabled); // provisions default to enabled=true
14827        assert_eq!(config.tax.provisions.statutory_rate, 0.21);
14828    }
14829
14830    // ==========================================================================
14831    // SessionSchemaConfig Tests
14832    // ==========================================================================
14833
14834    #[test]
14835    fn test_session_config_default_disabled() {
14836        let yaml = "{}";
14837        let config: SessionSchemaConfig =
14838            serde_yaml::from_str(yaml).expect("Failed to parse empty session config");
14839        assert!(!config.enabled);
14840        assert!(config.checkpoint_path.is_none());
14841        assert!(config.per_period_output);
14842        assert!(config.consolidated_output);
14843    }
14844
14845    #[test]
14846    fn test_config_backward_compatible_without_session() {
14847        let yaml = r#"
14848            global:
14849              seed: 42
14850              start_date: "2024-01-01"
14851              period_months: 12
14852              industry: retail
14853            companies:
14854              - code: C001
14855                name: Test Corp
14856                currency: USD
14857                country: US
14858                annual_transaction_volume: ten_k
14859            chart_of_accounts:
14860              complexity: small
14861            output:
14862              output_directory: ./output
14863        "#;
14864
14865        let config: GeneratorConfig =
14866            serde_yaml::from_str(yaml).expect("Failed to parse config without session");
14867        // Session should default to disabled
14868        assert!(!config.session.enabled);
14869        assert!(config.session.per_period_output);
14870        assert!(config.session.consolidated_output);
14871        // fiscal_year_months should be None
14872        assert!(config.global.fiscal_year_months.is_none());
14873    }
14874
14875    #[test]
14876    fn test_fiscal_year_months_parsed() {
14877        let yaml = r#"
14878            global:
14879              seed: 42
14880              start_date: "2024-01-01"
14881              period_months: 24
14882              industry: retail
14883              fiscal_year_months: 12
14884            companies:
14885              - code: C001
14886                name: Test Corp
14887                currency: USD
14888                country: US
14889                annual_transaction_volume: ten_k
14890            chart_of_accounts:
14891              complexity: small
14892            output:
14893              output_directory: ./output
14894            session:
14895              enabled: true
14896              checkpoint_path: /tmp/checkpoints
14897              per_period_output: true
14898              consolidated_output: false
14899        "#;
14900
14901        let config: GeneratorConfig =
14902            serde_yaml::from_str(yaml).expect("Failed to parse config with fiscal_year_months");
14903        assert_eq!(config.global.fiscal_year_months, Some(12));
14904        assert!(config.session.enabled);
14905        assert_eq!(
14906            config.session.checkpoint_path,
14907            Some("/tmp/checkpoints".to_string())
14908        );
14909        assert!(config.session.per_period_output);
14910        assert!(!config.session.consolidated_output);
14911    }
14912}