<?php

namespace App\Services;

use App\Models\Setting;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Log;

/**
 * Service d'envoi de SMS
 * 
 * Supporte plusieurs fournisseurs SMS africains:
 * - Twilio (international)
 * - Africa's Talking
 * - Orange SMS API
 * - Infobip
 * - Nexmo/Vonage
 * 
 * Configuration dans .env:
 * SMS_PROVIDER=africas_talking
 * SMS_API_KEY=your_api_key
 * SMS_API_SECRET=your_secret
 * SMS_SENDER_ID=BAMBOU_EKO
 */
class SmsService
{
    protected string $provider;
    protected string $apiKey;
    protected string $apiSecret;
    protected string $senderId;
    protected bool $enabled;

    public function __construct()
    {
        $this->provider = config('services.sms.provider', env('SMS_PROVIDER', 'log'));
        $this->apiKey = config('services.sms.api_key', env('SMS_API_KEY', ''));
        $this->apiSecret = config('services.sms.api_secret', env('SMS_API_SECRET', ''));
        $this->senderId = config('services.sms.sender_id', env('SMS_SENDER_ID', 'BAMBOU'));
        $this->enabled = Setting::get('sms_enabled', false);
    }

    /**
     * Envoyer un SMS
     */
    public function send(string $phone, string $message): bool
    {
        if (!$this->enabled && $this->provider !== 'log') {
            Log::info('SMS désactivé', ['phone' => $phone, 'message' => $message]);
            return false;
        }

        // Formater le numéro de téléphone
        $phone = $this->formatPhoneNumber($phone);

        if (!$this->validatePhoneNumber($phone)) {
            Log::warning('Numéro de téléphone invalide', ['phone' => $phone]);
            return false;
        }

        try {
            return match($this->provider) {
                'twilio' => $this->sendViaTwilio($phone, $message),
                'africas_talking' => $this->sendViaAfricasTalking($phone, $message),
                'orange' => $this->sendViaOrange($phone, $message),
                'infobip' => $this->sendViaInfobip($phone, $message),
                'vonage', 'nexmo' => $this->sendViaVonage($phone, $message),
                'log' => $this->logSms($phone, $message),
                default => $this->logSms($phone, $message),
            };
        } catch (\Exception $e) {
            Log::error('Erreur envoi SMS', [
                'provider' => $this->provider,
                'phone' => $phone,
                'error' => $e->getMessage(),
            ]);
            return false;
        }
    }

    /**
     * Envoyer un SMS en masse
     */
    public function sendBulk(array $recipients, string $message): array
    {
        $results = [
            'success' => 0,
            'failed' => 0,
            'details' => [],
        ];

        foreach ($recipients as $phone) {
            $success = $this->send($phone, $message);
            
            if ($success) {
                $results['success']++;
            } else {
                $results['failed']++;
            }
            
            $results['details'][$phone] = $success;
        }

        return $results;
    }

    /**
     * Formater le numéro de téléphone (format international)
     */
    protected function formatPhoneNumber(string $phone): string
    {
        // Supprimer les espaces et caractères spéciaux
        $phone = preg_replace('/[^0-9+]/', '', $phone);

        // Ajouter le préfixe international si manquant (Côte d'Ivoire par défaut)
        if (!str_starts_with($phone, '+')) {
            // Si commence par 0, remplacer par +225
            if (str_starts_with($phone, '0')) {
                $phone = '+225' . substr($phone, 1);
            } 
            // Si commence par 225, ajouter le +
            elseif (str_starts_with($phone, '225')) {
                $phone = '+' . $phone;
            }
            // Sinon, supposer Côte d'Ivoire
            else {
                $phone = '+225' . $phone;
            }
        }

        return $phone;
    }

    /**
     * Valider le numéro de téléphone
     */
    protected function validatePhoneNumber(string $phone): bool
    {
        // Doit commencer par + et avoir entre 10 et 15 chiffres
        return preg_match('/^\+[1-9][0-9]{9,14}$/', $phone);
    }

    /**
     * Envoyer via Twilio
     */
    protected function sendViaTwilio(string $phone, string $message): bool
    {
        $accountSid = $this->apiKey;
        $authToken = $this->apiSecret;
        $from = env('TWILIO_FROM_NUMBER', $this->senderId);

        $response = Http::withBasicAuth($accountSid, $authToken)
            ->asForm()
            ->post("https://api.twilio.com/2010-04-01/Accounts/{$accountSid}/Messages.json", [
                'From' => $from,
                'To' => $phone,
                'Body' => $message,
            ]);

        if ($response->successful()) {
            Log::info('SMS Twilio envoyé', ['phone' => $phone, 'sid' => $response->json('sid')]);
            return true;
        }

        Log::error('Erreur Twilio', ['response' => $response->json()]);
        return false;
    }

    /**
     * Envoyer via Africa's Talking
     */
    protected function sendViaAfricasTalking(string $phone, string $message): bool
    {
        $username = env('AT_USERNAME', 'sandbox');
        $apiKey = $this->apiKey;

        $response = Http::withHeaders([
            'apiKey' => $apiKey,
            'Content-Type' => 'application/x-www-form-urlencoded',
        ])->asForm()->post('https://api.africastalking.com/version1/messaging', [
            'username' => $username,
            'to' => $phone,
            'message' => $message,
            'from' => $this->senderId,
        ]);

        if ($response->successful() && str_contains($response->body(), 'Success')) {
            Log::info('SMS Africa\'s Talking envoyé', ['phone' => $phone]);
            return true;
        }

        Log::error('Erreur Africa\'s Talking', ['response' => $response->body()]);
        return false;
    }

    /**
     * Envoyer via Orange SMS API
     */
    protected function sendViaOrange(string $phone, string $message): bool
    {
        // Obtenir le token OAuth2
        $tokenResponse = Http::asForm()
            ->withBasicAuth($this->apiKey, $this->apiSecret)
            ->post('https://api.orange.com/oauth/v3/token', [
                'grant_type' => 'client_credentials',
            ]);

        if (!$tokenResponse->successful()) {
            Log::error('Erreur token Orange', ['response' => $tokenResponse->body()]);
            return false;
        }

        $accessToken = $tokenResponse->json('access_token');

        // Envoyer le SMS
        $response = Http::withToken($accessToken)
            ->post('https://api.orange.com/smsmessaging/v1/outbound/tel:+' . substr($phone, 1) . '/requests', [
                'outboundSMSMessageRequest' => [
                    'address' => 'tel:' . $phone,
                    'senderAddress' => 'tel:+' . env('ORANGE_SENDER_NUMBER'),
                    'outboundSMSTextMessage' => [
                        'message' => $message,
                    ],
                ],
            ]);

        if ($response->successful()) {
            Log::info('SMS Orange envoyé', ['phone' => $phone]);
            return true;
        }

        Log::error('Erreur Orange SMS', ['response' => $response->body()]);
        return false;
    }

    /**
     * Envoyer via Infobip
     */
    protected function sendViaInfobip(string $phone, string $message): bool
    {
        $baseUrl = env('INFOBIP_BASE_URL', 'https://api.infobip.com');

        $response = Http::withHeaders([
            'Authorization' => 'App ' . $this->apiKey,
            'Content-Type' => 'application/json',
        ])->post($baseUrl . '/sms/2/text/advanced', [
            'messages' => [
                [
                    'from' => $this->senderId,
                    'destinations' => [
                        ['to' => $phone],
                    ],
                    'text' => $message,
                ],
            ],
        ]);

        if ($response->successful()) {
            Log::info('SMS Infobip envoyé', ['phone' => $phone]);
            return true;
        }

        Log::error('Erreur Infobip', ['response' => $response->json()]);
        return false;
    }

    /**
     * Envoyer via Vonage (Nexmo)
     */
    protected function sendViaVonage(string $phone, string $message): bool
    {
        $response = Http::post('https://rest.nexmo.com/sms/json', [
            'api_key' => $this->apiKey,
            'api_secret' => $this->apiSecret,
            'from' => $this->senderId,
            'to' => ltrim($phone, '+'),
            'text' => $message,
        ]);

        $data = $response->json();
        
        if (isset($data['messages'][0]['status']) && $data['messages'][0]['status'] === '0') {
            Log::info('SMS Vonage envoyé', ['phone' => $phone, 'message_id' => $data['messages'][0]['message-id'] ?? null]);
            return true;
        }

        Log::error('Erreur Vonage', ['response' => $data]);
        return false;
    }

    /**
     * Logger le SMS (mode développement)
     */
    protected function logSms(string $phone, string $message): bool
    {
        Log::info('📱 SMS (mode log)', [
            'to' => $phone,
            'message' => $message,
            'sender' => $this->senderId,
        ]);

        return true;
    }

    /**
     * Obtenir le solde SMS (si supporté par le fournisseur)
     */
    public function getBalance(): ?float
    {
        try {
            return match($this->provider) {
                'africas_talking' => $this->getAfricasTalkingBalance(),
                'infobip' => $this->getInfobipBalance(),
                default => null,
            };
        } catch (\Exception $e) {
            Log::error('Erreur récupération solde SMS', ['error' => $e->getMessage()]);
            return null;
        }
    }

    protected function getAfricasTalkingBalance(): ?float
    {
        $username = env('AT_USERNAME', 'sandbox');
        
        $response = Http::withHeaders([
            'apiKey' => $this->apiKey,
        ])->get("https://api.africastalking.com/version1/user?username={$username}");

        if ($response->successful()) {
            return (float) ($response->json('UserData.balance') ?? 0);
        }

        return null;
    }

    protected function getInfobipBalance(): ?float
    {
        $baseUrl = env('INFOBIP_BASE_URL', 'https://api.infobip.com');

        $response = Http::withHeaders([
            'Authorization' => 'App ' . $this->apiKey,
        ])->get($baseUrl . '/account/1/balance');

        if ($response->successful()) {
            return (float) ($response->json('balance') ?? 0);
        }

        return null;
    }
}
