<?php

namespace App\Services;

use App\Models\SmsConfiguration;
use App\Models\SmsLog;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Log;

/**
 * SMSProvider
 * 
 * Handles direct API communication with Africa's Talking SMS gateway.
 * This service is responsible for making HTTP requests to the API
 * and handling responses.
 * 
 * Key Features:
 * - Sends SMS messages via Africa's Talking API
 * - Tests API connectivity
 * - Validates and formats phone numbers
 * - Handles API errors gracefully
 * 
 * @package App\Services
 */
class SMSProvider
{
    /**
     * Africa's Talking API endpoints.
     *
     * @var array
     */
    protected array $apiEndpoints = [
        'sandbox' => 'https://api.sandbox.africastalking.com/version1/messaging',
        'production' => 'https://api.africastalking.com/version1/messaging',
    ];

    /**
     * Configuration service instance.
     *
     * @var SmsConfigurationService
     */
    protected SmsConfigurationService $configService;

    /**
     * Create a new SMSProvider instance.
     *
     * @param SmsConfigurationService $configService
     */
    public function __construct(SmsConfigurationService $configService)
    {
        $this->configService = $configService;
    }

    /**
     * Send an SMS message to a single recipient.
     *
     * @param string $phoneNumber Recipient phone number
     * @param string $message Message content
     * @param SmsConfiguration|null $configuration Optional specific configuration
     * @return array Response with success status and details
     */
    public function sendSMS(string $phoneNumber, string $message, ?SmsConfiguration $configuration = null): array
    {
        $startTime = microtime(true);
        
        // Get configuration
        $config = $configuration ?? $this->configService->getConfiguration();
        
        if (!$config) {
            return [
                'success' => false,
                'message' => 'No active SMS configuration found. Please configure SMS settings.',
                'error_code' => 'NO_CONFIGURATION',
            ];
        }

        // Validate phone number
        $validatedPhone = $this->validatePhoneNumber($phoneNumber);
        if (!$validatedPhone['valid']) {
            return [
                'success' => false,
                'message' => $validatedPhone['error'],
                'error_code' => 'INVALID_PHONE',
            ];
        }

        // Format phone number
        $formattedPhone = $this->formatPhoneNumber($phoneNumber);

        // Create log entry
        $log = SmsLog::create([
            'recipient' => $formattedPhone,
            'message' => $message,
            'message_length' => strlen($message),
            'segment_count' => SmsLog::calculateSegments($message),
            'status' => SmsLog::STATUS_PENDING,
            'sms_configuration_id' => $config->id,
            'user_id' => auth()->id(),
        ]);

        try {
            // Make API request
            $response = $this->makeApiRequest($config, $formattedPhone, $message);
            
            $executionTime = round((microtime(true) - $startTime) * 1000);
            
            if ($response['success']) {
                // Update log with success
                $log->update([
                    'status' => SmsLog::STATUS_SENT,
                    'message_id' => $response['message_id'] ?? null,
                    'response_body' => $response['raw_response'] ?? null,
                    'execution_time_ms' => $executionTime,
                    'cost' => $response['cost'] ?? null,
                ]);

                Log::info('SMS sent successfully', [
                    'recipient' => $formattedPhone,
                    'message_id' => $response['message_id'] ?? null,
                    'execution_time_ms' => $executionTime,
                ]);

                return [
                    'success' => true,
                    'message' => 'SMS sent successfully',
                    'message_id' => $response['message_id'] ?? null,
                    'cost' => $response['cost'] ?? null,
                    'log_id' => $log->id,
                ];
            } else {
                // Update log with failure
                $log->update([
                    'status' => SmsLog::STATUS_FAILED,
                    'error_message' => $response['error'] ?? 'Unknown error',
                    'response_body' => $response['raw_response'] ?? null,
                    'execution_time_ms' => $executionTime,
                ]);

                Log::error('SMS failed', [
                    'recipient' => $formattedPhone,
                    'error' => $response['error'] ?? 'Unknown error',
                ]);

                return [
                    'success' => false,
                    'message' => $response['error'] ?? 'Failed to send SMS',
                    'error_code' => $response['error_code'] ?? 'API_ERROR',
                    'log_id' => $log->id,
                ];
            }

        } catch (\Exception $e) {
            $executionTime = round((microtime(true) - $startTime) * 1000);
            
            // Update log with exception
            $log->update([
                'status' => SmsLog::STATUS_FAILED,
                'error_message' => $e->getMessage(),
                'execution_time_ms' => $executionTime,
            ]);

            Log::error('SMS exception', [
                'recipient' => $formattedPhone,
                'error' => $e->getMessage(),
                'trace' => $e->getTraceAsString(),
            ]);

            return [
                'success' => false,
                'message' => 'An error occurred while sending SMS: ' . $e->getMessage(),
                'error_code' => 'EXCEPTION',
                'log_id' => $log->id,
            ];
        }
    }

    /**
     * Test API connectivity by sending a test SMS.
     *
     * @param string $testPhone Test phone number
     * @param string|null $testMessage Optional test message
     * @return array Test result
     */
    public function testConnection(string $testPhone, ?string $testMessage = null): array
    {
        $config = $this->configService->getConfiguration();
        
        if (!$config) {
            return [
                'success' => false,
                'message' => 'No SMS configuration found. Please save your configuration first.',
            ];
        }

        $message = $testMessage ?? 'This is a test message from SSHIA SMS Configuration. ' . now()->toDateTimeString();

        $result = $this->sendSMS($testPhone, $message, $config);

        return array_merge($result, [
            'configuration' => [
                'environment' => $config->environment,
                'sender_id' => $config->sender_id,
            ],
        ]);
    }

    /**
     * Make the API request to Africa's Talking.
     *
     * @param SmsConfiguration $config
     * @param string $phoneNumber
     * @param string $message
     * @return array
     */
    protected function makeApiRequest(SmsConfiguration $config, string $phoneNumber, string $message): array
    {
        $apiUrl = $config->getApiUrl();

        try {
            $response = Http::withHeaders([
                'apiKey' => $config->api_key,
                'Content-Type' => 'application/x-www-form-urlencoded',
                'Accept' => 'application/json',
            ])->asForm()->post($apiUrl, [
                'username' => $config->username,
                'to' => $phoneNumber,
                'message' => $message,
                'from' => $config->sender_id,
            ]);

            $rawResponse = $response->json();

            if ($response->successful()) {
                $data = $rawResponse['SMSMessageData'] ?? [];
                $recipients = $data['Recipients'] ?? [];

                if (!empty($recipients) && isset($recipients[0])) {
                    $recipient = $recipients[0];
                    
                    if (isset($recipient['status']) && $recipient['status'] === 'Success') {
                        return [
                            'success' => true,
                            'message_id' => $recipient['messageId'] ?? null,
                            'cost' => $recipient['cost'] ?? null,
                            'raw_response' => $rawResponse,
                        ];
                    } else {
                        return [
                            'success' => false,
                            'error' => $recipient['status'] ?? 'Unknown status',
                            'error_code' => $recipient['statusCode'] ?? 'UNKNOWN',
                            'raw_response' => $rawResponse,
                        ];
                    }
                }

                return [
                    'success' => false,
                    'error' => 'No recipient data in response',
                    'raw_response' => $rawResponse,
                ];
            }

            // Handle API error response
            $errorMessage = $rawResponse['SMSMessageData']['Message'] ?? 'API request failed';
            
            return [
                'success' => false,
                'error' => $errorMessage,
                'error_code' => 'API_ERROR',
                'raw_response' => $rawResponse,
            ];

        } catch (\Exception $e) {
            Log::error('Africa\'s Talking API request failed', [
                'error' => $e->getMessage(),
                'phone' => $phoneNumber,
            ]);

            return [
                'success' => false,
                'error' => 'Failed to connect to Africa\'s Talking API: ' . $e->getMessage(),
                'error_code' => 'CONNECTION_ERROR',
            ];
        }
    }

    /**
     * Validate a phone number format.
     *
     * @param string $phone
     * @return array
     */
    public function validatePhoneNumber(string $phone): array
    {
        // Remove spaces and dashes
        $phone = preg_replace('/[\s\-]/', '', $phone);

        // Check if empty
        if (empty($phone)) {
            return [
                'valid' => false,
                'error' => 'Phone number is required',
            ];
        }

        // Check for valid characters
        if (!preg_match('/^\+?[0-9]+$/', $phone)) {
            return [
                'valid' => false,
                'error' => 'Phone number contains invalid characters',
            ];
        }

        // Check length (should be between 9 and 15 digits)
        $digitsOnly = ltrim($phone, '+');
        if (strlen($digitsOnly) < 9 || strlen($digitsOnly) > 15) {
            return [
                'valid' => false,
                'error' => 'Phone number must be between 9 and 15 digits',
            ];
        }

        return [
            'valid' => true,
            'phone' => $phone,
        ];
    }

    /**
     * Format a phone number to international format.
     *
     * Supports South Sudan (+211), Uganda (+256), and Kenya (+254).
     * Defaults to South Sudan for local numbers without a country code,
     * since this platform is deployed in South Sudan.
     *
     * @param string $phone
     * @return string
     */
    public function formatPhoneNumber(string $phone): string
    {
        // Remove spaces, dashes, and parentheses
        $phone = preg_replace('/[\s\-\(\)]/', '', $phone);

        // Already in international format — return as-is
        if (str_starts_with($phone, '+')) {
            return $phone;
        }

        // 00-prefixed international format → replace 00 with +
        if (str_starts_with($phone, '00')) {
            return '+' . substr($phone, 2);
        }

        // South Sudan: 0XXXXXXXXX (10 digits total, local format) → +211XXXXXXXXX
        if (str_starts_with($phone, '0') && strlen($phone) === 10) {
            return '+211' . substr($phone, 1);
        }

        // South Sudan: 211XXXXXXXXX (12 digits, without +) → +211XXXXXXXXX
        if (str_starts_with($phone, '211') && strlen($phone) === 12) {
            return '+' . $phone;
        }

        // Uganda: 256XXXXXXXXX (without +) → +256XXXXXXXXX
        if (str_starts_with($phone, '256')) {
            return '+' . $phone;
        }

        // Kenya: 254XXXXXXXXX (without +) → +254XXXXXXXXX
        if (str_starts_with($phone, '254')) {
            return '+' . $phone;
        }

        // 9-digit bare number — assume South Sudan subscriber number
        if (strlen($phone) === 9) {
            return '+211' . $phone;
        }

        // Default: prepend + and hope for the best
        return '+' . $phone;
    }

    /**
     * Check if the API is reachable.
     *
     * @return array
     */
    public function checkApiStatus(): array
    {
        $config = $this->configService->getConfiguration();
        
        if (!$config) {
            return [
                'status' => 'not_configured',
                'message' => 'SMS is not configured',
            ];
        }

        try {
            $response = Http::timeout(5)
                ->get('https://api.africastalking.com');

            return [
                'status' => 'online',
                'message' => 'Africa\'s Talking API is reachable',
                'environment' => $config->environment,
            ];

        } catch (\Exception $e) {
            return [
                'status' => 'offline',
                'message' => 'Unable to reach Africa\'s Talking API',
                'error' => $e->getMessage(),
            ];
        }
    }
}
