1use bytemuck::Zeroable;
4use iced::Color;
5
6#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
8#[repr(u8)]
9pub enum AlertType {
10 #[default]
12 VelocityBreach = 0,
13 AmountThreshold = 1,
15 GeographicAnomaly = 2,
17 StructuredTransaction = 3,
19 CircularTrading = 4,
21 SanctionsHit = 5,
23 PEPRelated = 6,
25 AdverseMedia = 7,
27 UnusualPattern = 8,
29}
30
31impl AlertType {
32 pub fn name(&self) -> &'static str {
34 match self {
35 AlertType::VelocityBreach => "Velocity Breach",
36 AlertType::AmountThreshold => "Amount Threshold",
37 AlertType::GeographicAnomaly => "Geographic Anomaly",
38 AlertType::StructuredTransaction => "Structured Transaction",
39 AlertType::CircularTrading => "Circular Trading",
40 AlertType::SanctionsHit => "Sanctions Hit",
41 AlertType::PEPRelated => "PEP Related",
42 AlertType::AdverseMedia => "Adverse Media",
43 AlertType::UnusualPattern => "Unusual Pattern",
44 }
45 }
46
47 pub fn code(&self) -> &'static str {
49 match self {
50 AlertType::VelocityBreach => "VEL",
51 AlertType::AmountThreshold => "AMT",
52 AlertType::GeographicAnomaly => "GEO",
53 AlertType::StructuredTransaction => "STR",
54 AlertType::CircularTrading => "CIR",
55 AlertType::SanctionsHit => "SAN",
56 AlertType::PEPRelated => "PEP",
57 AlertType::AdverseMedia => "ADV",
58 AlertType::UnusualPattern => "UNS",
59 }
60 }
61
62 pub fn from_u8(v: u8) -> Option<Self> {
64 match v {
65 0 => Some(AlertType::VelocityBreach),
66 1 => Some(AlertType::AmountThreshold),
67 2 => Some(AlertType::GeographicAnomaly),
68 3 => Some(AlertType::StructuredTransaction),
69 4 => Some(AlertType::CircularTrading),
70 5 => Some(AlertType::SanctionsHit),
71 6 => Some(AlertType::PEPRelated),
72 7 => Some(AlertType::AdverseMedia),
73 8 => Some(AlertType::UnusualPattern),
74 _ => None,
75 }
76 }
77}
78
79impl std::fmt::Display for AlertType {
80 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
81 write!(f, "{}", self.name())
82 }
83}
84
85#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
87#[repr(u8)]
88pub enum AlertSeverity {
89 #[default]
91 Low = 0,
92 Medium = 1,
94 High = 2,
96 Critical = 3,
98}
99
100impl AlertSeverity {
101 pub fn name(&self) -> &'static str {
103 match self {
104 AlertSeverity::Low => "LOW",
105 AlertSeverity::Medium => "MEDIUM",
106 AlertSeverity::High => "HIGH",
107 AlertSeverity::Critical => "CRITICAL",
108 }
109 }
110
111 pub fn color(&self) -> Color {
113 match self {
114 AlertSeverity::Low => Color::from_rgb(0.4, 0.7, 0.4), AlertSeverity::Medium => Color::from_rgb(0.9, 0.8, 0.2), AlertSeverity::High => Color::from_rgb(0.9, 0.5, 0.1), AlertSeverity::Critical => Color::from_rgb(0.9, 0.2, 0.2), }
119 }
120
121 pub fn indicator(&self) -> char {
123 match self {
124 AlertSeverity::Low => '\u{25CB}', AlertSeverity::Medium => '\u{25D0}', AlertSeverity::High => '\u{25CF}', AlertSeverity::Critical => '\u{25C9}', }
129 }
130
131 pub fn from_u8(v: u8) -> Option<Self> {
133 match v {
134 0 => Some(AlertSeverity::Low),
135 1 => Some(AlertSeverity::Medium),
136 2 => Some(AlertSeverity::High),
137 3 => Some(AlertSeverity::Critical),
138 _ => None,
139 }
140 }
141}
142
143impl std::fmt::Display for AlertSeverity {
144 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
145 write!(f, "{}", self.name())
146 }
147}
148
149#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
151#[repr(u8)]
152pub enum AlertStatus {
153 #[default]
155 New = 0,
156 Acknowledged = 1,
158 UnderReview = 2,
160 Escalated = 3,
162 Closed = 4,
164}
165
166impl AlertStatus {
167 pub fn from_u8(v: u8) -> Option<Self> {
169 match v {
170 0 => Some(AlertStatus::New),
171 1 => Some(AlertStatus::Acknowledged),
172 2 => Some(AlertStatus::UnderReview),
173 3 => Some(AlertStatus::Escalated),
174 4 => Some(AlertStatus::Closed),
175 _ => None,
176 }
177 }
178}
179
180#[derive(Debug, Clone, Copy)]
184#[repr(C, align(128))]
185pub struct MonitoringAlert {
186 pub alert_id: u64, pub transaction_id: u64, pub customer_id: u64, pub alert_type: u8, pub severity: u8, pub status: u8, _padding1: [u8; 5], pub amount_cents: u64, pub risk_score: u32, pub velocity_count: u32, pub timestamp: u64, pub country_code: u16, pub flags: u16, _padding2: [u8; 4], _reserved: [u8; 64], }
217
218unsafe impl bytemuck::Zeroable for MonitoringAlert {}
220unsafe impl bytemuck::Pod for MonitoringAlert {}
221
222const _: () = assert!(std::mem::size_of::<MonitoringAlert>() == 128);
223
224impl MonitoringAlert {
225 pub fn new(
227 alert_id: u64,
228 transaction_id: u64,
229 customer_id: u64,
230 alert_type: AlertType,
231 severity: AlertSeverity,
232 amount_cents: u64,
233 timestamp: u64,
234 ) -> Self {
235 Self {
236 alert_id,
237 transaction_id,
238 customer_id,
239 alert_type: alert_type as u8,
240 severity: severity as u8,
241 status: AlertStatus::New as u8,
242 _padding1: [0; 5],
243 amount_cents,
244 risk_score: 0,
245 velocity_count: 0,
246 timestamp,
247 country_code: 0,
248 flags: 0,
249 _padding2: [0; 4],
250 _reserved: [0; 64],
251 }
252 }
253
254 pub fn alert_type(&self) -> AlertType {
256 AlertType::from_u8(self.alert_type).unwrap_or_default()
257 }
258
259 pub fn severity(&self) -> AlertSeverity {
261 AlertSeverity::from_u8(self.severity).unwrap_or_default()
262 }
263
264 pub fn status(&self) -> AlertStatus {
266 AlertStatus::from_u8(self.status).unwrap_or_default()
267 }
268
269 pub fn format_amount(&self) -> String {
271 let dollars = self.amount_cents / 100;
272 let cents = self.amount_cents % 100;
273 format!("${}.{:02}", dollars, cents)
274 }
275
276 pub fn requires_immediate_action(&self) -> bool {
278 self.severity() == AlertSeverity::Critical
279 || self.alert_type() == AlertType::SanctionsHit
280 || self.alert_type() == AlertType::PEPRelated
281 }
282}
283
284impl Default for MonitoringAlert {
285 fn default() -> Self {
286 Self::zeroed()
287 }
288}