1use crate::application::dto::{
2 CancelExchangeDto, CompleteExchangeDto, CreateLocalExchangeDto, LocalExchangeResponseDto,
3 OwnerCreditBalanceDto, OwnerExchangeSummaryDto, RateExchangeDto, RequestExchangeDto,
4 SelStatisticsDto,
5};
6use crate::application::ports::{
7 LocalExchangeRepository, OwnerCreditBalanceRepository, OwnerRepository,
8};
9use crate::domain::entities::{ExchangeStatus, ExchangeType, LocalExchange};
10use std::sync::Arc;
11use uuid::Uuid;
12
13pub struct LocalExchangeUseCases {
15 exchange_repo: Arc<dyn LocalExchangeRepository>,
16 balance_repo: Arc<dyn OwnerCreditBalanceRepository>,
17 owner_repo: Arc<dyn OwnerRepository>,
18}
19
20impl LocalExchangeUseCases {
21 pub fn new(
22 exchange_repo: Arc<dyn LocalExchangeRepository>,
23 balance_repo: Arc<dyn OwnerCreditBalanceRepository>,
24 owner_repo: Arc<dyn OwnerRepository>,
25 ) -> Self {
26 Self {
27 exchange_repo,
28 balance_repo,
29 owner_repo,
30 }
31 }
32
33 pub async fn create_exchange(
35 &self,
36 provider_id: Uuid, dto: CreateLocalExchangeDto,
38 ) -> Result<LocalExchangeResponseDto, String> {
39 let provider = self
41 .owner_repo
42 .find_by_id(provider_id)
43 .await?
44 .ok_or("Provider not found".to_string())?;
45
46 let _ = self
48 .owner_repo
49 .find_by_id(dto.building_id) .await?;
51
52 let exchange = LocalExchange::new(
54 dto.building_id,
55 provider_id,
56 dto.exchange_type,
57 dto.title,
58 dto.description,
59 dto.credits,
60 )?;
61
62 let created = self.exchange_repo.create(&exchange).await?;
64
65 Ok(LocalExchangeResponseDto::from_entity(
67 created,
68 format!("{} {}", provider.first_name, provider.last_name),
69 None,
70 ))
71 }
72
73 pub async fn get_exchange(&self, id: Uuid) -> Result<LocalExchangeResponseDto, String> {
75 let exchange = self
76 .exchange_repo
77 .find_by_id(id)
78 .await?
79 .ok_or("Exchange not found".to_string())?;
80
81 let provider = self
83 .owner_repo
84 .find_by_id(exchange.provider_id)
85 .await?
86 .ok_or("Provider not found".to_string())?;
87 let provider_name = format!("{} {}", provider.first_name, provider.last_name);
88
89 let requester_name = if let Some(requester_id) = exchange.requester_id {
91 let requester = self.owner_repo.find_by_id(requester_id).await?;
92 requester.map(|r| format!("{} {}", r.first_name, r.last_name))
93 } else {
94 None
95 };
96
97 Ok(LocalExchangeResponseDto::from_entity(
98 exchange,
99 provider_name,
100 requester_name,
101 ))
102 }
103
104 pub async fn list_building_exchanges(
106 &self,
107 building_id: Uuid,
108 ) -> Result<Vec<LocalExchangeResponseDto>, String> {
109 let exchanges = self.exchange_repo.find_by_building(building_id).await?;
110
111 self.enrich_exchanges_with_names(exchanges).await
112 }
113
114 pub async fn list_available_exchanges(
116 &self,
117 building_id: Uuid,
118 ) -> Result<Vec<LocalExchangeResponseDto>, String> {
119 let exchanges = self
120 .exchange_repo
121 .find_available_by_building(building_id)
122 .await?;
123
124 self.enrich_exchanges_with_names(exchanges).await
125 }
126
127 pub async fn list_owner_exchanges(
129 &self,
130 owner_id: Uuid,
131 ) -> Result<Vec<LocalExchangeResponseDto>, String> {
132 let exchanges = self.exchange_repo.find_by_owner(owner_id).await?;
133
134 self.enrich_exchanges_with_names(exchanges).await
135 }
136
137 pub async fn list_exchanges_by_type(
139 &self,
140 building_id: Uuid,
141 exchange_type: ExchangeType,
142 ) -> Result<Vec<LocalExchangeResponseDto>, String> {
143 let exchanges = self
144 .exchange_repo
145 .find_by_type(building_id, exchange_type.to_sql())
146 .await?;
147
148 self.enrich_exchanges_with_names(exchanges).await
149 }
150
151 pub async fn request_exchange(
153 &self,
154 exchange_id: Uuid,
155 requester_id: Uuid, _dto: RequestExchangeDto,
157 ) -> Result<LocalExchangeResponseDto, String> {
158 let mut exchange = self
160 .exchange_repo
161 .find_by_id(exchange_id)
162 .await?
163 .ok_or("Exchange not found".to_string())?;
164
165 exchange.request(requester_id)?;
167
168 let updated = self.exchange_repo.update(&exchange).await?;
170
171 self.get_exchange(updated.id).await
173 }
174
175 pub async fn start_exchange(
178 &self,
179 exchange_id: Uuid,
180 actor_id: Uuid, ) -> Result<LocalExchangeResponseDto, String> {
182 let mut exchange = self
183 .exchange_repo
184 .find_by_id(exchange_id)
185 .await?
186 .ok_or("Exchange not found".to_string())?;
187
188 exchange.start(actor_id)?;
189
190 let updated = self.exchange_repo.update(&exchange).await?;
191
192 self.get_exchange(updated.id).await
193 }
194
195 pub async fn complete_exchange(
198 &self,
199 exchange_id: Uuid,
200 actor_id: Uuid, _dto: CompleteExchangeDto,
202 ) -> Result<LocalExchangeResponseDto, String> {
203 let mut exchange = self
204 .exchange_repo
205 .find_by_id(exchange_id)
206 .await?
207 .ok_or("Exchange not found".to_string())?;
208
209 let requester_id = exchange
211 .requester_id
212 .ok_or("Exchange has no requester".to_string())?;
213
214 exchange.complete(actor_id)?;
215
216 let updated = self.exchange_repo.update(&exchange).await?;
217
218 self.update_credit_balances_on_completion(&updated).await?;
220
221 let mut provider_balance = self
223 .balance_repo
224 .get_or_create(updated.provider_id, updated.building_id)
225 .await?;
226 provider_balance.increment_exchanges();
227 self.balance_repo.update(&provider_balance).await?;
228
229 let mut requester_balance = self
230 .balance_repo
231 .get_or_create(requester_id, updated.building_id)
232 .await?;
233 requester_balance.increment_exchanges();
234 self.balance_repo.update(&requester_balance).await?;
235
236 self.get_exchange(updated.id).await
237 }
238
239 pub async fn cancel_exchange(
241 &self,
242 exchange_id: Uuid,
243 actor_id: Uuid, dto: CancelExchangeDto,
245 ) -> Result<LocalExchangeResponseDto, String> {
246 let mut exchange = self
247 .exchange_repo
248 .find_by_id(exchange_id)
249 .await?
250 .ok_or("Exchange not found".to_string())?;
251
252 exchange.cancel(actor_id, dto.reason)?;
253
254 let updated = self.exchange_repo.update(&exchange).await?;
255
256 self.get_exchange(updated.id).await
257 }
258
259 pub async fn rate_provider(
261 &self,
262 exchange_id: Uuid,
263 requester_id: Uuid, dto: RateExchangeDto,
265 ) -> Result<LocalExchangeResponseDto, String> {
266 let mut exchange = self
267 .exchange_repo
268 .find_by_id(exchange_id)
269 .await?
270 .ok_or("Exchange not found".to_string())?;
271
272 exchange.rate_provider(requester_id, dto.rating)?;
273
274 let updated = self.exchange_repo.update(&exchange).await?;
275
276 self.update_average_rating(updated.provider_id, updated.building_id)
278 .await?;
279
280 self.get_exchange(updated.id).await
281 }
282
283 pub async fn rate_requester(
285 &self,
286 exchange_id: Uuid,
287 provider_id: Uuid, dto: RateExchangeDto,
289 ) -> Result<LocalExchangeResponseDto, String> {
290 let mut exchange = self
291 .exchange_repo
292 .find_by_id(exchange_id)
293 .await?
294 .ok_or("Exchange not found".to_string())?;
295
296 exchange.rate_requester(provider_id, dto.rating)?;
297
298 let updated = self.exchange_repo.update(&exchange).await?;
299
300 if let Some(requester_id) = updated.requester_id {
302 self.update_average_rating(requester_id, updated.building_id)
303 .await?;
304 }
305
306 self.get_exchange(updated.id).await
307 }
308
309 pub async fn delete_exchange(&self, exchange_id: Uuid, actor_id: Uuid) -> Result<(), String> {
311 let exchange = self
312 .exchange_repo
313 .find_by_id(exchange_id)
314 .await?
315 .ok_or("Exchange not found".to_string())?;
316
317 if exchange.provider_id != actor_id {
319 return Err("Only the provider can delete the exchange".to_string());
320 }
321
322 if exchange.status == ExchangeStatus::Completed {
324 return Err("Cannot delete a completed exchange".to_string());
325 }
326
327 self.exchange_repo.delete(exchange_id).await?;
328
329 Ok(())
330 }
331
332 pub async fn get_credit_balance(
334 &self,
335 owner_id: Uuid,
336 building_id: Uuid,
337 ) -> Result<OwnerCreditBalanceDto, String> {
338 let balance = self
339 .balance_repo
340 .get_or_create(owner_id, building_id)
341 .await?;
342
343 let owner = self
344 .owner_repo
345 .find_by_id(owner_id)
346 .await?
347 .ok_or("Owner not found".to_string())?;
348 let owner_name = format!("{} {}", owner.first_name, owner.last_name);
349
350 Ok(OwnerCreditBalanceDto::from_entity(balance, owner_name))
351 }
352
353 pub async fn get_leaderboard(
355 &self,
356 building_id: Uuid,
357 limit: i32,
358 ) -> Result<Vec<OwnerCreditBalanceDto>, String> {
359 let balances = self
360 .balance_repo
361 .get_leaderboard(building_id, limit)
362 .await?;
363
364 let mut dtos = Vec::new();
365 for balance in balances {
366 if let Some(owner) = self.owner_repo.find_by_id(balance.owner_id).await? {
367 let owner_name = format!("{} {}", owner.first_name, owner.last_name);
368 dtos.push(OwnerCreditBalanceDto::from_entity(balance, owner_name));
369 }
370 }
371
372 Ok(dtos)
373 }
374
375 pub async fn get_statistics(&self, building_id: Uuid) -> Result<SelStatisticsDto, String> {
377 let total_exchanges = self.exchange_repo.count_by_building(building_id).await? as i32;
378
379 let active_exchanges = self
380 .exchange_repo
381 .count_by_building_and_status(building_id, ExchangeStatus::Offered.to_sql())
382 .await? as i32
383 + self
384 .exchange_repo
385 .count_by_building_and_status(building_id, ExchangeStatus::Requested.to_sql())
386 .await? as i32
387 + self
388 .exchange_repo
389 .count_by_building_and_status(building_id, ExchangeStatus::InProgress.to_sql())
390 .await? as i32;
391
392 let completed_exchanges = self
393 .exchange_repo
394 .count_by_building_and_status(building_id, ExchangeStatus::Completed.to_sql())
395 .await? as i32;
396
397 let total_credits_exchanged = self
398 .exchange_repo
399 .get_total_credits_exchanged(building_id)
400 .await?;
401
402 let active_participants = self
403 .balance_repo
404 .count_active_participants(building_id)
405 .await? as i32;
406
407 let average_exchange_rating = self
408 .exchange_repo
409 .get_average_exchange_rating(building_id)
410 .await?;
411
412 let service_count = self
414 .exchange_repo
415 .count_by_building_and_status(building_id, ExchangeType::Service.to_sql())
416 .await?;
417 let object_loan_count = self
418 .exchange_repo
419 .count_by_building_and_status(building_id, ExchangeType::ObjectLoan.to_sql())
420 .await?;
421 let shared_purchase_count = self
422 .exchange_repo
423 .count_by_building_and_status(building_id, ExchangeType::SharedPurchase.to_sql())
424 .await?;
425
426 let most_popular_exchange_type =
427 if service_count >= object_loan_count && service_count >= shared_purchase_count {
428 Some(ExchangeType::Service)
429 } else if object_loan_count >= shared_purchase_count {
430 Some(ExchangeType::ObjectLoan)
431 } else {
432 Some(ExchangeType::SharedPurchase)
433 };
434
435 Ok(SelStatisticsDto {
436 building_id,
437 total_exchanges,
438 active_exchanges,
439 completed_exchanges,
440 total_credits_exchanged,
441 active_participants,
442 average_exchange_rating,
443 most_popular_exchange_type,
444 })
445 }
446
447 pub async fn get_owner_summary(
449 &self,
450 owner_id: Uuid,
451 ) -> Result<OwnerExchangeSummaryDto, String> {
452 let owner = self
453 .owner_repo
454 .find_by_id(owner_id)
455 .await?
456 .ok_or("Owner not found".to_string())?;
457 let owner_name = format!("{} {}", owner.first_name, owner.last_name);
458
459 let exchanges = self.exchange_repo.find_by_owner(owner_id).await?;
460
461 let as_provider = exchanges
462 .iter()
463 .filter(|e| e.provider_id == owner_id)
464 .count() as i32;
465
466 let as_requester = exchanges
467 .iter()
468 .filter(|e| e.requester_id == Some(owner_id))
469 .count() as i32;
470
471 let mut total_ratings = 0;
473 let mut rating_count = 0;
474
475 for exchange in &exchanges {
476 if exchange.provider_id == owner_id {
477 if let Some(rating) = exchange.provider_rating {
478 total_ratings += rating;
479 rating_count += 1;
480 }
481 }
482 if exchange.requester_id == Some(owner_id) {
483 if let Some(rating) = exchange.requester_rating {
484 total_ratings += rating;
485 rating_count += 1;
486 }
487 }
488 }
489
490 let average_rating = if rating_count > 0 {
491 Some(total_ratings as f32 / rating_count as f32)
492 } else {
493 None
494 };
495
496 let recent: Vec<_> = exchanges.into_iter().take(5).collect();
498 let recent_dtos = self.enrich_exchanges_with_names(recent).await?;
499
500 Ok(OwnerExchangeSummaryDto {
501 owner_id,
502 owner_name,
503 as_provider,
504 as_requester,
505 total_exchanges: as_provider + as_requester,
506 average_rating,
507 recent_exchanges: recent_dtos,
508 })
509 }
510
511 async fn update_credit_balances_on_completion(
515 &self,
516 exchange: &LocalExchange,
517 ) -> Result<(), String> {
518 let requester_id = exchange
519 .requester_id
520 .ok_or("Exchange has no requester".to_string())?;
521
522 let mut provider_balance = self
524 .balance_repo
525 .get_or_create(exchange.provider_id, exchange.building_id)
526 .await?;
527 provider_balance.earn_credits(exchange.credits)?;
528 self.balance_repo.update(&provider_balance).await?;
529
530 let mut requester_balance = self
532 .balance_repo
533 .get_or_create(requester_id, exchange.building_id)
534 .await?;
535 requester_balance.spend_credits(exchange.credits)?;
536 self.balance_repo.update(&requester_balance).await?;
537
538 Ok(())
539 }
540
541 async fn update_average_rating(&self, owner_id: Uuid, building_id: Uuid) -> Result<(), String> {
543 let exchanges = self.exchange_repo.find_by_owner(owner_id).await?;
544
545 let mut total_ratings = 0;
546 let mut rating_count = 0;
547
548 for exchange in exchanges {
549 if exchange.provider_id == owner_id {
550 if let Some(rating) = exchange.provider_rating {
551 total_ratings += rating;
552 rating_count += 1;
553 }
554 }
555 if exchange.requester_id == Some(owner_id) {
556 if let Some(rating) = exchange.requester_rating {
557 total_ratings += rating;
558 rating_count += 1;
559 }
560 }
561 }
562
563 if rating_count > 0 {
564 let average = total_ratings as f32 / rating_count as f32;
565
566 let mut balance = self
567 .balance_repo
568 .get_or_create(owner_id, building_id)
569 .await?;
570 balance.update_rating(average)?;
571 self.balance_repo.update(&balance).await?;
572 }
573
574 Ok(())
575 }
576
577 async fn enrich_exchanges_with_names(
579 &self,
580 exchanges: Vec<LocalExchange>,
581 ) -> Result<Vec<LocalExchangeResponseDto>, String> {
582 let mut dtos = Vec::new();
583
584 for exchange in exchanges {
585 let provider = self.owner_repo.find_by_id(exchange.provider_id).await?;
587 let provider_name = if let Some(p) = provider {
588 format!("{} {}", p.first_name, p.last_name)
589 } else {
590 "Unknown".to_string()
591 };
592
593 let requester_name = if let Some(requester_id) = exchange.requester_id {
595 let requester = self.owner_repo.find_by_id(requester_id).await?;
596 requester.map(|r| format!("{} {}", r.first_name, r.last_name))
597 } else {
598 None
599 };
600
601 dtos.push(LocalExchangeResponseDto::from_entity(
602 exchange,
603 provider_name,
604 requester_name,
605 ));
606 }
607
608 Ok(dtos)
609 }
610}