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

/**
 * VMO Bulk SMS Marketing
 *
 * Campaign creation, audience selection, scheduling, sending (via Twilio),
 * and analytics for bulk SMS marketing campaigns.
 *
 * @package VMO_AI_Website_Builder
 * @since   1.0.0
 */
class VMO_Bulk_SMS_Marketing {

    private static $instance = null;

    public static function get_instance() {
        if ( null === self::$instance ) {
            self::$instance = new self();
        }
        return self::$instance;
    }

    public function init() {
        // AJAX handlers
        add_action( 'wp_ajax_vmo_sms_campaign_create',      [ $this, 'ajax_create_campaign' ] );
        add_action( 'wp_ajax_vmo_sms_campaign_list',        [ $this, 'ajax_list_campaigns' ] );
        add_action( 'wp_ajax_vmo_sms_campaign_send',        [ $this, 'ajax_send_campaign' ] );
        add_action( 'wp_ajax_vmo_sms_campaign_schedule',    [ $this, 'ajax_schedule_campaign' ] );
        add_action( 'wp_ajax_vmo_sms_campaign_stats',       [ $this, 'ajax_campaign_stats' ] );
        add_action( 'wp_ajax_vmo_sms_campaign_delete',      [ $this, 'ajax_delete_campaign' ] );
        add_action( 'wp_ajax_vmo_sms_campaign_preview',     [ $this, 'ajax_preview_cost' ] );
        add_action( 'wp_ajax_vmo_sms_campaign_audience',    [ $this, 'ajax_get_audience' ] );
        add_action( 'wp_ajax_vmo_sms_campaign_ai_compose',  [ $this, 'ajax_ai_compose' ] );

        // Cron for scheduled campaigns
        add_action( 'vmo_sms_campaign_cron', [ $this, 'process_scheduled_campaigns' ] );
        if ( ! wp_next_scheduled( 'vmo_sms_campaign_cron' ) ) {
            wp_schedule_event( time(), 'five_minutes', 'vmo_sms_campaign_cron' );
        }

        $this->ensure_tables();
    }

    /* ═══════════════════════════════════════
       DATABASE
    ═══════════════════════════════════════ */

    private function ensure_tables() {
        global $wpdb;
        $charset    = $wpdb->get_charset_collate();
        $campaigns  = $wpdb->prefix . 'vmo_sms_campaigns';
        $recipients = $wpdb->prefix . 'vmo_sms_campaign_recipients';

        if ( $wpdb->get_var( "SHOW TABLES LIKE '{$campaigns}'" ) !== $campaigns ) {
            $wpdb->query( "CREATE TABLE {$campaigns} (
                id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
                user_id BIGINT UNSIGNED NOT NULL,
                name VARCHAR(255) NOT NULL,
                message TEXT NOT NULL,
                audience_filter VARCHAR(50) DEFAULT 'all_sms_subscribed',
                audience_tags TEXT,
                total_recipients INT UNSIGNED DEFAULT 0,
                sent_count INT UNSIGNED DEFAULT 0,
                delivered_count INT UNSIGNED DEFAULT 0,
                failed_count INT UNSIGNED DEFAULT 0,
                reply_count INT UNSIGNED DEFAULT 0,
                opt_out_count INT UNSIGNED DEFAULT 0,
                status VARCHAR(30) DEFAULT 'draft',
                scheduled_at DATETIME NULL,
                started_at DATETIME NULL,
                completed_at DATETIME NULL,
                created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
                updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
                INDEX idx_user_status (user_id, status),
                INDEX idx_scheduled (status, scheduled_at)
            ) {$charset}" );
        }

        if ( $wpdb->get_var( "SHOW TABLES LIKE '{$recipients}'" ) !== $recipients ) {
            $wpdb->query( "CREATE TABLE {$recipients} (
                id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
                campaign_id BIGINT UNSIGNED NOT NULL,
                contact_id VARCHAR(64),
                phone VARCHAR(30) NOT NULL,
                contact_name VARCHAR(255),
                status VARCHAR(20) DEFAULT 'pending',
                twilio_sid VARCHAR(100),
                sent_at DATETIME NULL,
                delivered_at DATETIME NULL,
                error_message TEXT,
                INDEX idx_campaign (campaign_id),
                INDEX idx_status (campaign_id, status)
            ) {$charset}" );
        }
    }

    /* ═══════════════════════════════════════
       AJAX: Create / Save Campaign
    ═══════════════════════════════════════ */

    public function ajax_create_campaign() {
        if ( ! current_user_can( 'read' ) ) wp_send_json_error( [ 'message' => 'Unauthorized' ], 403 );

        $user_id = get_current_user_id();
        $name    = sanitize_text_field( wp_unslash( $_POST['name'] ?? '' ) );
        $message = sanitize_textarea_field( wp_unslash( $_POST['message'] ?? '' ) );
        $filter  = sanitize_key( wp_unslash( $_POST['audience_filter'] ?? 'all_sms_subscribed' ) );
        $tags    = sanitize_text_field( wp_unslash( $_POST['audience_tags'] ?? '' ) );

        if ( ! $name || ! $message ) {
            wp_send_json_error( [ 'message' => 'Campaign name and message are required.' ] );
        }

        if ( mb_strlen( $message ) > 1600 ) {
            wp_send_json_error( [ 'message' => 'Message exceeds 1600 character limit.' ] );
        }

        global $wpdb;
        $table = $wpdb->prefix . 'vmo_sms_campaigns';

        $wpdb->insert( $table, [
            'user_id'         => $user_id,
            'name'            => $name,
            'message'         => $message,
            'audience_filter' => $filter,
            'audience_tags'   => $tags,
            'status'          => 'draft',
        ] );

        $campaign_id = $wpdb->insert_id;
        if ( ! $campaign_id ) {
            wp_send_json_error( [ 'message' => 'Failed to create campaign.' ] );
        }

        // Populate recipients
        $count = $this->populate_recipients( $campaign_id, $user_id, $filter, $tags );
        $wpdb->update( $table, [ 'total_recipients' => $count ], [ 'id' => $campaign_id ] );

        wp_send_json_success( [
            'campaign_id'      => $campaign_id,
            'total_recipients' => $count,
            'message'          => "Campaign created with {$count} recipients.",
        ] );
    }

    /* ═══════════════════════════════════════
       AJAX: List Campaigns
    ═══════════════════════════════════════ */

    public function ajax_list_campaigns() {
        if ( ! current_user_can( 'read' ) ) wp_send_json_error( [ 'message' => 'Unauthorized' ], 403 );

        global $wpdb;
        $table   = $wpdb->prefix . 'vmo_sms_campaigns';
        $user_id = get_current_user_id();

        $campaigns = $wpdb->get_results( $wpdb->prepare(
            "SELECT id, name, message, audience_filter, total_recipients, sent_count, delivered_count, failed_count, reply_count, opt_out_count, status, scheduled_at, started_at, completed_at, created_at FROM {$table} WHERE user_id = %d ORDER BY created_at DESC LIMIT 50",
            $user_id
        ), ARRAY_A );

        wp_send_json_success( [ 'campaigns' => $campaigns ?: [] ] );
    }

    /* ═══════════════════════════════════════
       AJAX: Get Audience Preview
    ═══════════════════════════════════════ */

    public function ajax_get_audience() {
        if ( ! current_user_can( 'read' ) ) wp_send_json_error( [ 'message' => 'Unauthorized' ], 403 );

        $user_id = get_current_user_id();
        $filter  = sanitize_key( wp_unslash( $_POST['audience_filter'] ?? 'all_sms_subscribed' ) );
        $tags    = sanitize_text_field( wp_unslash( $_POST['audience_tags'] ?? '' ) );

        $contacts = $this->get_audience_contacts( $user_id, $filter, $tags );

        wp_send_json_success( [
            'count'    => count( $contacts ),
            'contacts' => array_slice( $contacts, 0, 20 ), // preview first 20
        ] );
    }

    /* ═══════════════════════════════════════
       AJAX: Preview Cost
    ═══════════════════════════════════════ */

    public function ajax_preview_cost() {
        if ( ! current_user_can( 'read' ) ) wp_send_json_error( [ 'message' => 'Unauthorized' ], 403 );

        $user_id    = get_current_user_id();
        $filter     = sanitize_key( wp_unslash( $_POST['audience_filter'] ?? 'all_sms_subscribed' ) );
        $tags       = sanitize_text_field( wp_unslash( $_POST['audience_tags'] ?? '' ) );
        $msg_length = (int) ( $_POST['message_length'] ?? 160 );

        $contacts      = $this->get_audience_contacts( $user_id, $filter, $tags );
        $count         = count( $contacts );
        $segments      = max( 1, ceil( $msg_length / 160 ) );
        $cost_per_sms  = 0.0079; // Twilio US rate (approximate)
        $total_cost    = round( $count * $segments * $cost_per_sms, 2 );

        // Token check
        $has_tokens = true;
        if ( class_exists( 'VMO_Twilio_Token_Economics' ) ) {
            $econ = VMO_Twilio_Token_Economics::get_instance();
            $has_tokens = $econ->can_send_sms( $user_id );
        }

        // SMS activation check
        $sms_ready = false;
        if ( function_exists( 'vmo_get_sms_activation_snapshot' ) ) {
            $snap = vmo_get_sms_activation_snapshot( $user_id, false );
            $sms_ready = ! empty( $snap['bulk_marketing_ready'] );
        }

        wp_send_json_success( [
            'recipient_count' => $count,
            'segments'        => $segments,
            'estimated_cost'  => $total_cost,
            'has_tokens'      => $has_tokens,
            'sms_ready'       => $sms_ready,
            'readiness'       => $sms_ready && $has_tokens && $count > 0,
        ] );
    }

    /* ═══════════════════════════════════════
       AJAX: AI Compose Message
    ═══════════════════════════════════════ */

    public function ajax_ai_compose() {
        if ( ! current_user_can( 'read' ) ) wp_send_json_error( [ 'message' => 'Unauthorized' ], 403 );

        $purpose = sanitize_text_field( wp_unslash( $_POST['purpose'] ?? '' ) );
        $tone    = sanitize_text_field( wp_unslash( $_POST['tone'] ?? 'professional' ) );
        $user_id = get_current_user_id();

        $biz_name = get_user_meta( $user_id, 'vmo_business_name', true ) ?: get_bloginfo( 'name' );
        $biz_type = get_user_meta( $user_id, 'vmo_business_type', true ) ?: 'business';

        $prompt = "Write a concise SMS marketing message (max 160 characters preferred, max 320 absolute) for a {$biz_type} called \"{$biz_name}\".
Purpose: {$purpose}
Tone: {$tone}

Rules:
- Must include a clear call-to-action
- Must be compliant with TCPA/A2P regulations (no deceptive content)
- End with: Reply STOP to opt out
- Return ONLY the message text, nothing else";

        $api_key = get_option( 'vmo_openai_api_key', '' );
        if ( ! $api_key ) {
            wp_send_json_error( [ 'message' => 'OpenAI API key not configured.' ] );
        }

        $model = function_exists('vmo_get_model') ? vmo_get_model('mini') : (defined('VMO_MODEL_MINI') ? VMO_MODEL_MINI : 'gpt-5.5-mini');
        $response = wp_remote_post( 'https://api.openai.com/v1/chat/completions', [
            'timeout' => 30,
            'headers' => [
                'Authorization' => 'Bearer ' . $api_key,
                'Content-Type'  => 'application/json',
            ],
            'body' => wp_json_encode( array_merge( [
                'model'       => $model,
                'messages'    => [
                    [ 'role' => 'system', 'content' => 'You are a professional SMS marketing copywriter. Write compliant, effective messages.' ],
                    [ 'role' => 'user', 'content' => $prompt ],
                ],
                'temperature' => 0.7,
            ], function_exists('vmo_get_token_param') ? vmo_get_token_param($model, 200) : [ 'max_completion_tokens' => 200 ] ) ),
        ] );

        if ( is_wp_error( $response ) ) {
            wp_send_json_error( [ 'message' => 'AI generation failed.' ] );
        }

        $body = json_decode( wp_remote_retrieve_body( $response ), true );
        $text = trim( $body['choices'][0]['message']['content'] ?? '' );

        if ( ! $text ) {
            wp_send_json_error( [ 'message' => 'AI returned empty response.' ] );
        }

        wp_send_json_success( [ 'message' => $text ] );
    }

    /* ═══════════════════════════════════════
       AJAX: Send Campaign Immediately
    ═══════════════════════════════════════ */

    public function ajax_send_campaign() {
        if ( ! current_user_can( 'read' ) ) wp_send_json_error( [ 'message' => 'Unauthorized' ], 403 );

        $campaign_id = (int) ( $_POST['campaign_id'] ?? 0 );
        if ( $campaign_id <= 0 ) wp_send_json_error( [ 'message' => 'Invalid campaign.' ] );

        global $wpdb;
        $table   = $wpdb->prefix . 'vmo_sms_campaigns';
        $user_id = get_current_user_id();

        $campaign = $wpdb->get_row( $wpdb->prepare(
            "SELECT * FROM {$table} WHERE id = %d AND user_id = %d",
            $campaign_id, $user_id
        ) );

        if ( ! $campaign ) wp_send_json_error( [ 'message' => 'Campaign not found.' ] );
        if ( ! in_array( $campaign->status, [ 'draft', 'scheduled' ], true ) ) {
            wp_send_json_error( [ 'message' => 'Campaign already sent or in progress.' ] );
        }

        // Readiness check
        if ( function_exists( 'vmo_get_sms_activation_snapshot' ) ) {
            $snap = vmo_get_sms_activation_snapshot( $user_id, false );
            if ( empty( $snap['bulk_marketing_ready'] ) ) {
                wp_send_json_error( [ 'message' => 'SMS marketing not activated. Complete A2P registration first.' ] );
            }
        }

        // Mark as sending
        $wpdb->update( $table, [
            'status'     => 'sending',
            'started_at' => current_time( 'mysql' ),
        ], [ 'id' => $campaign_id ] );

        // Process sends
        $result = $this->execute_campaign_sends( $campaign_id, $user_id, $campaign->message );

        // Update final stats
        $wpdb->update( $table, [
            'status'       => 'completed',
            'sent_count'   => $result['sent'],
            'failed_count' => $result['failed'],
            'completed_at' => current_time( 'mysql' ),
        ], [ 'id' => $campaign_id ] );

        // Log to Today tab actions
        if ( function_exists( 'vmo_log_interaction' ) ) {
            vmo_log_interaction( $user_id, 'system', [
                'type'    => 'sms_campaign',
                'channel' => 'sms',
                'notes'   => "SMS campaign \"{$campaign->name}\" sent to {$result['sent']} recipients ({$result['failed']} failed)",
                'outcome' => $result['failed'] === 0 ? 'positive' : 'neutral',
            ] );
        }

        wp_send_json_success( [
            'message' => "Campaign sent! {$result['sent']} delivered, {$result['failed']} failed.",
            'sent'    => $result['sent'],
            'failed'  => $result['failed'],
        ] );
    }

    /* ═══════════════════════════════════════
       AJAX: Schedule Campaign
    ═══════════════════════════════════════ */

    public function ajax_schedule_campaign() {
        if ( ! current_user_can( 'read' ) ) wp_send_json_error( [ 'message' => 'Unauthorized' ], 403 );

        $campaign_id  = (int) ( $_POST['campaign_id'] ?? 0 );
        $scheduled_at = sanitize_text_field( wp_unslash( $_POST['scheduled_at'] ?? '' ) );

        if ( $campaign_id <= 0 || ! $scheduled_at ) {
            wp_send_json_error( [ 'message' => 'Campaign ID and schedule time required.' ] );
        }

        global $wpdb;
        $table   = $wpdb->prefix . 'vmo_sms_campaigns';
        $user_id = get_current_user_id();

        $wpdb->update( $table, [
            'status'       => 'scheduled',
            'scheduled_at' => $scheduled_at,
        ], [ 'id' => $campaign_id, 'user_id' => $user_id ] );

        wp_send_json_success( [ 'message' => "Campaign scheduled for {$scheduled_at}." ] );
    }

    /* ═══════════════════════════════════════
       AJAX: Campaign Stats
    ═══════════════════════════════════════ */

    public function ajax_campaign_stats() {
        if ( ! current_user_can( 'read' ) ) wp_send_json_error( [ 'message' => 'Unauthorized' ], 403 );

        $campaign_id = (int) ( $_POST['campaign_id'] ?? 0 );
        if ( $campaign_id <= 0 ) wp_send_json_error( [ 'message' => 'Invalid campaign.' ] );

        global $wpdb;
        $table   = $wpdb->prefix . 'vmo_sms_campaigns';
        $r_table = $wpdb->prefix . 'vmo_sms_campaign_recipients';
        $user_id = get_current_user_id();

        $campaign = $wpdb->get_row( $wpdb->prepare(
            "SELECT * FROM {$table} WHERE id = %d AND user_id = %d",
            $campaign_id, $user_id
        ), ARRAY_A );

        if ( ! $campaign ) wp_send_json_error( [ 'message' => 'Campaign not found.' ] );

        $recipients = $wpdb->get_results( $wpdb->prepare(
            "SELECT contact_name, phone, status, sent_at, delivered_at, error_message FROM {$r_table} WHERE campaign_id = %d ORDER BY sent_at DESC LIMIT 100",
            $campaign_id
        ), ARRAY_A );

        wp_send_json_success( [
            'campaign'   => $campaign,
            'recipients' => $recipients ?: [],
        ] );
    }

    /* ═══════════════════════════════════════
       AJAX: Delete Campaign
    ═══════════════════════════════════════ */

    public function ajax_delete_campaign() {
        if ( ! current_user_can( 'read' ) ) wp_send_json_error( [ 'message' => 'Unauthorized' ], 403 );

        $campaign_id = (int) ( $_POST['campaign_id'] ?? 0 );
        if ( $campaign_id <= 0 ) wp_send_json_error( [ 'message' => 'Invalid campaign.' ] );

        global $wpdb;
        $user_id = get_current_user_id();
        $table   = $wpdb->prefix . 'vmo_sms_campaigns';
        $r_table = $wpdb->prefix . 'vmo_sms_campaign_recipients';

        // Only allow deleting draft/completed
        $status = $wpdb->get_var( $wpdb->prepare(
            "SELECT status FROM {$table} WHERE id = %d AND user_id = %d", $campaign_id, $user_id
        ) );

        if ( $status === 'sending' ) {
            wp_send_json_error( [ 'message' => 'Cannot delete a campaign that is currently sending.' ] );
        }

        $wpdb->delete( $r_table, [ 'campaign_id' => $campaign_id ] );
        $wpdb->delete( $table, [ 'id' => $campaign_id, 'user_id' => $user_id ] );

        wp_send_json_success( [ 'message' => 'Campaign deleted.' ] );
    }

    /* ═══════════════════════════════════════
       CRON: Process Scheduled Campaigns
    ═══════════════════════════════════════ */

    public function process_scheduled_campaigns() {
        global $wpdb;
        $table = $wpdb->prefix . 'vmo_sms_campaigns';
        $now   = current_time( 'mysql' );

        $due = $wpdb->get_results( $wpdb->prepare(
            "SELECT * FROM {$table} WHERE status = 'scheduled' AND scheduled_at <= %s LIMIT 5",
            $now
        ) );

        if ( ! $due ) return;

        foreach ( $due as $campaign ) {
            // Readiness check
            if ( function_exists( 'vmo_get_sms_activation_snapshot' ) ) {
                $snap = vmo_get_sms_activation_snapshot( $campaign->user_id, false );
                if ( empty( $snap['bulk_marketing_ready'] ) ) {
                    $wpdb->update( $table, [
                        'status' => 'failed',
                    ], [ 'id' => $campaign->id ] );
                    continue;
                }
            }

            $wpdb->update( $table, [
                'status'     => 'sending',
                'started_at' => current_time( 'mysql' ),
            ], [ 'id' => $campaign->id ] );

            $result = $this->execute_campaign_sends( $campaign->id, $campaign->user_id, $campaign->message );

            $wpdb->update( $table, [
                'status'       => 'completed',
                'sent_count'   => $result['sent'],
                'failed_count' => $result['failed'],
                'completed_at' => current_time( 'mysql' ),
            ], [ 'id' => $campaign->id ] );
        }
    }

    /* ═══════════════════════════════════════
       INTERNAL: Populate Recipients
    ═══════════════════════════════════════ */

    private function populate_recipients( int $campaign_id, int $user_id, string $filter, string $tags ): int {
        $contacts = $this->get_audience_contacts( $user_id, $filter, $tags );
        if ( ! $contacts ) return 0;

        global $wpdb;
        $r_table = $wpdb->prefix . 'vmo_sms_campaign_recipients';

        $count = 0;
        foreach ( $contacts as $c ) {
            $phone = $c['phone'] ?? '';
            if ( ! $phone ) continue;

            $wpdb->insert( $r_table, [
                'campaign_id'  => $campaign_id,
                'contact_id'   => $c['contact_id'] ?? '',
                'phone'        => $phone,
                'contact_name' => $c['full_name'] ?? '',
                'status'       => 'pending',
            ] );
            $count++;
        }

        return $count;
    }

    /* ═══════════════════════════════════════
       INTERNAL: Execute Campaign Sends
    ═══════════════════════════════════════ */

    private function execute_campaign_sends( int $campaign_id, int $user_id, string $message ): array {
        global $wpdb;
        $r_table = $wpdb->prefix . 'vmo_sms_campaign_recipients';
        $sent    = 0;
        $failed  = 0;

        $recipients = $wpdb->get_results( $wpdb->prepare(
            "SELECT id, contact_id, phone, contact_name FROM {$r_table} WHERE campaign_id = %d AND status = 'pending'",
            $campaign_id
        ) );

        if ( ! $recipients ) return [ 'sent' => 0, 'failed' => 0 ];

        // Get Twilio client
        $client = null;
        if ( class_exists( 'VMO_Twilio_Enhanced_Client' ) ) {
            $client = VMO_Twilio_Enhanced_Client::get_instance_for_user( $user_id );
            if ( ! $client ) {
                $client = VMO_Twilio_Enhanced_Client::get_instance();
            }
        }

        if ( ! $client || ! method_exists( $client, 'send_sms' ) ) {
            // Mark all as failed
            $wpdb->query( $wpdb->prepare(
                "UPDATE {$r_table} SET status = 'failed', error_message = 'Twilio client unavailable' WHERE campaign_id = %d AND status = 'pending'",
                $campaign_id
            ) );
            return [ 'sent' => 0, 'failed' => count( $recipients ) ];
        }

        foreach ( $recipients as $r ) {
            // Personalise: replace {name} placeholder
            $personalised = str_replace( '{name}', ( $r->contact_name ?: 'there' ), $message );

            $result = $client->send_sms( $r->phone, $personalised, [ 'track_status' => true ] );

            if ( is_wp_error( $result ) ) {
                $wpdb->update( $r_table, [
                    'status'        => 'failed',
                    'error_message' => $result->get_error_message(),
                    'sent_at'       => current_time( 'mysql' ),
                ], [ 'id' => $r->id ] );
                $failed++;
            } else {
                $sid = $result['sid'] ?? ( $result['message_sid'] ?? '' );
                $wpdb->update( $r_table, [
                    'status'     => 'sent',
                    'twilio_sid' => $sid,
                    'sent_at'    => current_time( 'mysql' ),
                ], [ 'id' => $r->id ] );
                $sent++;
            }

            // Rate limiting — 1 message per 100ms to avoid Twilio throttling
            usleep( 100000 );
        }

        // Log SMS sends to CRM interactions
        if ( function_exists( 'vmo_log_interaction' ) ) {
            $c_table = $wpdb->prefix . 'vmo_sms_campaigns';
            $name = $wpdb->get_var( $wpdb->prepare( "SELECT name FROM {$c_table} WHERE id = %d", $campaign_id ) );
            foreach ( $recipients as $r ) {
                $crm_contact_id = ! empty( $r->contact_id ) ? (string) $r->contact_id : '';
                if ( ! $crm_contact_id && function_exists( 'vmo_upsert_contact' ) ) {
                    $crm_contact_id = vmo_upsert_contact( (int) $user_id, [
                        'name' => $r->contact_name,
                        'phone' => $r->phone,
                        'source' => 'other',
                        'primary_category' => 'lead',
                        'lifecycle_stage' => 'engaged',
                        'tags' => 'sms_campaign',
                    ] );
                }
                if ( $crm_contact_id ) {
                    vmo_log_interaction( $user_id, $crm_contact_id, [
                        'type'    => 'sms_campaign',
                        'channel' => 'sms',
                        'notes'   => "Bulk SMS campaign: {$name}",
                        'outcome' => 'neutral',
                        'direction' => 'outbound',
                        'metadata' => [
                            'campaign_id' => $campaign_id,
                            'recipient_id' => $r->id,
                            'phone' => $r->phone,
                        ],
                    ] );
                }
            }
        }

        return [ 'sent' => $sent, 'failed' => $failed ];
    }

    /* ═══════════════════════════════════════
       INTERNAL: Get Audience Contacts
    ═══════════════════════════════════════ */

    private function get_audience_contacts( int $user_id, string $filter, string $tags = '' ): array {
        global $wpdb;
        $table = $wpdb->prefix . 'vmo_contacts';

        if ( ! vmo_table_exists( $table ) ) return [];

        $where = [ $wpdb->prepare( 'user_id = %d', $user_id ) ];
        $where[] = "phone IS NOT NULL AND phone != ''";

        switch ( $filter ) {
            case 'all_sms_subscribed':
                $where[] = 'sms_subscribed = 1';
                break;
            case 'all_with_phone':
                // No extra filter — just needs phone
                break;
            case 'leads':
                $where[] = "contact_type IN ('lead','inbound_lead')";
                $where[] = 'sms_subscribed = 1';
                break;
            case 'customers':
                $where[] = "contact_type IN ('customer','vip')";
                $where[] = 'sms_subscribed = 1';
                break;
            case 'engaged':
                $where[] = "contact_type IN ('engaged_prospect','customer','vip')";
                $where[] = 'sms_subscribed = 1';
                break;
            case 'tagged':
                if ( $tags ) {
                    $tag_list = array_map( 'trim', explode( ',', $tags ) );
                    $placeholders = implode( ',', array_fill( 0, count( $tag_list ), '%s' ) );
                    $where[] = $wpdb->prepare(
                        "EXISTS (SELECT 1 FROM {$wpdb->prefix}vmo_contact_tags ct WHERE ct.contact_id = {$table}.contact_id AND ct.tag IN ({$placeholders}))",
                        ...$tag_list
                    );
                    $where[] = 'sms_subscribed = 1';
                }
                break;
            default:
                $where[] = 'sms_subscribed = 1';
        }

        $sql = "SELECT contact_id, full_name, phone, email, contact_type FROM {$table} WHERE " . implode( ' AND ', $where ) . " ORDER BY full_name ASC LIMIT 5000";

        return $wpdb->get_results( $sql, ARRAY_A ) ?: [];
    }
}
