Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
97.44% covered (success)
97.44%
38 / 39
87.50% covered (warning)
87.50%
7 / 8
CRAP
0.00% covered (danger)
0.00%
0 / 1
SenderResolverService
97.44% covered (success)
97.44%
38 / 39
87.50% covered (warning)
87.50%
7 / 8
21
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
 isValidChannel
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 resolveProvider
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
3
 getProviders
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getSenders
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
2
 getDefaultSender
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
3
 resolve
93.33% covered (success)
93.33%
14 / 15
0.00% covered (danger)
0.00%
0 / 1
9.02
 getChannelFlags
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2
3namespace App\Modules\ContactModule\Services;
4
5use App\Modules\ChannelModule\Models\ChannelProviderModel;
6use App\Modules\ChannelModule\Models\ChannelSettingModel;
7use App\Modules\ContactModule\Models\SenderModel;
8
9class SenderResolverService
10{
11    public function __construct(
12        protected ChannelProviderModel $providerModel,
13        protected ChannelSettingModel  $channelSettingModel,
14        protected SenderModel          $senderModel
15    ) {}
16
17    /*
18    |--------------------------------------------------------------------------
19    | 1. VALIDATION CANAL
20    | Source : ChannelSettingModel — flags activés par l'admin
21    |--------------------------------------------------------------------------
22    */
23
24    /**
25     * Vérifie si un canal est activé dans les paramètres globaux.
26     * Utilise ChannelSettingModel::getChannelFlags() — source unique de vérité.
27     */
28    public function isValidChannel(string $channel): bool
29    {
30        $flags = $this->channelSettingModel->getChannelFlags();
31        return ($flags[$channel] ?? 0) === 1;
32    }
33
34    /*
35    |--------------------------------------------------------------------------
36    | 2. PROVIDER RESOLUTION
37    | Source : ChannelProviderModel::getByUserContext()
38    | Priorité : country exact > global, filtrés is_active = 1
39    |--------------------------------------------------------------------------
40    */
41
42    /**
43     * Retourne l'ID du provider prioritaire pour un canal + pays.
44     * S'appuie sur getByUserContext() qui joint channel_provider_setting.
45     */
46    public function resolveProvider(string $channel, string $countryCode): ?int
47    {
48        $providers = $this->providerModel->getByUserContext($channel, $countryCode);
49
50        if (empty($providers)) {
51            return null;
52        }
53
54        $first = $providers[0];
55
56        return is_object($first)
57            ? ($first->id ?? null)
58            : ($first['id'] ?? null);
59    }
60
61    /*
62    |--------------------------------------------------------------------------
63    | 3. LISTE DES PROVIDERS (AJAX)
64    | Source : ChannelProviderModel::getByUserContext()
65    |--------------------------------------------------------------------------
66    */
67
68    /**
69     * Retourne tous les providers actifs pour un canal + pays.
70     * Utilisé par le endpoint AJAX getProvidersByChannel.
71     */
72    public function getProviders(string $channel, string $countryCode): array
73    {
74        return $this->providerModel->getByUserContext($channel, $countryCode);
75    }
76
77    /*
78    |--------------------------------------------------------------------------
79    | 4. SENDERS LIST
80    | Source : SenderModel
81    |--------------------------------------------------------------------------
82    */
83
84    /**
85     * Retourne les expéditeurs actifs pour un canal + pays.
86     */
87    public function getSenders(string $channel, string $countryCode): array
88    {
89        $providerId = $this->resolveProvider($channel, $countryCode);
90
91        if (!$providerId) {
92            return [];
93        }
94
95        return $this->senderModel->getByChannelProvider($channel, $providerId);
96    }
97
98    /*
99    |--------------------------------------------------------------------------
100    | 5. DEFAULT SENDER
101    | Source : SenderModel, fallback sur premier disponible
102    |--------------------------------------------------------------------------
103    */
104
105    /**
106     * Retourne l'expéditeur par défaut pour un canal.
107     * Fallback sur le premier expéditeur disponible si aucun défaut défini.
108     */
109    public function getDefaultSender(string $channel, string $countryCode)
110    {
111        $providerId = $this->resolveProvider($channel, $countryCode);
112
113        if (!$providerId) {
114            return null;
115        }
116
117        $sender = $this->senderModel->getDefault($channel, $providerId);
118
119        if (!$sender) {
120            $senders = $this->senderModel->getByChannelProvider($channel, $providerId);
121            return $senders[0] ?? null;
122        }
123
124        return $sender;
125    }
126
127    /*
128    |--------------------------------------------------------------------------
129    | 6. RESOLUTION FINALE (ENTRY POINT)
130    | Combine provider + sender en un seul appel
131    |--------------------------------------------------------------------------
132    */
133
134    /**
135     * Résout provider + sender pour un canal + pays.
136     * Si senderId fourni et valide, l'utilise — sinon fallback sur default.
137     *
138     * @throws \RuntimeException si aucun provider actif
139     */
140    public function resolve(string $channel, string $countryCode, ?int $senderId = null): array
141    {
142        if (!$this->isValidChannel($channel)) {
143            throw new \RuntimeException("Canal désactivé: {$channel}");
144        }
145
146        $providerId = $this->resolveProvider($channel, $countryCode);
147
148        if (!$providerId) {
149            throw new \RuntimeException("Aucun provider actif pour le canal: {$channel}");
150        }
151
152        if ($senderId) {
153            $sender           = $this->senderModel->find($senderId);
154            $senderChannel    = is_object($sender) ? ($sender->channel ?? null)     : ($sender['channel'] ?? null);
155            $senderProviderId = is_object($sender) ? ($sender->provider_id ?? null) : ($sender['provider_id'] ?? null);
156
157            if ($sender && $senderChannel === $channel && (int) $senderProviderId === (int) $providerId) {
158                return ['provider_id' => $providerId, 'sender' => $sender];
159            }
160        }
161
162        return [
163            'provider_id' => $providerId,
164            'sender'      => $this->getDefaultSender($channel, $countryCode),
165        ];
166    }
167
168    /**
169     * Proxy — expose les flags canaux au controller sans couplage direct au model.
170     */
171    public function getChannelFlags(): array
172    {
173        return $this->channelSettingModel->getChannelFlags();
174    }
175}