<?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();
    }

    /**
     * Send SMS message with country-based routing.
     *
     * PRC-621 — sms_messages log'u burada MERKEZÎ: her gönderim (client,
     * admin bildirimi, test, doğrulama) tek noktadan kayda düşer. Client
     * tarafında SendClientSmsJob client_id geçirir; admin/test'te null kalır.
     */
    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;
        }
    }

    /**
     * SMS fiziksel gönderiminden SONRA log/DB hatası yukarı kabarmasın:
     * aksi halde gönderim kaybedilmiş sayılır + retry çift-gönderim üretir
     * (SendEmailJob::logEmailToDatabase deseni).
     */
    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(),
            ]);
        }
    }

    /**
     * Send SMS via a specific provider (bypass routing)
     */
    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;
        }
    }

    /**
     * Resolve the best provider based on country routing or default
     */
    private function resolveProvider(string $to, ?string $countryCode = null): SmsInterface
    {
        // If country code provided, try country-based routing
        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(),
                        ]);
                    }
                }
            }
        }

        // Try to detect country from phone number prefix
        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(),
                            ]);
                        }
                    }
                }
            }
        }

        // Fallback to default provider
        $defaultProvider = Setting::getValue('sms_default_provider',
            Setting::getValue('sms_provider', 'twilio')
        );

        if ($this->manager->hasProvider($defaultProvider)) {
            return $this->manager->getProviderInstance($defaultProvider);
        }

        // Last resort: try twilio
        return $this->manager->getProviderInstance('twilio');
    }

    /**
     * Detect country code from phone number prefix
     */
    private function detectCountryFromPhone(string $phone): ?string
    {
        $phone = preg_replace('/[^0-9+]/', '', $phone);

        // Common prefixes mapping
        $prefixMap = [
            '90' => 'TR',   // Turkey
            '1' => 'US',    // USA/Canada
            '44' => 'GB',   // UK
            '49' => 'DE',   // Germany
            '33' => 'FR',   // France
            '39' => 'IT',   // Italy
            '34' => 'ES',   // Spain
            '31' => 'NL',   // Netherlands
            '46' => 'SE',   // Sweden
            '47' => 'NO',   // Norway
            '45' => 'DK',   // Denmark
            '358' => 'FI',  // Finland
            '48' => 'PL',   // Poland
            '7' => 'RU',    // Russia
            '81' => 'JP',   // Japan
            '82' => 'KR',   // South Korea
            '86' => 'CN',   // China
            '91' => 'IN',   // India
            '61' => 'AU',   // Australia
            '55' => 'BR',   // Brazil
            '27' => 'ZA',   // South Africa
            '971' => 'AE',  // UAE
            '966' => 'SA',  // Saudi Arabia
        ];

        // Remove + prefix
        $cleanPhone = ltrim($phone, '+');

        // Try 3-digit prefix first, then 2, then 1
        foreach ([3, 2, 1] as $length) {
            $prefix = substr($cleanPhone, 0, $length);
            if (isset($prefixMap[$prefix])) {
                return $prefixMap[$prefix];
            }
        }

        return null;
    }

    /**
     * Get available SMS providers
     */
    public function getAvailableProviders(): array
    {
        return $this->manager->getAvailableProviders();
    }

    /**
     * Get the SMS Manager instance
     */
    public function getManager(): SmsManager
    {
        return $this->manager;
    }
}
