1use crate::error::{Error, Result};
2use crate::output::{print_success, OutputFormat};
3use crate::tap_integration::TapIntegration;
4use clap::Subcommand;
5use serde::Serialize;
6use tap_node::customer::CustomerManager;
7use tap_node::storage::models::{Customer, SchemaType};
8
9#[derive(Subcommand, Debug)]
10pub enum CustomerCommands {
11 List {
13 #[arg(long)]
15 agent_did: Option<String>,
16 #[arg(long, default_value = "50")]
18 limit: u32,
19 #[arg(long, default_value = "0")]
21 offset: u32,
22 },
23 Create {
25 #[arg(long)]
27 customer_id: String,
28 #[arg(long)]
30 profile: String,
31 #[arg(long)]
33 agent_did: Option<String>,
34 },
35 Details {
37 #[arg(long)]
39 customer_id: String,
40 #[arg(long)]
42 agent_did: Option<String>,
43 },
44 Update {
46 #[arg(long)]
48 customer_id: String,
49 #[arg(long)]
51 profile: String,
52 #[arg(long)]
54 agent_did: Option<String>,
55 },
56 Ivms101 {
58 #[arg(long)]
60 customer_id: String,
61 #[arg(long)]
63 agent_did: Option<String>,
64 },
65}
66
67#[derive(Debug, Serialize)]
68struct CustomerInfo {
69 id: String,
70 display_name: Option<String>,
71 schema_type: String,
72 address_country: Option<String>,
73 created_at: String,
74 updated_at: String,
75}
76
77#[derive(Debug, Serialize)]
78struct CustomerListResponse {
79 customers: Vec<CustomerInfo>,
80 total: usize,
81}
82
83pub async fn handle(
84 cmd: &CustomerCommands,
85 format: OutputFormat,
86 default_agent_did: &str,
87 tap_integration: &TapIntegration,
88) -> Result<()> {
89 match cmd {
90 CustomerCommands::List {
91 agent_did,
92 limit,
93 offset,
94 } => {
95 let effective_did = agent_did.as_deref().unwrap_or(default_agent_did);
96 let storage = tap_integration.storage_for_agent(effective_did).await?;
97 let customers = storage
98 .list_customers(effective_did, *limit, *offset)
99 .await?;
100
101 let customer_infos: Vec<CustomerInfo> = customers
102 .iter()
103 .map(|c| CustomerInfo {
104 id: c.id.clone(),
105 display_name: c.display_name.clone(),
106 schema_type: format!("{:?}", c.schema_type),
107 address_country: c.address_country.clone(),
108 created_at: c.created_at.clone(),
109 updated_at: c.updated_at.clone(),
110 })
111 .collect();
112
113 let response = CustomerListResponse {
114 total: customer_infos.len(),
115 customers: customer_infos,
116 };
117 print_success(format, &response);
118 Ok(())
119 }
120 CustomerCommands::Create {
121 customer_id,
122 profile,
123 agent_did,
124 } => {
125 let effective_did = agent_did.as_deref().unwrap_or(default_agent_did);
126 let storage = tap_integration.storage_for_agent(effective_did).await?;
127
128 let profile_json: serde_json::Value = serde_json::from_str(profile)
129 .map_err(|e| Error::invalid_parameter(format!("Invalid profile JSON: {}", e)))?;
130
131 let schema_type =
132 if profile_json.get("@type").and_then(|v| v.as_str()) == Some("Organization") {
133 SchemaType::Organization
134 } else {
135 SchemaType::Person
136 };
137
138 let customer = Customer {
139 id: customer_id.clone(),
140 agent_did: effective_did.to_string(),
141 schema_type,
142 given_name: profile_json
143 .get("givenName")
144 .and_then(|v| v.as_str())
145 .map(String::from),
146 family_name: profile_json
147 .get("familyName")
148 .and_then(|v| v.as_str())
149 .map(String::from),
150 display_name: profile_json
151 .get("name")
152 .and_then(|v| v.as_str())
153 .map(String::from),
154 legal_name: profile_json
155 .get("legalName")
156 .and_then(|v| v.as_str())
157 .map(String::from),
158 lei_code: profile_json
159 .get("leiCode")
160 .and_then(|v| v.as_str())
161 .map(String::from),
162 mcc_code: profile_json
163 .get("mccCode")
164 .and_then(|v| v.as_str())
165 .map(String::from),
166 address_country: profile_json
167 .get("addressCountry")
168 .and_then(|v| v.as_str())
169 .map(String::from),
170 address_locality: profile_json
171 .get("addressLocality")
172 .and_then(|v| v.as_str())
173 .map(String::from),
174 postal_code: profile_json
175 .get("postalCode")
176 .and_then(|v| v.as_str())
177 .map(String::from),
178 street_address: profile_json
179 .get("streetAddress")
180 .and_then(|v| v.as_str())
181 .map(String::from),
182 profile: profile_json,
183 ivms101_data: None,
184 verified_at: None,
185 created_at: chrono::Utc::now().to_rfc3339(),
186 updated_at: chrono::Utc::now().to_rfc3339(),
187 };
188
189 storage
190 .upsert_customer(&customer)
191 .await
192 .map_err(|e| Error::command_failed(format!("Failed to create customer: {}", e)))?;
193
194 #[derive(Serialize)]
195 struct Created {
196 customer_id: String,
197 status: String,
198 }
199 let response = Created {
200 customer_id: customer_id.clone(),
201 status: "created".to_string(),
202 };
203 print_success(format, &response);
204 Ok(())
205 }
206 CustomerCommands::Details {
207 customer_id,
208 agent_did,
209 } => {
210 let effective_did = agent_did.as_deref().unwrap_or(default_agent_did);
211 let storage = tap_integration.storage_for_agent(effective_did).await?;
212 let customer = storage.get_customer(customer_id).await?;
213
214 match customer {
215 Some(c) => {
216 print_success(format, &c);
217 Ok(())
218 }
219 None => Err(Error::command_failed(format!(
220 "Customer '{}' not found",
221 customer_id
222 ))),
223 }
224 }
225 CustomerCommands::Update {
226 customer_id,
227 profile,
228 agent_did,
229 } => {
230 let effective_did = agent_did.as_deref().unwrap_or(default_agent_did);
231 let storage = tap_integration.storage_for_agent(effective_did).await?;
232
233 let profile_json: serde_json::Value = serde_json::from_str(profile)
234 .map_err(|e| Error::invalid_parameter(format!("Invalid profile JSON: {}", e)))?;
235
236 let existing = storage.get_customer(customer_id).await?.ok_or_else(|| {
238 Error::command_failed(format!("Customer '{}' not found", customer_id))
239 })?;
240
241 let customer = Customer {
242 profile: profile_json,
243 updated_at: chrono::Utc::now().to_rfc3339(),
244 ..existing
245 };
246
247 storage
248 .upsert_customer(&customer)
249 .await
250 .map_err(|e| Error::command_failed(format!("Failed to update customer: {}", e)))?;
251
252 #[derive(Serialize)]
253 struct Updated {
254 customer_id: String,
255 status: String,
256 }
257 let response = Updated {
258 customer_id: customer_id.clone(),
259 status: "updated".to_string(),
260 };
261 print_success(format, &response);
262 Ok(())
263 }
264 CustomerCommands::Ivms101 {
265 customer_id,
266 agent_did,
267 } => {
268 let effective_did = agent_did.as_deref().unwrap_or(default_agent_did);
269 let storage = tap_integration.storage_for_agent(effective_did).await?;
270 let customer_manager = CustomerManager::new(storage);
271
272 let ivms_data = customer_manager
273 .generate_ivms101_data(customer_id)
274 .await
275 .map_err(|e| {
276 Error::command_failed(format!("Failed to generate IVMS101 data: {}", e))
277 })?;
278 print_success(format, &ivms_data);
279 Ok(())
280 }
281 }
282}