<?php
 namespace App\Services; use App\Core\Sms\SmsInterface; use App\Core\Sms\SmsManager; use App\Models\Setting; use App\Models\SmsCountryRoute; use App\Models\SmsMessage; use Illuminate\Support\Facades\Log; class SmsService { private SmsManager $manager; public function __construct() { $this->manager = new SmsManager(); } public function send(string $phone, string $message, ?string $countryCode = null, ?int $clientId = null): bool { $providerName = null; try { $provider = $this->resolveProvider($phone, $countryCode); $providerName = $provider->getModuleInfo()['name'] ?? null; $result = $provider->sendSms([ 'to' => $phone, 'content' => $message, ]); $ok = $result['success'] ?? false; $this->logMessage($phone, $message, $ok, $clientId, $providerName, $result['message_id'] ?? null); return $ok; } catch (\Exception $e) { Log::error('SMS sending failed', [ 'phone' => $phone, 'country_code' => $countryCode, 'error' => $e->getMessage(), ]); $this->logMessage($phone, $message, false, $clientId, $providerName, null, $e->getMessage()); return false; } } private function logMessage( string $phone, string $content, bool $ok, ?int $clientId, ?string $provider = null, ?string $providerMessageId = null, ?string $statusDetail = null, ): void { try { SmsMessage::create([ 'client_id' => $clientId, 'to_number' => $phone, 'content' => $content, 'status' => $ok ? 'sent' : 'failed', 'status_detail' => $statusDetail, 'provider' => $provider, 'provider_message_id' => $providerMessageId, 'sent_at' => now(), ]); } catch (\Throwable $e) { Log::warning('SmsService: failed to log sms to database', [ 'phone' => $phone, 'error' => $e->getMessage(), ]); } } public function sendViaProvider(string $providerName, string $phone, string $message): bool { try { $provider = $this->manager->getProviderInstance($providerName); $result = $provider->sendSms([ 'to' => $phone, 'content' => $message, ]); return $result['success'] ?? false; } catch (\Exception $e) { Log::error('SMS sending failed via provider', [ 'provider' => $providerName, 'phone' => $phone, 'error' => $e->getMessage(), ]); return false; } } private function resolveProvider(string $to, ?string $countryCode = null): SmsInterface { if ($countryCode) { $countryCode = strtoupper($countryCode); $routes = SmsCountryRoute::forCountry($countryCode) ->active() ->orderByDesc('priority') ->get(); foreach ($routes as $route) { if ($this->manager->hasProvider($route->provider)) { try { return $this->manager->getProviderInstance($route->provider); } catch (\Exception $e) { Log::warning("SMS country route provider failed, trying next", [ 'country' => $countryCode, 'provider' => $route->provider, 'error' => $e->getMessage(), ]); } } } } if (! $countryCode) { $detectedCountry = $this->detectCountryFromPhone($to); if ($detectedCountry) { $routes = SmsCountryRoute::forCountry($detectedCountry) ->active() ->orderByDesc('priority') ->get(); foreach ($routes as $route) { if ($this->manager->hasProvider($route->provider)) { try { return $this->manager->getProviderInstance($route->provider); } catch (\Exception $e) { Log::warning("SMS country route provider failed, trying next", [ 'country' => $detectedCountry, 'provider' => $route->provider, 'error' => $e->getMessage(), ]); } } } } } $defaultProvider = Setting::getValue('sms_default_provider', Setting::getValue('sms_provider', 'twilio') ); if ($this->manager->hasProvider($defaultProvider)) { return $this->manager->getProviderInstance($defaultProvider); } return $this->manager->getProviderInstance('twilio'); } private function detectCountryFromPhone(string $phone): ?string { $phone = preg_replace('/[^0-9+]/', '', $phone); $prefixMap = [ '90' => 'TR', '1' => 'US', '44' => 'GB', '49' => 'DE', '33' => 'FR', '39' => 'IT', '34' => 'ES', '31' => 'NL', '46' => 'SE', '47' => 'NO', '45' => 'DK', '358' => 'FI', '48' => 'PL', '7' => 'RU', '81' => 'JP', '82' => 'KR', '86' => 'CN', '91' => 'IN', '61' => 'AU', '55' => 'BR', '27' => 'ZA', '971' => 'AE', '966' => 'SA', ]; $cleanPhone = ltrim($phone, '+'); foreach ([3, 2, 1] as $length) { $prefix = substr($cleanPhone, 0, $length); if (isset($prefixMap[$prefix])) { return $prefixMap[$prefix]; } } return null; } public function getAvailableProviders(): array { return $this->manager->getAvailableProviders(); } public function getManager(): SmsManager { return $this->manager; } } 