swift_mt_message/headers/
mod.rs

1//! # SWIFT Message Headers and Trailers
2//!
3//! ## Purpose
4//! Comprehensive header and trailer structures for SWIFT MT messages, implementing the complete
5//! SWIFT FIN block structure including Basic Header (Block 1), Application Header (Block 2),
6//! User Header (Block 3), and Trailer (Block 5).
7//!
8//! ## Block Structure
9//! - **Block 1**: Basic Header - Sender identification and routing information
10//! - **Block 2**: Application Header - Message type and delivery information
11//! - **Block 3**: User Header - Optional user-defined fields and references
12//! - **Block 5**: Trailer - Optional authentication and delivery confirmation
13//!
14//! ## Features
15//! - **Complete SWIFT Compliance**: Follows SWIFT User Handbook specifications
16//! - **Type-Safe Parsing**: Strongly-typed header structures with validation
17//! - **Authentication Support**: MAC and authentication key handling
18//! - **Sample Generation**: Realistic header generation for testing
19//! - **Network Validation**: BIC validation and routing verification
20
21use crate::errors::{ParseError, Result};
22use serde::{Deserialize, Serialize};
23
24/// **Basic Header (Block 1): SWIFT Message Identification and Routing**
25///
26/// ## Purpose
27/// The Basic Header constitutes the first mandatory block of every SWIFT message, providing
28/// essential identification, routing, and sequencing information. This header enables the
29/// SWIFT network to authenticate the sender, route the message appropriately, and maintain
30/// message sequence integrity across the global financial messaging infrastructure.
31///
32/// ## Format Specification
33/// - **Block Format**: `{1:F01SSSSSSSSSCCC0000NNNNNN}`
34/// - **Total Length**: Exactly 25 characters
35/// - **Structure**: Application ID + Service ID + LT Address + Session + Sequence
36/// - **Character Set**: Alphanumeric, uppercase only
37///
38/// ## Business Context Applications
39/// - **Message Authentication**: Sender identification and verification
40/// - **Network Routing**: Logical terminal addressing for message delivery
41/// - **Session Management**: Message sequencing within communication sessions
42/// - **Audit Trail**: Complete message tracking and reconciliation
43///
44/// ## Component Breakdown
45/// ### Application Identifier (1 character)
46/// - **F**: FIN application (Financial messages)
47/// - **A**: GPA application (General Purpose Application)
48/// - **L**: GPA application (for certain message types)
49/// - **Usage**: Determines message processing rules and validation
50///
51/// ### Service Identifier (2 characters)
52/// - **01**: FIN service (standard financial messages)
53/// - **03**: FIN Copy service (third-party copy)
54/// - **05**: GPA service
55/// - **21**: ACK/NAK service
56///
57/// ### Logical Terminal Address (12 characters)
58/// - **BIC Code**: First 8 characters (institution identifier)
59/// - **Terminal Code**: Next 1 character (logical terminal)
60/// - **Branch Code**: Last 3 characters (XXX for head office)
61/// - **Format**: Must be valid SWIFT-connected BIC
62///
63/// ## Network Validation Requirements
64/// - **BIC Validation**: Must be active SWIFT participant
65/// - **Service Compatibility**: Service ID must match message type
66/// - **Session Validity**: Session number must be within valid range
67/// - **Sequence Continuity**: Sequence numbers must be sequential
68///
69/// ## Session and Sequence Management
70/// ### Session Number (4 digits)
71/// - **Range**: 0000-9999
72/// - **Purpose**: Groups related messages within a session
73/// - **Reset**: Can be reset based on bilateral agreement
74/// - **Tracking**: Used for message reconciliation
75///
76/// ### Sequence Number (6 digits)
77/// - **Range**: 000000-999999
78/// - **Increment**: Sequential within session
79/// - **Uniqueness**: Combined with session ensures message uniqueness
80/// - **Recovery**: Critical for message recovery procedures
81///
82/// ## Regional Considerations
83/// - **Time Zones**: LT address determines processing time zone
84/// - **Operating Hours**: Service availability based on regional center
85/// - **Holiday Schedules**: Regional holiday impacts on processing
86/// - **Regulatory Compliance**: Regional reporting requirements
87///
88/// ## Error Prevention Guidelines
89/// - **BIC Accuracy**: Verify sender BIC is authorized for service
90/// - **Sequence Management**: Maintain strict sequence number control
91/// - **Session Coordination**: Coordinate session numbers with correspondent
92/// - **Format Compliance**: Ensure exact 25-character length
93///
94/// ## Security and Authentication
95/// - **Sender Authentication**: BIC must match authenticated connection
96/// - **Message Integrity**: Header contributes to message MAC calculation
97/// - **Non-repudiation**: Sender identification cannot be disputed
98/// - **Audit Trail**: Complete tracking from sender to receiver
99///
100/// ## Integration with Other Blocks
101/// - **Block 2**: Message type and routing continuation
102/// - **Block 3**: Optional user header for enhanced services
103/// - **Block 4**: Message text validated based on Block 1 service
104/// - **Block 5**: Trailer with checksums and authentication
105///
106/// ## Compliance Framework
107/// - **SWIFT Standards**: Full compliance with FIN interface standards
108/// - **Service Level Agreements**: Performance guarantees based on service
109/// - **Regulatory Reporting**: Sender identification for compliance
110/// - **Audit Requirements**: Complete message trail maintenance
111///
112/// ## Best Practices
113/// - **BIC Management**: Keep BIC directory updated
114/// - **Sequence Control**: Implement robust sequence management
115/// - **Session Planning**: Plan session number allocation
116/// - **Error Recovery**: Implement sequence gap detection
117///
118/// ## See Also
119/// - SWIFT FIN Interface Standards: Block 1 Specifications
120/// - BIC Directory: Valid SWIFT Participant Codes
121/// - Session Management Guide: Best Practices
122/// - Message Sequencing: Control and Recovery Procedures
123#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
124pub struct BasicHeader {
125    /// Application identifier
126    ///
127    /// Format: 1!a - Single alphabetic character
128    /// Values: F (FIN), A (GPA), L (GPA legacy)
129    /// Determines message processing rules and validation requirements
130    pub application_id: String,
131
132    /// Service identifier
133    ///
134    /// Format: 2!n - Two numeric characters
135    /// Common values: 01 (FIN), 03 (FIN Copy), 05 (GPA), 21 (ACK/NAK)
136    /// Specifies the SWIFT service type for message handling
137    pub service_id: String,
138
139    /// Logical Terminal (LT) address
140    ///
141    /// Format: 12!c - 12 alphanumeric characters
142    /// Structure: 8-char BIC + 1-char terminal + 3-char branch
143    /// Uniquely identifies the sending terminal in SWIFT network
144    pub logical_terminal: String,
145
146    /// Sender BIC extracted from logical terminal
147    ///
148    /// Format: 8!c - First 8 characters of logical terminal
149    /// The sending institution's Bank Identifier Code
150    pub sender_bic: String,
151
152    /// Session number
153    ///
154    /// Format: 4!n - Four numeric digits (0000-9999)
155    /// Groups related messages within a communication session
156    /// Used for message reconciliation and recovery procedures
157    pub session_number: String,
158
159    /// Sequence number
160    ///
161    /// Format: 6!n - Six numeric digits (000000-999999)
162    /// Sequential message counter within session
163    /// Critical for message ordering and duplicate detection
164    pub sequence_number: String,
165}
166
167impl BasicHeader {
168    /// Parse basic header from block 1 string
169    pub fn parse(block1: &str) -> Result<Self> {
170        if block1.len() < 21 {
171            return Err(ParseError::InvalidBlockStructure {
172                block: "1".to_string(),
173                message: format!(
174                    "Block 1 too short: expected at least 21 characters, got {}",
175                    block1.len()
176                ),
177            });
178        }
179
180        let application_id = block1[0..1].to_string();
181        let service_id = block1[1..3].to_string();
182        let logical_terminal = block1[3..15].to_string();
183        let session_number = block1[15..19].to_string();
184        let sequence_number = block1[19..].to_string();
185        let sender_bic = logical_terminal[0..8].to_string();
186
187        Ok(BasicHeader {
188            application_id,
189            service_id,
190            logical_terminal,
191            sender_bic,
192            session_number,
193            sequence_number,
194        })
195    }
196}
197
198impl std::fmt::Display for BasicHeader {
199    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
200        write!(
201            f,
202            "{}{}{}{}{}",
203            self.application_id,
204            self.service_id,
205            self.logical_terminal,
206            self.session_number,
207            self.sequence_number
208        )
209    }
210}
211
212/// **Input Application Header: Message Sent to SWIFT Network**
213///
214/// ## Purpose
215/// Represents messages being sent to the SWIFT network for processing and delivery.
216/// Contains routing information, priority settings, and delivery monitoring options.
217///
218/// ## Format Specification
219/// - **Format**: `{2:I103DDDDDDDDDDDDP[M][OOO]}`
220/// - **Length**: 17-21 characters
221/// - **Components**: Direction (I) + Type + Destination + Priority + Optional monitoring/obsolescence
222#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
223pub struct InputApplicationHeader {
224    /// Message type
225    ///
226    /// Format: 3!n - Three numeric digits
227    /// Examples: 103 (Customer Transfer), 202 (Bank Transfer), 940 (Statement)
228    pub message_type: String,
229
230    /// Destination address
231    ///
232    /// Format: 12!c - 12 alphanumeric characters
233    /// Structure: 8-char BIC + 1-char terminal + 3-char branch
234    pub destination_address: String,
235
236    /// Receiver BIC extracted from destination address
237    ///
238    /// Format: 8!c - First 8 characters of destination
239    pub receiver_bic: String,
240
241    /// Message priority
242    ///
243    /// Format: 1!a - Single alphabetic character
244    /// Values: U (Urgent), N (Normal), S (System)
245    pub priority: String,
246
247    /// Delivery monitoring option
248    ///
249    /// Format: 1!n - Single numeric digit
250    /// Values: 1 (Non-delivery warning), 2 (Delivery notification), 3 (Both)
251    #[serde(skip_serializing_if = "Option::is_none")]
252    pub delivery_monitoring: Option<String>,
253
254    /// Obsolescence period
255    ///
256    /// Format: 3!n - Three numeric digits
257    /// Range: 003-999 (units of 5 minutes)
258    #[serde(skip_serializing_if = "Option::is_none")]
259    pub obsolescence_period: Option<String>,
260}
261
262/// **Output Application Header: Message Delivered from SWIFT Network**
263///
264/// ## Purpose
265/// Represents messages delivered from the SWIFT network to the receiver.
266/// Contains complete Message Input Reference (MIR), timing information, and delivery confirmation.
267///
268/// ## Format Specification
269/// - **Format**: `{2:O103HHMMYYYYMMDDDDDDDDDDDDDDNNNNSSSSSSYYYYMMDDHHMMP}`
270/// - **Length**: 46-47 characters
271/// - **Components**: Direction (O) + Type + Input time + MIR + Output date/time + Priority
272#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
273pub struct OutputApplicationHeader {
274    /// Message type
275    ///
276    /// Format: 3!n - Three numeric digits
277    /// Examples: 103 (Customer Transfer), 202 (Bank Transfer), 940 (Statement)
278    pub message_type: String,
279
280    /// Input time
281    ///
282    /// Format: HHMM - The hour and minute when the sender sent the message
283    pub input_time: String,
284
285    /// MIR (Message Input Reference)
286    ///
287    /// Complete MIR structure including date, LT address, session, and sequence
288    pub mir: MessageInputReference,
289
290    /// Output date
291    ///
292    /// Format: YYMMDD - The output date local to the receiver
293    pub output_date: String,
294
295    /// Output time
296    ///
297    /// Format: HHMM - The output time local to the receiver
298    pub output_time: String,
299
300    /// Message priority
301    ///
302    /// Format: 1!a - Single alphabetic character
303    /// Values: U (Urgent), N (Normal), S (System)
304    #[serde(skip_serializing_if = "Option::is_none")]
305    pub priority: Option<String>,
306}
307
308/// **Application Header (Block 2): Message Type and Routing Information**
309///
310/// ## Purpose
311/// The Application Header provides essential message type identification and routing
312/// information, enabling the SWIFT network to properly categorize, prioritize, and
313/// deliver messages. This header determines processing rules, delivery monitoring,
314/// and time-critical handling requirements for financial messages.
315///
316/// ## Format Specification
317/// - **Input Format**: `{2:I103DDDDDDDDDDDDP[M][OOO]}`
318/// - **Output Format**: `{2:O103HHMMYYYYMMDDDDDDDDDDDDDDNNNNSSSSSSYYYYMMDDHHMMP}`
319/// - **Direction Dependent**: Structure varies for input (I) vs output (O)
320/// - **Variable Length**: 17-21 characters for input, 46-47 for output
321///
322/// ## Business Context Applications
323/// - **Message Classification**: Determines processing rules by message type
324/// - **Priority Handling**: Urgent vs normal message processing
325/// - **Delivery Assurance**: Monitoring and non-delivery notifications
326/// - **Time Management**: Obsolescence period for time-sensitive messages
327///
328/// ## Direction Indicator
329/// ### Input Messages (I)
330/// - **Sender Perspective**: Messages being sent to SWIFT
331/// - **Validation**: Full message validation applied
332/// - **Routing**: To destination specified in header
333/// - **Storage**: Stored in sender's message archive
334///
335/// ### Output Messages (O)
336/// - **Receiver Perspective**: Messages delivered from SWIFT
337/// - **Delivery**: Includes delivery timestamp and MIR
338/// - **Status**: Confirmed successful delivery
339/// - **Archive**: Stored in receiver's message archive
340///
341/// ## Message Type Classification
342/// ### Category 1: Customer Payments (MT 1nn)
343/// - **MT 103**: Single Customer Credit Transfer
344/// - **MT 110**: Advice of Cheque
345/// - **Priority**: Often urgent for same-day value
346///
347/// ### Category 2: Bank Transfers (MT 2nn)
348/// - **MT 202**: General Financial Institution Transfer
349/// - **MT 202COV**: Cover Payment
350/// - **Priority**: High priority for bank liquidity
351///
352/// ### Category 9: Balance and Status (MT 9nn)
353/// - **MT 940**: Customer Statement
354/// - **MT 950**: Statement Message
355/// - **Priority**: Normal, end-of-day processing
356///
357/// ## Priority Management
358/// ### Urgent Priority (U)
359/// - **Processing**: Immediate, ahead of normal messages
360/// - **Use Cases**: Time-critical payments, cut-off deadlines
361/// - **Delivery**: Fastest available route
362/// - **Cost**: Premium pricing may apply
363///
364/// ### Normal Priority (N)
365/// - **Processing**: Standard queue processing
366/// - **Use Cases**: Regular payments and messages
367/// - **Delivery**: Standard delivery timeframes
368/// - **Cost**: Standard message pricing
369///
370/// ### System Priority (S)
371/// - **Processing**: System-generated messages
372/// - **Use Cases**: ACKs, NAKs, system notifications
373/// - **Delivery**: Highest priority delivery
374/// - **Access**: Reserved for SWIFT system use
375///
376/// ## Delivery Monitoring Options
377/// ### Non-Delivery Warning (1)
378/// - **Timeout**: Warning if not delivered within set time
379/// - **Action**: Sender notified of delivery delay
380/// - **Use Case**: Important but not critical messages
381///
382/// ### Delivery Notification (3)
383/// - **Confirmation**: Positive delivery confirmation required
384/// - **Notification**: MT 011 sent upon successful delivery
385/// - **Use Case**: Critical messages requiring confirmation
386///
387/// ### No Monitoring (blank)
388/// - **Standard**: Default delivery without monitoring
389/// - **Notification**: No delivery status updates
390/// - **Use Case**: Routine, non-critical messages
391///
392/// ## Obsolescence Period
393/// - **Format**: 3 numeric digits (003-999)
394/// - **Unit**: 5-minute intervals
395/// - **Maximum**: 999 = 83 hours
396/// - **Purpose**: Message validity timeout
397/// - **Action**: Automatic cancellation if not delivered
398///
399/// ## Network Validation Requirements
400/// - **BIC Validation**: Destination must be valid SWIFT participant
401/// - **Message Type**: Must be valid for sender's subscription
402/// - **Priority Rules**: Certain messages restricted to normal priority
403/// - **Monitoring Compatibility**: Not all messages support monitoring
404///
405/// ## Regional Considerations
406/// - **Cut-off Times**: Regional deadlines for urgent messages
407/// - **Processing Windows**: Regional operating hours impact
408/// - **Holiday Handling**: Regional holidays affect delivery
409/// - **Regulatory Priority**: Some regions mandate priority levels
410///
411/// ## Error Prevention Guidelines
412/// - **BIC Verification**: Confirm destination BIC is reachable
413/// - **Type Validation**: Ensure message type is authorized
414/// - **Priority Selection**: Use appropriate priority level
415/// - **Monitoring Choice**: Select monitoring based on criticality
416///
417/// ## Integration with Other Blocks
418/// - **Block 1**: Sender identification coordination
419/// - **Block 3**: Service options based on message type
420/// - **Block 4**: Content validation per message type
421/// - **Block 5**: Delivery confirmations and status
422///
423/// ## Compliance Framework
424/// - **Message Standards**: Type-specific validation rules
425/// - **Priority Policies**: Fair use of urgent priority
426/// - **Delivery SLAs**: Service level compliance
427/// - **Audit Trail**: Complete routing documentation
428///
429/// ## Processing Impact
430/// - **Queue Position**: Priority determines processing order
431/// - **Validation Depth**: Message type determines checks
432/// - **Routing Path**: Optimal path based on priority
433/// - **Cost Calculation**: Priority affects message pricing
434///
435/// ## Best Practices
436/// - **Priority Discipline**: Reserve urgent for true urgency
437/// - **Monitoring Selection**: Match monitoring to risk level
438/// - **Type Accuracy**: Ensure correct message type selection
439/// - **Destination Validation**: Verify BIC before sending
440///
441/// ## See Also
442/// - SWIFT FIN User Handbook: Block 2 Specifications
443/// - Message Type Catalog: Complete MT Message List
444/// - Priority Guidelines: Best Practices for Priority Selection
445/// - Delivery Monitoring: Service Options and Usage
446#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
447#[serde(tag = "direction")]
448pub enum ApplicationHeader {
449    /// Input message sent to SWIFT network
450    #[serde(rename = "I")]
451    Input(InputApplicationHeader),
452
453    /// Output message delivered from SWIFT network
454    #[serde(rename = "O")]
455    Output(OutputApplicationHeader),
456}
457
458impl ApplicationHeader {
459    /// Parse application header from block 2 string
460    pub fn parse(block2: &str) -> Result<Self> {
461        if block2.len() < 4 {
462            return Err(ParseError::InvalidBlockStructure {
463                block: "2".to_string(),
464                message: format!(
465                    "Block 2 too short: expected at least 4 characters, got {}",
466                    block2.len()
467                ),
468            });
469        }
470
471        let direction = &block2[0..1];
472        let message_type = block2[1..4].to_string();
473
474        match direction {
475            "I" => {
476                // Input format: {2:I103DDDDDDDDDDDDP[M][OOO]}
477                // I + 103 + 12-char destination + priority + optional monitoring + optional obsolescence
478                if block2.len() < 17 {
479                    return Err(ParseError::InvalidBlockStructure {
480                        block: "2".to_string(),
481                        message: format!(
482                            "Input Block 2 too short: expected at least 17 characters, got {}",
483                            block2.len()
484                        ),
485                    });
486                }
487
488                let destination_address = block2[4..16].to_string();
489                let priority = block2[16..17].to_string();
490                let receiver_bic = destination_address[0..8].to_string();
491
492                let delivery_monitoring = if block2.len() >= 18 {
493                    Some(block2[17..18].to_string())
494                } else {
495                    None
496                };
497
498                let obsolescence_period = if block2.len() >= 21 {
499                    Some(block2[18..21].to_string())
500                } else {
501                    None
502                };
503
504                Ok(ApplicationHeader::Input(InputApplicationHeader {
505                    message_type,
506                    destination_address,
507                    receiver_bic,
508                    priority,
509                    delivery_monitoring,
510                    obsolescence_period,
511                }))
512            }
513            "O" => {
514                // Output format: {2:O103HHMMYYYYMMDDDDDDDDDDDDDDNNNNSSSSSSYYYYMMDDHHMMP}
515                // Components:
516                // O (1) + message_type (3) + input_time (4) + mir (28) + output_date (6) + output_time (4) + priority (1)
517                // MIR consists of: date (6) + lt_address (12) + session (4) + sequence (6) = 28 chars
518                // Total: 1 + 3 + 4 + 28 + 6 + 4 = 46 characters minimum (priority optional)
519
520                if block2.len() < 46 {
521                    return Err(ParseError::InvalidBlockStructure {
522                        block: "2".to_string(),
523                        message: format!(
524                            "Output Block 2 too short: expected at least 46 characters, got {}",
525                            block2.len()
526                        ),
527                    });
528                }
529
530                // Parse Output format components according to SWIFT specification:
531                let input_time = block2[4..8].to_string(); // HHMM
532
533                // MIR (Message Input Reference) components
534                let mir_date = block2[8..14].to_string(); // YYMMDD
535                let mir_lt_address = block2[14..26].to_string(); // 12 characters (BIC8 + LT + Branch)
536                let mir_session = block2[26..30].to_string(); // 4 digits
537                let mir_sequence = block2[30..36].to_string(); // 6 digits
538
539                let output_date = block2[36..42].to_string(); // YYMMDD
540                let output_time = block2[42..46].to_string(); // HHMM
541
542                let priority = if block2.len() >= 47 {
543                    Some(block2[46..47].to_string())
544                } else {
545                    None
546                };
547
548                // Create MIR structure
549                let mir = MessageInputReference {
550                    date: mir_date,
551                    lt_identifier: mir_lt_address.clone(),
552                    branch_code: if mir_lt_address.len() >= 12 {
553                        mir_lt_address[9..12].to_string()
554                    } else {
555                        "XXX".to_string()
556                    },
557                    session_number: mir_session,
558                    sequence_number: mir_sequence,
559                };
560
561                Ok(ApplicationHeader::Output(OutputApplicationHeader {
562                    message_type,
563                    input_time,
564                    mir,
565                    output_date,
566                    output_time,
567                    priority,
568                }))
569            }
570            _ => Err(ParseError::InvalidBlockStructure {
571                block: "2".to_string(),
572                message: format!(
573                    "Invalid direction indicator: expected 'I' or 'O', got '{}'",
574                    direction
575                ),
576            }),
577        }
578    }
579
580    /// Get the message type regardless of direction
581    pub fn message_type(&self) -> &str {
582        match self {
583            ApplicationHeader::Input(header) => &header.message_type,
584            ApplicationHeader::Output(header) => &header.message_type,
585        }
586    }
587
588    /// Get the priority if available
589    pub fn priority(&self) -> Option<&str> {
590        match self {
591            ApplicationHeader::Input(header) => Some(&header.priority),
592            ApplicationHeader::Output(header) => header.priority.as_deref(),
593        }
594    }
595}
596
597impl std::fmt::Display for ApplicationHeader {
598    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
599        match self {
600            ApplicationHeader::Input(header) => {
601                // Input format: I + message_type + destination_address + priority [+ monitoring] [+ obsolescence]
602                let mut result = format!(
603                    "I{}{}{}",
604                    header.message_type, header.destination_address, header.priority
605                );
606
607                if let Some(ref delivery_monitoring) = header.delivery_monitoring {
608                    result.push_str(delivery_monitoring);
609                }
610
611                if let Some(ref obsolescence_period) = header.obsolescence_period {
612                    result.push_str(obsolescence_period);
613                }
614
615                write!(f, "{result}")
616            }
617            ApplicationHeader::Output(header) => {
618                // Output format: O + message_type + input_time + MIR + output_date + output_time + priority
619                // MIR = date + lt_address + session + sequence
620                let mut result = format!(
621                    "O{}{}{}{}{}{}{}{}",
622                    header.message_type,
623                    header.input_time,
624                    header.mir.date,
625                    header.mir.lt_identifier,
626                    header.mir.session_number,
627                    header.mir.sequence_number,
628                    header.output_date,
629                    header.output_time,
630                );
631
632                if let Some(ref priority) = header.priority {
633                    result.push_str(priority);
634                }
635
636                write!(f, "{result}")
637            }
638        }
639    }
640}
641
642impl std::fmt::Display for InputApplicationHeader {
643    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
644        let mut result = format!(
645            "I{}{}{}",
646            self.message_type, self.destination_address, self.priority
647        );
648
649        if let Some(ref delivery_monitoring) = self.delivery_monitoring {
650            result.push_str(delivery_monitoring);
651        }
652
653        if let Some(ref obsolescence_period) = self.obsolescence_period {
654            result.push_str(obsolescence_period);
655        }
656
657        write!(f, "{result}")
658    }
659}
660
661impl std::fmt::Display for OutputApplicationHeader {
662    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
663        let mut result = format!(
664            "O{}{}{}{}{}{}{}{}",
665            self.message_type,
666            self.input_time,
667            self.mir.date,
668            self.mir.lt_identifier,
669            self.mir.session_number,
670            self.mir.sequence_number,
671            self.output_date,
672            self.output_time,
673        );
674
675        if let Some(ref priority) = self.priority {
676            result.push_str(priority);
677        }
678
679        write!(f, "{result}")
680    }
681}
682
683/// **User Header (Block 3): Extended Service Options and Controls**
684///
685/// ## Purpose
686/// The User Header provides optional extended functionality for SWIFT messages,
687/// enabling advanced services, enhanced straight-through processing (STP),
688/// compliance controls, and end-to-end transaction tracking. This header has
689/// become increasingly important for regulatory compliance, particularly with
690/// SWIFT gpi tracking and enhanced payment transparency requirements.
691///
692/// ## Format Specification
693/// - **Block Format**: `{3:{tag:value}{tag:value}...}`
694/// - **Tag Format**: Numeric tags with structured values
695/// - **Nesting**: Tags enclosed in curly braces
696/// - **Optional Block**: Entire block 3 may be omitted
697///
698/// ## Business Context Applications
699/// - **SWIFT gpi Tracking**: End-to-end payment tracking via UETR
700/// - **STP Enhancement**: Validation flags for automated processing
701/// - **Regulatory Compliance**: Sanctions screening and payment controls
702/// - **Service Enhancement**: Additional service options and features
703///
704/// ## Critical Tags for Modern Payments
705/// ### Tag 121: Unique End-to-End Transaction Reference (UETR)
706/// - **Format**: 36 characters UUID (8-4-4-4-12 format)
707/// - **Purpose**: Global payment tracking across entire payment chain
708/// - **Mandatory**: Required for SWIFT gpi participant banks
709/// - **Persistence**: Must remain unchanged through payment lifecycle
710///
711/// ### Tag 119: Validation Flag
712/// - **STP**: Straight-Through Processing capability
713/// - **REMIT**: Remittance information present
714/// - **COV**: Cover payment indicator
715/// - **RFDD**: Request for Direct Debit
716///
717/// ## Service Identifiers
718/// ### Tag 103: Service Identifier
719/// - **Purpose**: Identifies specific SWIFT services
720/// - **Values**: Service-specific codes (e.g., "FIN")
721/// - **Usage**: Mainly for FIN Copy service
722/// - **Processing**: Affects message routing and copying
723///
724/// ### Tag 111: Service Type Identifier  
725/// - **Format**: 3 numeric digits
726/// - **Purpose**: Sub-categorizes service types
727/// - **Common**: "001" for standard processing
728/// - **Impact**: May affect fee calculation
729///
730/// ## Message Control and Reference
731/// ### Tag 108: Message User Reference (MUR)
732/// - **Format**: Up to 16 characters
733/// - **Purpose**: Sender's unique reference
734/// - **Usage**: Transaction tracking and reconciliation
735/// - **Uniqueness**: Should be unique per sender
736///
737/// ### Tag 113: Banking Priority
738/// - **Format**: 4 characters
739/// - **Values**: NORM, HIGH, URGP
740/// - **Purpose**: Internal bank priority handling
741/// - **Note**: Different from network priority
742///
743/// ## Compliance and Screening Tags
744/// ### Tag 433: Sanctions Screening Information
745/// - **AOK**: All OK - Passed screening
746/// - **FPO**: False Positive Override
747/// - **NOK**: Not OK - Requires review
748/// - **Additional**: Optional 20 character details
749///
750/// ### Tag 434: Payment Controls Information
751/// - **Format**: 3-letter code + optional details
752/// - **Purpose**: Payment control status
753/// - **Usage**: Compliance and regulatory controls
754/// - **Processing**: May trigger manual review
755///
756/// ## FIN Copy Service Tags
757/// ### Tag 115: Addressee Information
758/// - **Format**: Up to 32 characters
759/// - **Purpose**: Third-party copy recipient
760/// - **Service**: FIN Copy service only
761/// - **Delivery**: Additional message copy sent
762///
763/// ### Tag 165: Payment Release Information
764/// - **Format**: 3-char code + optional 34 chars
765/// - **Service**: FINInform service
766/// - **Purpose**: Payment release notifications
767/// - **Usage**: Corporate payment factories
768///
769/// ## Message Recovery Tags (MIRS)
770/// ### Tag 106: Message Input Reference (MIR)
771/// - **Format**: 28 characters structured
772/// - **Components**: Date + LT + Session + Sequence
773/// - **Purpose**: Original message reference
774/// - **Usage**: Message recovery and reconciliation
775///
776/// ### Tag 423: Balance Checkpoint
777/// - **Format**: YYMMDDHHMMSS\[ss\]
778/// - **Purpose**: Balance snapshot timing
779/// - **Service**: MIRS recovery service
780/// - **Precision**: Optional hundredths of second
781///
782/// ### Tag 424: Related Reference
783/// - **Format**: Up to 16 characters
784/// - **Purpose**: Links related messages
785/// - **Usage**: Message chains and corrections
786/// - **Service**: MIRS functionality
787///
788/// ## Network Validation Requirements
789/// - **Tag Compatibility**: Some tags require specific services
790/// - **Value Validation**: Each tag has specific format rules
791/// - **Service Subscription**: Tags available per service agreement
792/// - **Mandatory Combinations**: Some tags require others
793///
794/// ## Regional and Regulatory Impact
795/// - **SWIFT gpi**: Tag 121 mandatory for participants
796/// - **EU Regulations**: Enhanced screening requirements
797/// - **US Compliance**: Specific control requirements
798/// - **Local Rules**: Additional regional tag usage
799///
800/// ## STP Processing Impact
801/// ### Validation Flag Effects
802/// - **STP Flag**: Enables full automation
803/// - **Format Restrictions**: Stricter field validation
804/// - **Character Sets**: Limited to STP-safe characters
805/// - **Processing Speed**: Faster automated handling
806///
807/// ## Error Prevention Guidelines
808/// - **UETR Format**: Ensure valid UUID format
809/// - **Service Compatibility**: Verify tag availability
810/// - **Value Formats**: Follow exact specifications
811/// - **Mandatory Rules**: Include required combinations
812///
813/// ## Integration with Other Blocks
814/// - **Block 1**: Service must match subscription
815/// - **Block 2**: Message type affects available tags
816/// - **Block 4**: Validation flags affect field rules
817/// - **Block 5**: Some tags reflected in trailer
818///
819/// ## Compliance Framework
820/// - **Regulatory Mandates**: Screening and control requirements
821/// - **Audit Trail**: Enhanced tracking via UETR
822/// - **Service Agreements**: Tag usage per agreement
823/// - **Privacy Rules**: Data handling requirements
824///
825/// ## Best Practices
826/// - **UETR Generation**: Use proper UUID libraries
827/// - **Reference Uniqueness**: Ensure MUR uniqueness
828/// - **Screening Accuracy**: Accurate screening codes
829/// - **Service Alignment**: Use appropriate service tags
830///
831/// ## Future Evolution
832/// - **ISO 20022 Alignment**: Mapping considerations
833/// - **Enhanced Tracking**: Additional tracking features
834/// - **Compliance Evolution**: New regulatory tags
835/// - **Service Innovation**: New service identifiers
836///
837/// ## See Also
838/// - SWIFT FIN User Handbook: Block 3 Tag Specifications
839/// - SWIFT gpi Standards: UETR Implementation Guide
840/// - STP Guidelines: Validation Flag Requirements
841/// - Compliance Framework: Screening Tag Usage
842#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
843pub struct UserHeader {
844    /// Tag 103 - Service Identifier (3!a) - Mandatory for FINcopy Service
845    #[serde(skip_serializing_if = "Option::is_none")]
846    pub service_identifier: Option<String>,
847
848    /// Tag 113 - Banking Priority (4!x) - Optional
849    #[serde(skip_serializing_if = "Option::is_none")]
850    pub banking_priority: Option<String>,
851
852    /// Tag 108 - Message User Reference (16!x) - Optional
853    #[serde(skip_serializing_if = "Option::is_none")]
854    pub message_user_reference: Option<String>,
855
856    /// Tag 119 - Validation Flag (8c) - Optional (STP, REMIT, RFDD, COV)
857    #[serde(skip_serializing_if = "Option::is_none")]
858    pub validation_flag: Option<String>,
859
860    /// Tag 423 - Balance checkpoint date and time (YYMMDDHHMMSS\[ss\]) - Optional (MIRS only)
861    #[serde(skip_serializing_if = "Option::is_none")]
862    pub balance_checkpoint: Option<BalanceCheckpoint>,
863
864    /// Tag 106 - Message Input Reference MIR (28c) - Optional (MIRS only)
865    #[serde(skip_serializing_if = "Option::is_none")]
866    pub message_input_reference: Option<MessageInputReference>,
867
868    /// Tag 424 - Related reference (16x) - Optional (MIRS only)
869    #[serde(skip_serializing_if = "Option::is_none")]
870    pub related_reference: Option<String>,
871
872    /// Tag 111 - Service type identifier (3!n) - Optional
873    #[serde(skip_serializing_if = "Option::is_none")]
874    pub service_type_identifier: Option<String>,
875
876    /// Tag 121 - Unique end-to-end transaction reference (UUID format) - Mandatory for GPI
877    #[serde(skip_serializing_if = "Option::is_none")]
878    pub unique_end_to_end_reference: Option<String>,
879
880    /// Tag 115 - Addressee Information (32x) - Optional (FINCopy only)
881    #[serde(skip_serializing_if = "Option::is_none")]
882    pub addressee_information: Option<String>,
883
884    /// Tag 165 - Payment release information receiver (3!c/34x) - Optional (FINInform only)
885    #[serde(skip_serializing_if = "Option::is_none")]
886    pub payment_release_information: Option<PaymentReleaseInfo>,
887
888    /// Tag 433 - Sanctions screening information (3!a/\[20x\]) - Optional
889    #[serde(skip_serializing_if = "Option::is_none")]
890    pub sanctions_screening_info: Option<SanctionsScreeningInfo>,
891
892    /// Tag 434 - Payment controls information (3!a/\[20x\]) - Optional
893    #[serde(skip_serializing_if = "Option::is_none")]
894    pub payment_controls_info: Option<PaymentControlsInfo>,
895}
896
897/// Balance checkpoint structure for Tag 423
898#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
899pub struct BalanceCheckpoint {
900    pub date: String, // YYMMDD
901    pub time: String, // HHMMSS
902
903    #[serde(skip_serializing_if = "Option::is_none")]
904    pub hundredths_of_second: Option<String>, // ss (optional)
905}
906
907/// Message Input Reference structure for Tag 106
908#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
909pub struct MessageInputReference {
910    pub date: String,            // YYMMDD
911    pub lt_identifier: String,   // 12 characters
912    pub branch_code: String,     // 3!c
913    pub session_number: String,  // 4!n
914    pub sequence_number: String, // 6!n
915}
916
917/// Payment release information structure for Tag 165
918#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
919pub struct PaymentReleaseInfo {
920    pub code: String, // 3!c
921
922    #[serde(skip_serializing_if = "Option::is_none")]
923    pub additional_info: Option<String>, // 34x (optional)
924}
925
926/// Sanctions screening information structure for Tag 433
927#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
928pub struct SanctionsScreeningInfo {
929    pub code_word: String, // 3!a (AOK, FPO, NOK)
930
931    #[serde(skip_serializing_if = "Option::is_none")]
932    pub additional_info: Option<String>, // 20x (optional)
933}
934
935/// Payment controls information structure for Tag 434
936#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
937pub struct PaymentControlsInfo {
938    pub code_word: String, // 3!a
939
940    #[serde(skip_serializing_if = "Option::is_none")]
941    pub additional_info: Option<String>, // 20x (optional)
942}
943
944impl UserHeader {
945    /// Parse user header from block 3 string using structured parsing
946    pub fn parse(block3: &str) -> Result<Self> {
947        let mut user_header = UserHeader::default();
948
949        // Parse nested tags in format {tag:value}
950        // Simple parsing for now - more sophisticated regex parsing can be added later
951        if block3.contains("{103:")
952            && let Some(start) = block3.find("{103:")
953            && let Some(end) = block3[start..].find('}')
954        {
955            user_header.service_identifier = Some(block3[start + 5..start + end].to_string());
956        }
957
958        if block3.contains("{113:")
959            && let Some(start) = block3.find("{113:")
960            && let Some(end) = block3[start..].find('}')
961        {
962            user_header.banking_priority = Some(block3[start + 5..start + end].to_string());
963        }
964
965        if block3.contains("{108:")
966            && let Some(start) = block3.find("{108:")
967            && let Some(end) = block3[start..].find('}')
968        {
969            user_header.message_user_reference = Some(block3[start + 5..start + end].to_string());
970        }
971
972        if block3.contains("{119:")
973            && let Some(start) = block3.find("{119:")
974            && let Some(end) = block3[start..].find('}')
975        {
976            user_header.validation_flag = Some(block3[start + 5..start + end].to_string());
977        }
978
979        if block3.contains("{423:")
980            && let Some(start) = block3.find("{423:")
981            && let Some(end) = block3[start..].find('}')
982        {
983            let value = &block3[start + 5..start + end];
984            user_header.balance_checkpoint = Self::parse_balance_checkpoint(value);
985        }
986
987        if block3.contains("{106:")
988            && let Some(start) = block3.find("{106:")
989            && let Some(end) = block3[start..].find('}')
990        {
991            let value = &block3[start + 5..start + end];
992            user_header.message_input_reference = Self::parse_message_input_reference(value);
993        }
994
995        if block3.contains("{424:")
996            && let Some(start) = block3.find("{424:")
997            && let Some(end) = block3[start..].find('}')
998        {
999            user_header.related_reference = Some(block3[start + 5..start + end].to_string());
1000        }
1001
1002        if block3.contains("{111:")
1003            && let Some(start) = block3.find("{111:")
1004            && let Some(end) = block3[start..].find('}')
1005        {
1006            user_header.service_type_identifier = Some(block3[start + 5..start + end].to_string());
1007        }
1008
1009        if block3.contains("{121:")
1010            && let Some(start) = block3.find("{121:")
1011            && let Some(end) = block3[start..].find('}')
1012        {
1013            user_header.unique_end_to_end_reference =
1014                Some(block3[start + 5..start + end].to_string());
1015        }
1016
1017        if block3.contains("{115:")
1018            && let Some(start) = block3.find("{115:")
1019            && let Some(end) = block3[start..].find('}')
1020        {
1021            user_header.addressee_information = Some(block3[start + 5..start + end].to_string());
1022        }
1023
1024        if block3.contains("{165:")
1025            && let Some(start) = block3.find("{165:")
1026            && let Some(end) = block3[start..].find('}')
1027        {
1028            let value = &block3[start + 5..start + end];
1029            user_header.payment_release_information = Self::parse_payment_release_info(value);
1030        }
1031
1032        if block3.contains("{433:")
1033            && let Some(start) = block3.find("{433:")
1034            && let Some(end) = block3[start..].find('}')
1035        {
1036            let value = &block3[start + 5..start + end];
1037            user_header.sanctions_screening_info = Self::parse_sanctions_screening_info(value);
1038        }
1039
1040        if block3.contains("{434:")
1041            && let Some(start) = block3.find("{434:")
1042            && let Some(end) = block3[start..].find('}')
1043        {
1044            let value = &block3[start + 5..start + end];
1045            user_header.payment_controls_info = Self::parse_payment_controls_info(value);
1046        }
1047
1048        Ok(user_header)
1049    }
1050
1051    /// Parse balance checkpoint from tag value
1052    fn parse_balance_checkpoint(value: &str) -> Option<BalanceCheckpoint> {
1053        if value.len() >= 12 {
1054            Some(BalanceCheckpoint {
1055                date: value[0..6].to_string(),
1056                time: value[6..12].to_string(),
1057                hundredths_of_second: if value.len() > 12 {
1058                    Some(value[12..].to_string())
1059                } else {
1060                    None
1061                },
1062            })
1063        } else {
1064            None
1065        }
1066    }
1067
1068    /// Parse message input reference from tag value
1069    fn parse_message_input_reference(value: &str) -> Option<MessageInputReference> {
1070        if value.len() >= 28 {
1071            Some(MessageInputReference {
1072                date: value[0..6].to_string(),
1073                lt_identifier: value[6..18].to_string(),
1074                branch_code: value[18..21].to_string(),
1075                session_number: value[21..25].to_string(),
1076                sequence_number: value[25..].to_string(),
1077            })
1078        } else {
1079            None
1080        }
1081    }
1082
1083    /// Parse payment release info from tag value
1084    fn parse_payment_release_info(value: &str) -> Option<PaymentReleaseInfo> {
1085        if value.len() >= 3 {
1086            let code = value[0..3].to_string();
1087            let additional_info = if value.len() > 4 && value.chars().nth(3) == Some('/') {
1088                Some(value[4..].to_string())
1089            } else {
1090                None
1091            };
1092            Some(PaymentReleaseInfo {
1093                code,
1094                additional_info,
1095            })
1096        } else {
1097            None
1098        }
1099    }
1100
1101    /// Parse sanctions screening info from tag value
1102    fn parse_sanctions_screening_info(value: &str) -> Option<SanctionsScreeningInfo> {
1103        if value.len() >= 3 {
1104            let code_word = value[0..3].to_string();
1105            let additional_info = if value.len() > 4 && value.chars().nth(3) == Some('/') {
1106                Some(value[4..].to_string())
1107            } else {
1108                None
1109            };
1110            Some(SanctionsScreeningInfo {
1111                code_word,
1112                additional_info,
1113            })
1114        } else {
1115            None
1116        }
1117    }
1118
1119    /// Parse payment controls info from tag value
1120    fn parse_payment_controls_info(value: &str) -> Option<PaymentControlsInfo> {
1121        if value.len() >= 3 {
1122            let code_word = value[0..3].to_string();
1123            let additional_info = if value.len() > 4 && value.chars().nth(3) == Some('/') {
1124                Some(value[4..].to_string())
1125            } else {
1126                None
1127            };
1128            Some(PaymentControlsInfo {
1129                code_word,
1130                additional_info,
1131            })
1132        } else {
1133            None
1134        }
1135    }
1136}
1137
1138impl std::fmt::Display for UserHeader {
1139    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1140        let mut result = String::new();
1141
1142        if let Some(ref service_id) = self.service_identifier {
1143            result.push_str(&format!("{{103:{service_id}}}"));
1144        }
1145
1146        if let Some(ref banking_priority) = self.banking_priority {
1147            result.push_str(&format!("{{113:{banking_priority}}}"));
1148        }
1149
1150        if let Some(ref message_user_ref) = self.message_user_reference {
1151            result.push_str(&format!("{{108:{message_user_ref}}}"));
1152        }
1153
1154        if let Some(ref validation_flag) = self.validation_flag {
1155            result.push_str(&format!("{{119:{validation_flag}}}"));
1156        }
1157
1158        if let Some(ref unique_end_to_end_ref) = self.unique_end_to_end_reference {
1159            result.push_str(&format!("{{121:{unique_end_to_end_ref}}}"));
1160        }
1161
1162        if let Some(ref service_type_id) = self.service_type_identifier {
1163            result.push_str(&format!("{{111:{service_type_id}}}"));
1164        }
1165
1166        if let Some(ref payment_controls) = self.payment_controls_info {
1167            let mut value = payment_controls.code_word.clone();
1168            if let Some(ref additional) = payment_controls.additional_info {
1169                value.push('/');
1170                value.push_str(additional);
1171            }
1172            result.push_str(&format!("{{434:{value}}}"));
1173        }
1174
1175        if let Some(ref payment_release) = self.payment_release_information {
1176            let mut value = payment_release.code.clone();
1177            if let Some(ref additional) = payment_release.additional_info {
1178                value.push('/');
1179                value.push_str(additional);
1180            }
1181            result.push_str(&format!("{{165:{value}}}"));
1182        }
1183
1184        if let Some(ref sanctions) = self.sanctions_screening_info {
1185            let mut value = sanctions.code_word.clone();
1186            if let Some(ref additional) = sanctions.additional_info {
1187                value.push('/');
1188                value.push_str(additional);
1189            }
1190            result.push_str(&format!("{{433:{value}}}"));
1191        }
1192
1193        write!(f, "{result}")
1194    }
1195}
1196
1197/// **Trailer (Block 5): Message Security and Control Information**
1198///
1199/// ## Purpose
1200/// The Trailer block provides essential security, authentication, and control
1201/// information for SWIFT messages. It ensures message integrity through checksums,
1202/// enables duplicate detection, supports message authentication, and provides
1203/// system-level control information critical for secure and reliable message delivery.
1204///
1205/// ## Format Specification
1206/// - **Block Format**: `{5:{tag:value}{tag:value}...}`
1207/// - **Tag Types**: Three-letter tags with optional values
1208/// - **Security Tags**: MAC and CHK for authentication
1209/// - **Control Tags**: Various operational controls
1210///
1211/// ## Business Context Applications
1212/// - **Message Integrity**: Checksum validation for data integrity
1213/// - **Security Authentication**: MAC for message authentication
1214/// - **Duplicate Detection**: Prevention of duplicate processing
1215/// - **Operational Control**: Test messages and system controls
1216///
1217/// ## Security and Authentication Tags
1218/// ### CHK: Checksum (Mandatory)
1219/// - **Format**: 12 hexadecimal characters
1220/// - **Calculation**: Algorithm-based on message content
1221/// - **Purpose**: Detect transmission errors
1222/// - **Validation**: Automatic by SWIFT network
1223/// - **Failure Action**: Message rejection
1224///
1225/// ### MAC: Message Authentication Code
1226/// - **Format**: Variable length hexadecimal
1227/// - **Algorithm**: Agreed between parties
1228/// - **Purpose**: Authenticate message origin
1229/// - **Usage**: High-value or sensitive messages
1230/// - **Bilateral**: Requires key exchange
1231///
1232/// ## Duplicate Control Tags
1233/// ### PDM: Possible Duplicate Message
1234/// - **Format**: Optional time + MOR
1235/// - **Purpose**: Warn of possible duplicate
1236/// - **Action**: Receiver should check for duplicates
1237/// - **Components**: Time (HHMM) + Message Output Reference
1238///
1239/// ### PDE: Possible Duplicate Emission
1240/// - **Format**: Optional time + MIR  
1241/// - **Purpose**: Sender suspects duplicate sent
1242/// - **Usage**: Network recovery scenarios
1243/// - **Components**: Time (HHMM) + Message Input Reference
1244///
1245/// ## Operational Control Tags
1246/// ### TNG: Test and Training
1247/// - **Format**: Empty tag (presence only)
1248/// - **Purpose**: Identifies test messages
1249/// - **Processing**: Should not affect production
1250/// - **Usage**: Testing and training environments
1251/// - **Warning**: Must not be processed as live
1252///
1253/// ### DLM: Delayed Message
1254/// - **Format**: Empty tag (presence only)
1255/// - **Purpose**: Indicates delayed transmission
1256/// - **Cause**: Network issues or recovery
1257/// - **Action**: Check value dates and cut-offs
1258///
1259/// ## Reference and Tracking Tags
1260/// ### MRF: Message Reference
1261/// - **Format**: Date + Time + MIR
1262/// - **Purpose**: Reference related messages
1263/// - **Usage**: Corrections and cancellations
1264/// - **Components**: YYMMDD + HHMM + full MIR
1265///
1266/// ### SYS: System Originated Message
1267/// - **Format**: Optional time + MIR
1268/// - **Purpose**: System-generated messages
1269/// - **Examples**: Automatic responses
1270/// - **Processing**: May have special handling
1271///
1272/// ## Message Reference Structures
1273/// ### Message Input Reference (MIR)
1274/// - **Date**: YYMMDD format
1275/// - **LT Identifier**: 12-character sending LT
1276/// - **Session**: 4-digit session number
1277/// - **Sequence**: 6-digit sequence number
1278/// - **Usage**: Unique message identification
1279///
1280/// ### Message Output Reference (MOR)
1281/// - **Format**: Same structure as MIR
1282/// - **Perspective**: Receiver's reference
1283/// - **Purpose**: Delivery confirmation
1284/// - **Tracking**: End-to-end message tracking
1285///
1286/// ## Network Validation Requirements
1287/// - **CHK Mandatory**: All messages must have checksum
1288/// - **Tag Order**: Specific ordering requirements
1289/// - **Format Compliance**: Exact format specifications
1290/// - **Value Validation**: Tag-specific validations
1291///
1292/// ## Security Considerations
1293/// ### Checksum Protection
1294/// - **Coverage**: Entire message content
1295/// - **Algorithm**: SWIFT-specified calculation
1296/// - **Tampering**: Detects any modification
1297/// - **Reliability**: Very low false positive rate
1298///
1299/// ### MAC Authentication
1300/// - **Bilateral Agreement**: Key management required
1301/// - **Algorithm Choice**: Per agreement
1302/// - **Non-repudiation**: Proves message origin
1303/// - **Legal Standing**: Admissible evidence
1304///
1305/// ## Duplicate Detection Mechanisms
1306/// ### System Design
1307/// - **Detection Window**: Configurable timeframe
1308/// - **Reference Tracking**: MIR/MOR correlation
1309/// - **Recovery Support**: Post-incident reconciliation
1310/// - **Audit Trail**: Complete duplicate history
1311///
1312/// ### Processing Rules
1313/// - **PDM Messages**: Manual review recommended
1314/// - **Duplicate Window**: Typically 24-48 hours
1315/// - **Action Required**: Verify before processing
1316/// - **Documentation**: Record resolution actions
1317///
1318/// ## Operational Guidelines
1319/// ### Test Message Handling
1320/// - **TNG Identification**: Clear test marking
1321/// - **Environment Separation**: Test vs production
1322/// - **Processing Prevention**: Automatic filtering
1323/// - **Audit Exclusion**: Separate test reporting
1324///
1325/// ### Delayed Message Processing
1326/// - **DLM Recognition**: Special handling required
1327/// - **Value Date Check**: Verify still valid
1328/// - **Cut-off Impact**: May miss deadlines
1329/// - **Notification**: Alert relevant parties
1330///
1331/// ## Error Prevention Guidelines
1332/// - **CHK Calculation**: Automatic by system
1333/// - **Tag Formatting**: Follow exact specifications
1334/// - **Reference Accuracy**: Verify MIR/MOR format
1335/// - **Test Separation**: Clear test identification
1336///
1337/// ## Integration with Other Blocks
1338/// - **Block 1-4**: Content for checksum calculation
1339/// - **Block 1**: Session/sequence for references
1340/// - **Block 2**: Message type affects trailer options
1341/// - **Block 3**: Some services require specific tags
1342///
1343/// ## Compliance Framework
1344/// - **Security Standards**: Cryptographic requirements
1345/// - **Audit Requirements**: Trailer preservation
1346/// - **Legal Admissibility**: Authentication standards
1347/// - **Regulatory Compliance**: Security controls
1348///
1349/// ## Recovery and Reconciliation
1350/// ### Message Recovery
1351/// - **Reference Tracking**: Via MIR/MOR
1352/// - **Duplicate Resolution**: PDM/PDE handling
1353/// - **Audit Support**: Complete tag history
1354/// - **Dispute Resolution**: Authentication proof
1355///
1356/// ### System Recovery
1357/// - **Checkpoint References**: Recovery points
1358/// - **Sequence Verification**: Gap detection
1359/// - **Duplicate Prevention**: During recovery
1360/// - **Integrity Validation**: CHK verification
1361///
1362/// ## Best Practices
1363/// - **Security First**: Always validate CHK
1364/// - **MAC Usage**: For high-value messages
1365/// - **Duplicate Vigilance**: Check PDM warnings
1366/// - **Test Clarity**: Clearly mark test messages
1367///
1368/// ## See Also
1369/// - SWIFT FIN Security Guide: Authentication Standards
1370/// - Checksum Algorithms: Technical Specifications
1371/// - Duplicate Detection: Best Practices Guide
1372/// - MAC Implementation: Bilateral Agreement Templates
1373#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
1374pub struct Trailer {
1375    /// CHK - Checksum (12!h) - Mandatory
1376    #[serde(skip_serializing_if = "Option::is_none")]
1377    pub checksum: Option<String>,
1378
1379    /// TNG - Test & Training Message - Optional (empty tag)
1380    #[serde(skip_serializing_if = "Option::is_none")]
1381    pub test_and_training: Option<bool>,
1382
1383    /// PDE - Possible Duplicate Emission - Optional
1384    #[serde(skip_serializing_if = "Option::is_none")]
1385    pub possible_duplicate_emission: Option<PossibleDuplicateEmission>,
1386
1387    /// DLM - Delayed Message - Optional (empty tag)
1388    #[serde(skip_serializing_if = "Option::is_none")]
1389    pub delayed_message: Option<bool>,
1390
1391    /// MRF - Message Reference - Optional
1392    #[serde(skip_serializing_if = "Option::is_none")]
1393    pub message_reference: Option<MessageReference>,
1394
1395    /// PDM - Possible Duplicate Message - Optional
1396    #[serde(skip_serializing_if = "Option::is_none")]
1397    pub possible_duplicate_message: Option<PossibleDuplicateMessage>,
1398
1399    /// SYS - System Originated Message - Optional
1400    #[serde(skip_serializing_if = "Option::is_none")]
1401    pub system_originated_message: Option<SystemOriginatedMessage>,
1402
1403    /// MAC - Message Authentication Code - Optional
1404    #[serde(skip_serializing_if = "Option::is_none")]
1405    pub mac: Option<String>,
1406}
1407
1408/// Possible Duplicate Emission structure for PDE tag
1409#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
1410pub struct PossibleDuplicateEmission {
1411    #[serde(skip_serializing_if = "Option::is_none")]
1412    pub time: Option<String>, // HHMM (optional)
1413
1414    #[serde(skip_serializing_if = "Option::is_none")]
1415    pub message_input_reference: Option<MessageInputReference>, // MIR (optional)
1416}
1417
1418/// Message Reference structure for MRF tag
1419#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
1420pub struct MessageReference {
1421    pub date: String,                                   // YYMMDD
1422    pub full_time: String,                              // HHMM
1423    pub message_input_reference: MessageInputReference, // MIR
1424}
1425
1426/// Possible Duplicate Message structure for PDM tag
1427#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
1428pub struct PossibleDuplicateMessage {
1429    #[serde(skip_serializing_if = "Option::is_none")]
1430    pub time: Option<String>, // HHMM (optional)
1431
1432    #[serde(skip_serializing_if = "Option::is_none")]
1433    pub message_output_reference: Option<MessageOutputReference>, // MOR (optional)
1434}
1435
1436/// Message Output Reference structure (similar to MIR but for output)
1437#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
1438pub struct MessageOutputReference {
1439    pub date: String,            // YYMMDD
1440    pub lt_identifier: String,   // 12 characters
1441    pub branch_code: String,     // 3!c
1442    pub session_number: String,  // 4!n
1443    pub sequence_number: String, // 6!n
1444}
1445
1446/// System Originated Message structure for SYS tag
1447#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
1448pub struct SystemOriginatedMessage {
1449    #[serde(skip_serializing_if = "Option::is_none")]
1450    pub time: Option<String>, // HHMM (optional)
1451
1452    #[serde(skip_serializing_if = "Option::is_none")]
1453    pub message_input_reference: Option<MessageInputReference>, // MIR (optional)
1454}
1455
1456impl Trailer {
1457    /// Parse trailer from block 5 string using structured parsing
1458    pub fn parse(block5: &str) -> Result<Self> {
1459        let mut trailer = Trailer::default();
1460
1461        // Extract common tags if present
1462        if block5.contains("{CHK:")
1463            && let Some(start) = block5.find("{CHK:")
1464            && let Some(end) = block5[start..].find('}')
1465        {
1466            trailer.checksum = Some(block5[start + 5..start + end].to_string());
1467        }
1468
1469        if block5.contains("{TNG}") {
1470            trailer.test_and_training = Some(true);
1471        }
1472
1473        if block5.contains("{DLM}") {
1474            trailer.delayed_message = Some(true);
1475        }
1476
1477        if block5.contains("{MAC:")
1478            && let Some(start) = block5.find("{MAC:")
1479            && let Some(end) = block5[start..].find('}')
1480        {
1481            trailer.mac = Some(block5[start + 5..start + end].to_string());
1482        }
1483
1484        // More complex parsing for structured tags can be added here
1485        // For now, implementing basic tag extraction
1486
1487        Ok(trailer)
1488    }
1489}
1490
1491impl std::fmt::Display for Trailer {
1492    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1493        let mut result = String::new();
1494
1495        if let Some(ref checksum) = self.checksum {
1496            result.push_str(&format!("{{CHK:{checksum}}}"));
1497        }
1498
1499        if let Some(true) = self.test_and_training {
1500            result.push_str("{TNG}");
1501        }
1502
1503        if let Some(true) = self.delayed_message {
1504            result.push_str("{DLM}");
1505        }
1506
1507        if let Some(ref possible_duplicate_emission) = self.possible_duplicate_emission {
1508            result.push_str(&format!(
1509                "{{PDE:{}}}",
1510                possible_duplicate_emission.time.as_deref().unwrap_or("")
1511            ));
1512        }
1513
1514        if let Some(ref message_reference) = self.message_reference {
1515            result.push_str(&format!("{{MRF:{}}}", message_reference.date));
1516        }
1517
1518        if let Some(ref mac) = self.mac {
1519            result.push_str(&format!("{{MAC:{mac}}}"));
1520        }
1521
1522        write!(f, "{result}")
1523    }
1524}
1525
1526#[cfg(test)]
1527mod tests {
1528    use super::*;
1529
1530    #[test]
1531    fn test_application_header_input_parsing() {
1532        // Test Input message format parsing
1533        let block2 = "I103DEUTDEFFAXXXN";
1534        let header = ApplicationHeader::parse(block2).unwrap();
1535
1536        match header {
1537            ApplicationHeader::Input(input) => {
1538                assert_eq!(input.message_type, "103");
1539                assert_eq!(input.destination_address, "DEUTDEFFAXXX");
1540                assert_eq!(input.receiver_bic, "DEUTDEFF");
1541                assert_eq!(input.priority, "N");
1542                assert_eq!(input.delivery_monitoring, None);
1543                assert_eq!(input.obsolescence_period, None);
1544            }
1545            ApplicationHeader::Output(_) => panic!("Expected Input header, got Output"),
1546        }
1547    }
1548
1549    #[test]
1550    fn test_application_header_input_parsing_with_monitoring() {
1551        // Test Input message with delivery monitoring
1552        let block2 = "I103DEUTDEFFAXXXU3003";
1553        let header = ApplicationHeader::parse(block2).unwrap();
1554
1555        match header {
1556            ApplicationHeader::Input(input) => {
1557                assert_eq!(input.message_type, "103");
1558                assert_eq!(input.destination_address, "DEUTDEFFAXXX");
1559                assert_eq!(input.receiver_bic, "DEUTDEFF");
1560                assert_eq!(input.priority, "U");
1561                assert_eq!(input.delivery_monitoring, Some("3".to_string()));
1562                assert_eq!(input.obsolescence_period, Some("003".to_string()));
1563            }
1564            ApplicationHeader::Output(_) => panic!("Expected Input header, got Output"),
1565        }
1566    }
1567
1568    #[test]
1569    fn test_application_header_output_parsing() {
1570        // Test Output message format parsing - the exact case from the issue
1571        let block2 = "O1031535051028DEUTDEFFAXXX08264556280510281535N";
1572        let header = ApplicationHeader::parse(block2).unwrap();
1573
1574        match header {
1575            ApplicationHeader::Output(output) => {
1576                assert_eq!(output.message_type, "103");
1577                assert_eq!(output.input_time, "1535");
1578                assert_eq!(output.output_date, "051028");
1579                assert_eq!(output.output_time, "1535");
1580                assert_eq!(output.priority, Some("N".to_string()));
1581
1582                // Check MIR structure
1583                assert_eq!(output.mir.date, "051028");
1584                assert_eq!(output.mir.lt_identifier, "DEUTDEFFAXXX");
1585                assert_eq!(output.mir.branch_code, "XXX");
1586                assert_eq!(output.mir.session_number, "0826");
1587                assert_eq!(output.mir.sequence_number, "455628");
1588            }
1589            ApplicationHeader::Input(_) => panic!("Expected Output header, got Input"),
1590        }
1591    }
1592
1593    #[test]
1594    fn test_application_header_output_parsing_different_message_type() {
1595        // Test another Output message format
1596        let block2 = "O2021245051028CHASUS33AXXX08264556280510281245U";
1597        let header = ApplicationHeader::parse(block2).unwrap();
1598
1599        match header {
1600            ApplicationHeader::Output(output) => {
1601                assert_eq!(output.message_type, "202");
1602                assert_eq!(output.mir.lt_identifier, "CHASUS33AXXX");
1603                assert_eq!(output.priority, Some("U".to_string()));
1604            }
1605            ApplicationHeader::Input(_) => panic!("Expected Output header, got Input"),
1606        }
1607    }
1608
1609    #[test]
1610    fn test_application_header_invalid_direction() {
1611        let block2 = "X103DEUTDEFFAXXXN";
1612        let result = ApplicationHeader::parse(block2);
1613
1614        assert!(result.is_err());
1615        if let Err(ParseError::InvalidBlockStructure { message, .. }) = result {
1616            assert!(message.contains("Invalid direction indicator"));
1617        } else {
1618            panic!("Expected InvalidBlockStructure error");
1619        }
1620    }
1621
1622    #[test]
1623    fn test_application_header_input_too_short() {
1624        let block2 = "I103DEUTDEF"; // Too short for Input format
1625        let result = ApplicationHeader::parse(block2);
1626
1627        assert!(result.is_err());
1628    }
1629
1630    #[test]
1631    fn test_application_header_output_too_short() {
1632        let block2 = "O103153505102"; // Too short for Output format (13 characters)
1633        let result = ApplicationHeader::parse(block2);
1634
1635        assert!(result.is_err());
1636        if let Err(ParseError::InvalidBlockStructure { message, .. }) = result {
1637            // This will now hit the Output-specific check since initial check is for 4 chars
1638            assert!(message.contains("Output Block 2 too short: expected at least 46 characters"));
1639        } else {
1640            panic!("Expected InvalidBlockStructure error");
1641        }
1642    }
1643
1644    #[test]
1645    fn test_application_header_output_minimum_length_but_still_too_short() {
1646        // This has 17 characters so it passes initial check but fails Output-specific check
1647        let block2 = "O10315350510280DE"; // 17 characters, but Output needs 46
1648        let result = ApplicationHeader::parse(block2);
1649
1650        assert!(result.is_err());
1651        if let Err(ParseError::InvalidBlockStructure { message, .. }) = result {
1652            assert!(message.contains("Output Block 2 too short: expected at least 46 characters"));
1653        } else {
1654            panic!("Expected InvalidBlockStructure error");
1655        }
1656    }
1657
1658    #[test]
1659    fn test_basic_header_parsing() {
1660        let block1 = "F01DEUTDEFFAXXX0000123456";
1661        let header = BasicHeader::parse(block1).unwrap();
1662
1663        assert_eq!(header.application_id, "F");
1664        assert_eq!(header.service_id, "01");
1665        assert_eq!(header.logical_terminal, "DEUTDEFFAXXX");
1666        assert_eq!(header.sender_bic, "DEUTDEFF");
1667        assert_eq!(header.session_number, "0000");
1668        assert_eq!(header.sequence_number, "123456");
1669    }
1670
1671    #[test]
1672    fn test_application_header_input_display() {
1673        let header = ApplicationHeader::Input(InputApplicationHeader {
1674            message_type: "103".to_string(),
1675            destination_address: "DEUTDEFFAXXX".to_string(),
1676            receiver_bic: "DEUTDEFF".to_string(),
1677            priority: "U".to_string(),
1678            delivery_monitoring: Some("3".to_string()),
1679            obsolescence_period: Some("003".to_string()),
1680        });
1681
1682        assert_eq!(header.to_string(), "I103DEUTDEFFAXXXU3003");
1683    }
1684
1685    #[test]
1686    fn test_application_header_output_display() {
1687        let mir = MessageInputReference {
1688            date: "051028".to_string(),
1689            lt_identifier: "DEUTDEFFAXXX".to_string(),
1690            branch_code: "XXX".to_string(),
1691            session_number: "0826".to_string(),
1692            sequence_number: "455628".to_string(),
1693        };
1694
1695        let header = ApplicationHeader::Output(OutputApplicationHeader {
1696            message_type: "103".to_string(),
1697            input_time: "1535".to_string(),
1698            mir,
1699            output_date: "051028".to_string(),
1700            output_time: "1535".to_string(),
1701            priority: Some("N".to_string()),
1702        });
1703
1704        assert_eq!(
1705            header.to_string(),
1706            "O1031535051028DEUTDEFFAXXX08264556280510281535N"
1707        );
1708    }
1709
1710    #[test]
1711    fn test_application_header_helper_methods() {
1712        let input_header = ApplicationHeader::Input(InputApplicationHeader {
1713            message_type: "103".to_string(),
1714            destination_address: "DEUTDEFFAXXX".to_string(),
1715            receiver_bic: "DEUTDEFF".to_string(),
1716            priority: "U".to_string(),
1717            delivery_monitoring: None,
1718            obsolescence_period: None,
1719        });
1720
1721        assert_eq!(input_header.message_type(), "103");
1722        assert_eq!(input_header.priority(), Some("U"));
1723
1724        let mir = MessageInputReference {
1725            date: "051028".to_string(),
1726            lt_identifier: "DEUTDEFFAXXX".to_string(),
1727            branch_code: "XXX".to_string(),
1728            session_number: "0826".to_string(),
1729            sequence_number: "455628".to_string(),
1730        };
1731
1732        let output_header = ApplicationHeader::Output(OutputApplicationHeader {
1733            message_type: "202".to_string(),
1734            input_time: "1535".to_string(),
1735            mir,
1736            output_date: "051028".to_string(),
1737            output_time: "1535".to_string(),
1738            priority: Some("N".to_string()),
1739        });
1740
1741        assert_eq!(output_header.message_type(), "202");
1742        assert_eq!(output_header.priority(), Some("N"));
1743    }
1744}