Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
98.15% covered (success)
98.15%
53 / 54
50.00% covered (danger)
50.00%
1 / 2
CRAP
0.00% covered (danger)
0.00%
0 / 1
SendVisitReminderAction
98.15% covered (success)
98.15%
53 / 54
50.00% covered (danger)
50.00%
1 / 2
8
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 execute
98.11% covered (success)
98.11%
52 / 53
0.00% covered (danger)
0.00%
0 / 1
7
1<?php
2
3declare(strict_types=1);
4
5namespace ImovelZapAi\Visits\Application\Actions;
6
7use ImovelZapAi\Conversations\Infrastructure\Persistence\Eloquent\ContactModel;
8use ImovelZapAi\Conversations\Infrastructure\Persistence\Eloquent\ConversationModel;
9use ImovelZapAi\Visits\Application\Ports\VisitOutboundMessengerInterface;
10use ImovelZapAi\Visits\Application\Services\VisitLogWriter;
11use ImovelZapAi\Visits\Application\Services\VisitMessageRenderer;
12use ImovelZapAi\Visits\Domain\Enums\VisitLogEvent;
13use ImovelZapAi\Visits\Domain\Enums\VisitReminderStatus;
14use ImovelZapAi\Visits\Infrastructure\Persistence\Eloquent\VisitReminderModel;
15use ImovelZapAi\Tenancy\Support\TenantContext;
16
17final class SendVisitReminderAction
18{
19    public function __construct(
20        private readonly VisitMessageRenderer $renderer,
21        private readonly VisitOutboundMessengerInterface $outbound,
22        private readonly VisitLogWriter $logWriter,
23    ) {}
24
25    public function execute(string $reminderId, string $tenantId): VisitReminderModel
26    {
27        TenantContext::set($tenantId);
28
29        try {
30            $reminder = VisitReminderModel::query()
31                ->where('tenant_id', $tenantId)
32                ->whereKey($reminderId)
33                ->with(['visit.contact', 'visit.property', 'visit.conversation'])
34                ->firstOrFail();
35
36            if ($reminder->status !== VisitReminderStatus::Pending) {
37                return $reminder;
38            }
39
40            $visit = $reminder->visit;
41            if ($visit === null || $visit->status->isTerminal()) {
42                $reminder->forceFill([
43                    'status' => VisitReminderStatus::Cancelled,
44                    'error_message' => 'Visita terminal — lembrete cancelado.',
45                ])->save();
46
47                return $reminder;
48            }
49
50            $body = $this->renderer->render($tenantId, $reminder->type, $visit);
51            if ($body === null) {
52                $reminder->forceFill([
53                    'status' => VisitReminderStatus::Cancelled,
54                    'error_message' => 'Template desabilitado.',
55                ])->save();
56
57                return $reminder;
58            }
59
60            try {
61                /** @var ContactModel $contact */
62                $contact = $visit->contact;
63                $conversation = $visit->conversation_id !== null
64                    ? ConversationModel::query()->find($visit->conversation_id)
65                    : null;
66
67                $this->outbound->send($tenantId, $contact, $body, $conversation);
68
69                $reminder->forceFill([
70                    'status' => VisitReminderStatus::Sent,
71                    'sent_at' => now(),
72                    'message_body' => $body,
73                    'error_message' => null,
74                ])->save();
75
76                $this->logWriter->write(
77                    $tenantId,
78                    $visit->id,
79                    VisitLogEvent::ReminderSent,
80                    message: $reminder->type->label().' enviado.',
81                );
82            } catch (\Throwable $e) {
83                $reminder->forceFill([
84                    'status' => VisitReminderStatus::Failed,
85                    'error_message' => $e->getMessage(),
86                    'message_body' => $body,
87                ])->save();
88
89                $this->logWriter->write(
90                    $tenantId,
91                    $visit->id,
92                    VisitLogEvent::ReminderFailed,
93                    message: $e->getMessage(),
94                );
95            }
96
97            return $reminder->fresh();
98        } finally {
99            TenantContext::clear();
100        }
101    }
102}