<?php

namespace App\Modules\ThreeCX\Services;

use App\Modules\ThreeCX\Services\ThreeCXMakeCallService;
use App\Modules\ThreeCX\Services\ThreeCXCallTrackingService;
use App\Modules\ThreeCX\Services\ThreeCXAuthService;
use App\Models\Setting;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Log;

/**
 * 3CX Integration Service
 * 
 * Main service for integrating 3CX functionality into the application
 * Provides simplified interface for click-to-call and other 3CX features
 */
class ThreeCXIntegrationService
{
    protected $makeCallService;
    protected $callTrackingService;
    protected $authService;

    public function __construct(
        ThreeCXMakeCallService $makeCallService,
        ThreeCXCallTrackingService $callTrackingService,
        ThreeCXAuthService $authService
    ) {
        $this->makeCallService = $makeCallService;
        $this->callTrackingService = $callTrackingService;
        $this->authService = $authService;
    }

    /**
     * Initiate a call to a customer
     * This is the main method to be called from controllers/views
     * 
     * @param string $phoneNumber Customer phone number
     * @param array $options Additional options (timeout, etc.)
     * @return array Result with success status and details
     */
    public function makeCall(string $phoneNumber, array $options = []): array
    {
        // Use the phone number exactly as provided (don't auto-format)
        $cleanedNumber = trim($phoneNumber);
        
        if (!$this->isValidPhoneNumber($cleanedNumber)) {
            return [
                'success' => false,
                'error' => 'Invalid phone number',
                'message' => "Phone number '{$phoneNumber}' is not valid for calling"
            ];
        }

        // Set default timeout
        $timeout = $options['timeout'] ?? 30;

        Log::info('[3CX Integration] Click-to-call initiated', [
            'original_number' => $phoneNumber,
            'final_number' => $cleanedNumber,
            'number_changed' => $phoneNumber !== $cleanedNumber,
            'timeout' => $timeout
        ]);

        // Make the call using the make call service with the specified extension
        $extension = $options['extension'] ?? null;
        $result = $this->makeCallService->makeCall($cleanedNumber, $timeout, $extension);
        
        // Log the result for monitoring and start call tracking
        if ($result['success']) {
            Log::info('[3CX Integration] Click-to-call successful', [
                'phone_number' => $cleanedNumber,
                'participant_id' => $result['participant_id'] ?? null,
                'call_id' => $result['call_id'] ?? null
            ]);
            
            // Start real-time call tracking if we have the necessary IDs (skip if static display requested)
            if (isset($result['participant_id']) && isset($result['call_id'])) {
                // Check if static display is requested (no real-time tracking needed)
                $staticDisplay = $options['static_display'] ?? false;
                
                if ($staticDisplay) {
                    Log::info('[3CX Integration] Static display requested - skipping WebSocket and real-time tracking', [
                        'participant_id' => $result['participant_id'],
                        'call_id' => $result['call_id']
                    ]);
                    $result['websocket_started'] = false;
                    $result['tracking_started'] = false;
                } else {
                    // Start on-demand WebSocket listener for this call
                    try {
                        Log::info('[3CX Integration] Starting on-demand WebSocket for call', [
                            'participant_id' => $result['participant_id'],
                            'call_id' => $result['call_id']
                        ]);
                        
                        // Start WebSocket listener (smart start - only if needed)
                        $wsStarted = $this->smartStartWebSocket($result['participant_id']);
                        $result['websocket_started'] = $wsStarted;
                    } catch (\Exception $e) {
                        Log::warning('[3CX Integration] WebSocket failed to start', [
                            'participant_id' => $result['participant_id'],
                            'error' => $e->getMessage()
                        ]);
                        $result['websocket_started'] = false;
                    }

                    // Start monitoring asynchronously to avoid blocking the response
                    try {
                    $trackingStarted = $this->callTrackingService->startMonitoring(
                        $result['participant_id'],
                        $result['call_id'],
                        $cleanedNumber,
                        $options // Pass the additional context (task_id, user_id, extension)
                    );
                    $result['tracking_started'] = $trackingStarted;
                } catch (\Exception $e) {
                    Log::warning('[3CX Integration] Call tracking failed to start', [
                        'participant_id' => $result['participant_id'],
                        'error' => $e->getMessage()
                    ]);
                    $result['tracking_started'] = false;
                }
                
                    Log::info('[3CX Integration] Call tracking initiated', [
                        'participant_id' => $result['participant_id'],
                        'call_id' => $result['call_id'],
                        'tracking_started' => $result['tracking_started'] ?? false
                    ]);
                }
            }
        } else {
            Log::warning('[3CX Integration] Click-to-call failed', [
                'phone_number' => $cleanedNumber,
                'error' => $result['error'] ?? 'Unknown error',
                'message' => $result['message'] ?? 'No message provided'
            ]);
        }

        return $result;
    }

    /**
     * Check if 3CX integration is available
     * 
     * @return bool
     */
    public function isAvailable(): bool
    {
        $config = $this->makeCallService->getConfiguration();
        return $config['oauth_configured'] && $config['base_url_configured'];
    }

    /**
     * Get integration status for admin dashboard
     * 
     * @return array Status information
     */
    public function getStatus(): array
    {
        $config = $this->makeCallService->getConfiguration();
        
        return [
            'available' => $this->isAvailable(),
            'base_url' => $config['base_url'],
            'client_extension' => $config['client_extension'],
            'oauth_configured' => $config['oauth_configured'],
            'base_url_configured' => $config['base_url_configured']
        ];
    }

    /**
     * Test the integration
     * 
     * @param string $testNumber Optional test number
     * @return array Test results
     */
    public function testIntegration(string $testNumber = '07776668338'): array
    {
        return $this->makeCallService->testService($testNumber);
    }

    /**
     * Get call status for real-time monitoring
     * 
     * @param int $participantId
     * @return array|null
     */
    public function getCallStatus(int $participantId): ?array
    {
        return $this->callTrackingService->getCallStatus($participantId);
    }

    /**
     * Get all active call statuses for dashboard
     * 
     * @return array
     */
    public function getActiveCallStatuses(): array
    {
        return $this->callTrackingService->getActiveCallStatuses();
    }

    /**
     * Clean phone number for calling
     * 
     * @param string $phoneNumber
     * @return string
     */
    protected function cleanPhoneNumber(string $phoneNumber): string
    {
        // Return the number exactly as provided - no automatic formatting
        // This allows users to enter numbers in the exact format they want to call
        return trim($phoneNumber);
    }

    /**
     * Validate phone number
     * 
     * @param string $phoneNumber
     * @return bool
     */
    protected function isValidPhoneNumber(string $phoneNumber): bool
    {
        // Basic validation - at least 7 digits (allow shorter numbers for testing)
        $digitsOnly = preg_replace('/[^\d]/', '', $phoneNumber);
        if (strlen($digitsOnly) < 7) {
            return false;
        }

        // Allow any reasonable phone number format (be more permissive)
        $patterns = [
            '/^\+?\d{7,15}$/',     // International or local: +xxxxxxx to +xxxxxxxxxxxxxxx or xxxxxxx to xxxxxxxxxxxxxxx
            '/^0\d{6,10}$/',       // UK Local: 0xxxxxxx to 0xxxxxxxxxx
            '/^\d{7,11}$/'         // Generic: xxxxxxx to xxxxxxxxxxx
        ];

        // Remove common formatting characters for validation
        $cleanForValidation = preg_replace('/[\s\-\(\)\.]/', '', $phoneNumber);

        foreach ($patterns as $pattern) {
            if (preg_match($pattern, $cleanForValidation)) {
                return true;
            }
        }

        return false;
    }

    /**
     * Format phone number for display
     * 
     * @param string $phoneNumber
     * @return string
     */
    public function formatPhoneNumber(string $phoneNumber): string
    {
        $cleaned = $this->cleanPhoneNumber($phoneNumber);
        
        // UK mobile numbers
        if (preg_match('/^\+447(\d{3})(\d{3})(\d{3})$/', $cleaned, $matches)) {
            return '+44 7' . $matches[1] . ' ' . $matches[2] . ' ' . $matches[3];
        }
        
        // UK landline numbers  
        if (preg_match('/^\+44(\d{4})(\d{6})$/', $cleaned, $matches)) {
            return '+44 ' . $matches[1] . ' ' . $matches[2];
        }
        
        // Default formatting
        return $cleaned;
    }
    
    /**
     * Hangup an active call
     * 
     * @param int $participantId
     * @return array
     */
    public function hangupCall(int $participantId): array
    {
        // Simplified flow: hangup not supported
        return [
            'success' => false,
            'message' => 'Hangup not supported in simplified flow',
            'participant_id' => $participantId
        ];
    }
    
    /**
     * Smart start WebSocket - only starts if needed
     *
     * @param int $participantId
     * @return bool Success status
     */
    protected function smartStartWebSocket(int $participantId): bool
    {
        try {
            // Check if WebSocket is already running
            $statusCmd = new \Symfony\Component\Process\Process(['php', base_path('artisan'), 'threecx:ws', 'status'], base_path());
            $statusCmd->setTimeout(10);
            $statusCmd->run();

            if (strpos($statusCmd->getOutput(), 'Listener is running') !== false) {
                Log::info('[3CX Integration] WebSocket already running - no need to start', ['participant_id' => $participantId]);
                return true;
            }

            // Check if there will be active calls (current + this new one)
            $activeCalls = \Cache::get('threecx_active_calls', []);
            
            Log::info('[3CX Integration] Starting WebSocket for first call', [
                'participant_id' => $participantId,
                'current_active_calls' => count($activeCalls)
            ]);

            return $this->startWebSocketForCall($participantId);

        } catch (\Exception $e) {
            Log::error('[3CX Integration] Smart WebSocket start failed', [
                'participant_id' => $participantId,
                'error' => $e->getMessage()
            ]);
            return false;
        }
    }

    /**
     * Start WebSocket listener for a specific call
     *
     * @param int $participantId
     * @return bool Success status
     */
    protected function startWebSocketForCall(int $participantId): bool
    {
        try {
            $artisan = base_path('artisan');
            $php = 'php'; // Use standard PHP CLI instead of PHP_BINARY which may point to php-fpm
            $cwd = base_path();

            // 1) Check status first
            $statusCmd = new \Symfony\Component\Process\Process([$php, $artisan, 'threecx:ws', 'status'], $cwd);
            $statusCmd->setTimeout(10);
            $statusCmd->run();

            \Log::info('[3CX Integration] WS status check', [
                'participant_id' => $participantId,
                'exit_code' => $statusCmd->getExitCode(),
                'stdout' => $statusCmd->getOutput(),
                'stderr' => $statusCmd->getErrorOutput()
            ]);

            if (strpos($statusCmd->getOutput(), 'Listener is running') !== false) {
                \Log::info('[3CX Integration] WebSocket already running', ['participant_id' => $participantId]);
                // ensure cache marker exists
                try {
                    \Illuminate\Support\Facades\Cache::put('threecx_websocket_for_participant', $participantId, 3600);
                } catch (\Throwable $t) {
                    \Log::warning('[3CX Integration] Cache write failed while marking websocket participant', ['error' => $t->getMessage()]);
                }
                return true;
            }

            // 2) Try starting normally (blocking run) but capture all outputs
            $startCmd = new \Symfony\Component\Process\Process([$php, $artisan, 'threecx:ws', 'start'], $cwd);
            $startCmd->setTimeout(0); // no timeout for daemon bootstrap
            $startCmd->run();

            \Log::info('[3CX Integration] Tried starting WS (blocking)', [
                'participant_id' => $participantId,
                'exit_code' => $startCmd->getExitCode(),
                'stdout' => $startCmd->getOutput(),
                'stderr' => $startCmd->getErrorOutput()
            ]);

            // If successful and status says running, return true
            $statusCmd2 = new \Symfony\Component\Process\Process([$php, $artisan, 'threecx:ws', 'status'], $cwd);
            $statusCmd2->setTimeout(10);
            $statusCmd2->run();

            if (strpos($statusCmd2->getOutput(), 'Listener is running') !== false) {
                \Log::info('[3CX Integration] WebSocket started successfully for call', [
                    'participant_id' => $participantId,
                    'output' => $statusCmd2->getOutput()
                ]);
                \Illuminate\Support\Facades\Cache::put('threecx_websocket_for_participant', $participantId, 3600);
                return true;
            }

            // 3) If blocking start didn't produce a running listener, try detached nohup fallback
            \Log::warning('[3CX Integration] Blocking start did not report running - trying detached fallback', ['participant_id' => $participantId]);

            // Create a logfile and pidfile path
            $logfile = storage_path('logs/threecx_ws_nohup.log');
            $pidfile = storage_path('threecx_ws.pid');

            $nohupCmd = sprintf(
                'nohup %s %s threecx:ws start > %s 2>&1 & echo $! > %s',
                escapeshellarg($php),
                escapeshellarg($artisan),
                escapeshellarg($logfile),
                escapeshellarg($pidfile)
            );

            $nohupProc = \Symfony\Component\Process\Process::fromShellCommandline($nohupCmd, $cwd);
            $nohupProc->setTimeout(30);
            $nohupProc->run();

            \Log::info('[3CX Integration] Detached start attempted', [
                'participant_id' => $participantId,
                'exit_code' => $nohupProc->getExitCode(),
                'stdout' => $nohupProc->getOutput(),
                'stderr' => $nohupProc->getErrorOutput(),
                'logfile' => $logfile,
                'pidfile' => $pidfile
            ]);

            // Wait a short moment and re-check status
            sleep(1);

            $statusCmd3 = new \Symfony\Component\Process\Process([$php, $artisan, 'threecx:ws', 'status'], $cwd);
            $statusCmd3->setTimeout(10);
            $statusCmd3->run();

            \Log::info('[3CX Integration] WS status after detached start', [
                'participant_id' => $participantId,
                'exit_code' => $statusCmd3->getExitCode(),
                'stdout' => $statusCmd3->getOutput(),
                'stderr' => $statusCmd3->getErrorOutput()
            ]);

            if (strpos($statusCmd3->getOutput(), 'Listener is running') !== false) {
                try {
                    \Illuminate\Support\Facades\Cache::put('threecx_websocket_for_participant', $participantId, 3600);
                } catch (\Throwable $t) {
                    \Log::warning('[3CX Integration] Cache write failed while marking websocket participant', ['error' => $t->getMessage()]);
                }
                return true;
            }

            \Log::error('[3CX Integration] Failed to start WebSocket after attempts', [
                'participant_id' => $participantId
            ]);

            return false;
        } catch (\Throwable $e) {
            \Log::error('[3CX Integration] Exception starting WebSocket', [
                'participant_id' => $participantId,
                'error' => $e->getMessage(),
                'trace' => $e->getTraceAsString()
            ]);
            return false;
        }
    }
}
