<?php
if (!defined('ABSPATH')) exit;

/**
 * VMO Twilio Enhanced Client
 * 
 * Provides direct calling and messaging capabilities for the
 * Realtime Call Manager and AI Assistant. Replaces missing file functionality.
 */
class VMO_Twilio_Enhanced_Client {
    
    private $account_sid;
    private $auth_token;
    private $api_base = 'https://api.twilio.com/2010-04-01';
    
    public function __construct() {
        // Load credentials similar to Manager
        $this->account_sid = defined('VMO_TWILIO_ACCOUNT_SID') ? VMO_TWILIO_ACCOUNT_SID : get_option('vmo_twilio_account_sid');
        $this->auth_token = defined('VMO_TWILIO_AUTH_TOKEN') ? VMO_TWILIO_AUTH_TOKEN : get_option('vmo_twilio_auth_token');
    }

    /**
     * Get the current user's business phone number
     */
    private function get_from_number() {
        if (class_exists('VMO_Twilio_Manager')) {
            return VMO_Twilio_Manager::get_instance()->get_user_phone(get_current_user_id());
        }
        return false;
    }

    /**
     * Make an outbound call
     * 
     * @param string $to Destination number (passes through international formats like +234...)
     * @return array|WP_Error Twilio Call Object or Error
     */
    public function make_outbound_call($to) {
        if (!$this->account_sid || !$this->auth_token) {
            return new WP_Error('twilio_missing', 'Twilio credentials not configured.');
        }

        $from = $this->get_from_number();
        if (!$from) {
            return new WP_Error('no_phone', 'No business phone number found for this user.');
        }

        // URL that Twilio will request when the call connects
        // Using Conference mode creates a holding room that the Agent (User) can join via the Dashboard
        $user_id = get_current_user_id();
        $conference_name = 'vmo_call_' . $user_id . '_' . time();
        
        // Encode parameters for the TwiML generator.
        // Use the allowlisted admin-ajax gateway to avoid edge/WAF blocks.
        $manager = class_exists('VMO_Twilio_Manager') ? VMO_Twilio_Manager::get_instance() : null;
        if ($manager && method_exists($manager, 'phone_gateway_url')) {
            $callback_url = $manager->phone_gateway_url('pd_twiml', array(
                'twiml_action' => 'conference',
                'conference' => $conference_name,
                'user_id' => $user_id,
            ));
            $status_callback_url = $manager->phone_gateway_url('dial_status');
        } else {
            // Fallback (may be blocked on some hosts)
            $callback_url = add_query_arg(array(
                'vmo_twilio' => 'power_dialer_twiml',
                'twiml_action' => 'conference',
                'conference' => $conference_name,
                'user_id' => $user_id,
            ), home_url('/'));
            $status_callback_url = add_query_arg(array(
                'vmo_twilio' => 'dial_status',
            ), home_url('/'));
        }

        $args = array(
            'To' => $to,
            'From' => $from,
            'Url' => $callback_url,
            'StatusCallback' => $status_callback_url,
            'StatusCallbackEvent' => array('initiated', 'ringing', 'answered', 'completed')
        );

        // Enable call recording (defaults ON if never explicitly toggled)
        $rec_meta = get_user_meta($user_id, 'call_recording_enabled', true);
        $recording_enabled = ($rec_meta === '' || $rec_meta === false) ? true : (bool) intval($rec_meta);
        if ($recording_enabled) {
            $args['Record'] = 'true';
            $manager = class_exists('VMO_Twilio_Manager') ? VMO_Twilio_Manager::get_instance() : null;
            if ($manager && method_exists($manager, 'phone_gateway_url')) {
                $args['RecordingStatusCallback'] = $manager->phone_gateway_url('recording');
            }
        }

        $result = $this->request('POST', '/Calls.json', $args);

        // If the user has an external phone configured, call it into the same conference
        // so there is a two-way audio path (prevents the callee hearing hold music forever).
        $external_phone = get_user_meta($user_id, 'vmo_external_phone', true);
        $external_phone = is_string($external_phone) ? trim($external_phone) : '';
        if (!is_wp_error($result) && !empty($external_phone)) {
            // Don't double-dial if the destination IS the external phone —
            // that causes carrier conflicts ("voicemail not set up" errors).
            $clean_to  = preg_replace('/\D/', '', $to);
            $clean_ext = preg_replace('/\D/', '', $external_phone);
            if ($clean_to !== $clean_ext && !str_ends_with($clean_to, $clean_ext) && !str_ends_with($clean_ext, $clean_to)) {
                try {
                    $this->request('POST', '/Calls.json', array(
                        'To' => $external_phone,
                        'From' => $from,
                        'Url' => $callback_url,
                        'StatusCallback' => $status_callback_url,
                        'StatusCallbackEvent' => array('initiated', 'ringing', 'answered', 'completed')
                    ));
                } catch (Throwable $e) {
                    // Non-fatal: the outbound leg can still proceed; user can manually join later.
                }
            }
        }

        // Attach conference name so the browser can join directly
        if (!is_wp_error($result) && is_array($result)) {
            $result['conference_name'] = $conference_name;
        }

        // Log the call in the database so the frontend can track it
        if (!is_wp_error($result) && isset($result['sid'])) {
            global $wpdb;
            $table_name = $wpdb->prefix . 'vmo_power_dialer_calls';
            
            // Check if table exists (silently fail insert if not, to avoid crashing)
            // But we assume it exists as the Manager uses it.
            // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
            $wpdb->insert($table_name, array(
                'user_id' => get_current_user_id(),
                'campaign_id' => 0, // Direct call (no campaign)
                'phone' => $to,
                'call_sid' => $result['sid'],
                'status' => $result['status'] ?? 'initiated',
                'created_at' => current_time('mysql'),
                'updated_at' => current_time('mysql')
            ));

            // Also log into phone calls table for unified analytics/history.
            $phone_calls_table = $wpdb->prefix . 'vmo_phone_calls';
            if ($wpdb->get_var("SHOW TABLES LIKE '{$phone_calls_table}'") === $phone_calls_table) {
                // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
                $wpdb->insert(
                    $phone_calls_table,
                    array(
                        'user_id' => $user_id,
                        'call_sid' => $result['sid'],
                        'caller_number' => $to,
                        'direction' => 'outbound',
                        'status' => $result['status'] ?? 'initiated',
                        'ai_handled' => 0,
                        'created_at' => current_time('mysql')
                    ),
                    array('%d', '%s', '%s', '%s', '%s', '%d', '%s')
                );
            }
        }

        return $result;
    }

    /**
     * Send an SMS message
     * 
     * @param string $to Destination number
     * @param string $message Message body
     * @param array $options Optional send configuration
     * @return array|WP_Error Twilio Message Object or Error
     */
    public function send_sms($to, $message, array $options = array()) {
        if (!$this->account_sid || !$this->auth_token) {
            return new WP_Error('twilio_missing', 'Twilio credentials not configured.');
        }

        $from = $this->get_from_number();
        if (!$from) {
            return new WP_Error('no_phone', 'No business phone number found for this user.');
        }

        // ✅ TOKEN CHECK: Verify balance before sending SMS
        $user_id = get_current_user_id();
        if ($user_id && class_exists('VMO_Twilio_Token_Economics')) {
            $econ = VMO_Twilio_Token_Economics::get_instance();
            if (!$econ->can_send_sms($user_id)) {
                return new WP_Error('insufficient_tokens', 'Insufficient tokens to send SMS.');
            }
        }

        $args = array(
            'To' => $to,
            'From' => $from,
            'Body' => $message,
        );

        if (!empty($options['track_status'])) {
            $manager = class_exists('VMO_Twilio_Manager') ? VMO_Twilio_Manager::get_instance() : null;
            if ($manager && method_exists($manager, 'phone_gateway_url')) {
                $query_args = (!empty($options['status_query_args']) && is_array($options['status_query_args']))
                    ? $options['status_query_args']
                    : array();
                $args['StatusCallback'] = $manager->phone_gateway_url('status', $query_args);
            } else {
                $args['StatusCallback'] = add_query_arg(array('vmo_twilio' => 'status_callback'), home_url('/'));
            }
        }

        $result = $this->request('POST', '/Messages.json', $args);

        // ✅ TOKEN DEDUCTION: Charge for outbound SMS after successful send
        if (!is_wp_error($result) && $user_id && class_exists('VMO_Twilio_Token_Economics')) {
            VMO_Twilio_Token_Economics::get_instance()->deduct_sms_outbound($user_id);
        }

        return $result;
    }

    /**
     * Provision a business phone number by delegating to the Twilio manager.
     */
    public function provision_business_phone_number($user_id, array $responses = array(), array $options = array()) {
        if (!class_exists('VMO_Twilio_Manager')) {
            return array(
                'success' => false,
                'error' => 'Twilio manager is not available.'
            );
        }

        return VMO_Twilio_Manager::get_instance()->provision_business_phone_number($user_id, $responses, $options);
    }

    /**
     * Return all active phone numbers for the provided user.
     */
    public function get_all_active_phone_numbers($user_id) {
        if (!class_exists('VMO_Twilio_Manager')) {
            return array();
        }

        return VMO_Twilio_Manager::get_instance()->get_all_active_phone_numbers($user_id);
    }

    /**
     * Perform Twilio API Request
     */
    private function request($method, $endpoint, $args = array()) {
        $url = $this->api_base . '/Accounts/' . $this->account_sid . $endpoint;
        
        $request_args = array(
            'headers' => array(
                'Authorization' => 'Basic ' . base64_encode($this->account_sid . ':' . $this->auth_token) // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode -- HTTP Basic auth header
            ),
            'timeout' => 30
        );

        if ($method === 'GET') {
            if (!empty($args)) {
                $url = add_query_arg($args, $url);
            }
            $response = wp_remote_get($url, $request_args);
        } else {
            $request_args['body'] = $args;
            $response = wp_remote_post($url, $request_args);
        }

        if (is_wp_error($response)) {
            return $response;
        }

        $code = wp_remote_retrieve_response_code($response);
        $body = wp_remote_retrieve_body($response);
        $data = json_decode($body, true);

        if ($code >= 400) {
            $msg = isset($data['message']) ? $data['message'] : 'Twilio API Error';
            return new WP_Error('twilio_error', $msg, array('status' => $code));
        }

        return $data;
    }
}
