<?php

namespace App\Console\Commands;
use Illuminate\Console\Command;
use Session;
use App\Helper\Helper;
use Illuminate\Http\Request;
use App\Events\IncomingLead;
use Google\Cloud\TextToSpeech\V1\AudioConfig;
use Google\Cloud\TextToSpeech\V1\AudioEncoding;
use Google\Cloud\TextToSpeech\V1\SsmlVoiceGender;
use Google\Cloud\TextToSpeech\V1\SynthesisInput;
use Google\Cloud\TextToSpeech\V1\TextToSpeechClient;
use Google\Cloud\TextToSpeech\V1\VoiceSelectionParams;
use App\Model\VoiceTemplate;
use Illuminate\Support\Facades\DB;
use App\Model\Client\Campaign;
use App\Model\Client\CampaignList;
use App\Model\Client\ListData;
use App\Model\Client\LeadTemp;
use App\Model\Dialer;
use App\Model\Client\CustomFieldLabelsValues;
use App\Model\Client\Label;
use App\Model\SmsTemplete;
use App\Model\Client\ListHeader;
use App\Model\Cron;
use  App\Model\Master\Client;
use App\Model\Master\ClientPackage;
use App\Model\Master\ClientServers;
use App\Jobs\OutboundAICallJob;
use Carbon\Carbon;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Cache;
use Razorpay\Api\Api;



class OutboundUpdateCron extends Command
{

    protected $signature = 'outbound:cron';
    protected $description = 'Command description';

    public function __construct()
    {
        parent::__construct();
    }

    public function handle()
    {
        // Issue #6: Mutex lock to prevent overlapping manual runs.
        // The Kernel has withoutOverlapping() for scheduled runs, but this
        // also protects against concurrent `php artisan outbound:cron` invocations.
        $lock = Cache::lock('outbound_cron_lock', 120);
        if (!$lock->get()) {
            Log::warning('OutboundUpdateCron: Another instance is already running. Exiting.');
            return;
        }

        try
        {
            // ========== EMAIL NOTIFICATION TOGGLE ==========
            $SEND_ADMIN_EMAILS = true;

            // ========== AI QUOTA CHECK TOGGLE ==========
            $CHECK_AI_QUOTA = true;

            if ($CHECK_AI_QUOTA && !isAIQuotaAvailable()) {
                Log::info('OpenAI quota exhausted - Checking for active outbound_ai campaigns...');

                $clientIds = Client::where('outbond_ai', 1)->pluck('id')->toArray();
                $totalDisabled = 0;
                $affectedClients = [];
                $allDisabledCampaigns = [];

                foreach($clientIds as $clientId) {
                    $campaignsToDisable = DB::connection('mysql_'.$clientId)
                        ->table('campaign')
                        ->where('dial_mode', 'outbound_ai')
                        ->where('status', 1)
                        ->select('id', 'title')
                        ->get()
                        ->toArray();

                    if (count($campaignsToDisable) > 0) {
                        $sql = "UPDATE campaign SET status = 0, pause_reason = 'ai_quota_exhausted' WHERE dial_mode = :dial_mode AND status = 1";
                        $updated = DB::connection('mysql_'.$clientId)->update($sql, array('dial_mode' => 'outbound_ai'));
                        $totalDisabled += $updated;
                        $affectedClients[] = $clientId;

                        foreach($campaignsToDisable as $camp) {
                            $allDisabledCampaigns[] = [
                                'client_id' => $clientId,
                                'campaign_id' => $camp->id,
                                'campaign_title' => $camp->title
                            ];
                        }

                        Log::info("Disabled {$updated} outbound_ai campaigns for client {$clientId}");
                    }
                }

                if ($totalDisabled > 0) {
                    $adminEmail = env('ADMIN_NOTIFY_EMAIL');
                    if ($SEND_ADMIN_EMAILS && !empty($adminEmail)) {
                        $fullApiKey = env('OPENAI_API_KEY', '');
                        $maskedApiKey = strlen($fullApiKey) > 10
                            ? str_repeat('*', strlen($fullApiKey) - 10) . substr($fullApiKey, -10)
                            : $fullApiKey;

                        $campaignListHtml = '';
                        foreach($allDisabledCampaigns as $camp) {
                            $campaignListHtml .= "<tr>
                                <td style='border: 1px solid #ddd; padding: 8px;'>{$camp['client_id']}</td>
                                <td style='border: 1px solid #ddd; padding: 8px;'>{$camp['campaign_id']}</td>
                                <td style='border: 1px solid #ddd; padding: 8px;'>{$camp['campaign_title']}</td>
                            </tr>";
                        }

                        $emailBody = "
                            <h2 style='color: #dc3545;'>OpenAI Quota Exhausted - Campaigns Disabled</h2>
                            <p><strong>Alert Time:</strong> " . Carbon::now('US/Eastern')->format('Y-m-d H:i:s') . "</p>
                            <p><strong>Issue:</strong> OpenAI API quota has been exhausted</p>
                            <p><strong>Action Taken:</strong> Active outbound_ai campaigns have been disabled</p>
                            <hr>
                            <h3>Summary:</h3>
                            <ul>
                                <li><strong>API Key:</strong> {$maskedApiKey}</li>
                                <li><strong>Affected Clients:</strong> " . count($affectedClients) . "</li>
                                <li><strong>Total Campaigns Disabled:</strong> {$totalDisabled}</li>
                                <li><strong>Client IDs:</strong> " . implode(', ', $affectedClients) . "</li>
                            </ul>
                            <hr>
                            <h3>Disabled Campaigns (for reactivation):</h3>
                            <table style='border-collapse: collapse; width: 100%; margin-top: 10px;'>
                                <tr style='background: #f8f8f8;'>
                                    <th style='border: 1px solid #ddd; padding: 8px; text-align: left;'>Client ID</th>
                                    <th style='border: 1px solid #ddd; padding: 8px; text-align: left;'>Campaign ID</th>
                                    <th style='border: 1px solid #ddd; padding: 8px; text-align: left;'>Campaign Name</th>
                                </tr>
                                {$campaignListHtml}
                            </table>
                            <hr>
                            <p style='color: #6c757d;'>Please check your OpenAI account and add credits to resume service.</p>
                        ";
                        send_sendgrid_mail(
                            $adminEmail,
                            "[CRITICAL] OpenAI Quota Exhausted - Outbound AI Campaigns Disabled",
                            $emailBody
                        );
                        Log::info("Admin notification email sent to {$adminEmail}");
                    }
                    Log::info("Disabled {$totalDisabled} outbound_ai campaigns due to quota exhaustion");
                } else {
                    Log::info('No active outbound_ai campaigns found - no action needed');
                }

                return;
            }

            // Issue #11: Use Carbon instead of date_default_timezone_set()
            $last_time_cron_run = Carbon::now('US/Eastern')->format('Y-m-d H:i:s');

            $clients = Client::where('outbond_ai', 1)->get(['id', 'currency_code']);

            // ======================================================================
            // PHASE 1: Collect all eligible (client, campaign) pairs with validation
            // ======================================================================
            $eligibleCampaigns = [];

            foreach($clients as $client)
            {
                $clientId = $client->id;
                $currency = $client->currency_code;

                // ========== RAZORPAY SUBSCRIPTION CHECK (INR clients) ==========
                if ($currency == 'INR') {
                    $hasValidSubscription = ClientPackage::where('client_id', $clientId)
                        ->whereNotNull('razorpay_subscription_id')
                        ->where('end_time', '>=', Carbon::now())
                        ->exists();

                    if (!$hasValidSubscription) {
                        Log::info("Client {$clientId} has no valid Razorpay subscription. Skipping...");

                        $campaignsToDisable = DB::connection('mysql_'.$clientId)
                            ->table('campaign')
                            ->where('dial_mode', 'outbound_ai')
                            ->where('status', 1)
                            ->select('id', 'title')
                            ->get()
                            ->toArray();

                        $sql = "UPDATE campaign SET status = 0, pause_reason = 'no_subscription' WHERE dial_mode = :dial_mode AND status = 1";
                        $disabled = DB::connection('mysql_'.$clientId)->update($sql, ['dial_mode' => 'outbound_ai']);

                        if ($disabled > 0) {
                            Log::info("Disabled {$disabled} outbound_ai campaigns for client {$clientId} due to missing subscription.");

                            $campaignListHtml = '';
                            foreach($campaignsToDisable as $camp) {
                                $campaignListHtml .= "<tr>
                                    <td style='border: 1px solid #ddd; padding: 8px;'>{$camp->id}</td>
                                    <td style='border: 1px solid #ddd; padding: 8px;'>{$camp->title}</td>
                                </tr>";
                            }

                            $adminEmail = env('ADMIN_NOTIFY_EMAIL');
                            if ($SEND_ADMIN_EMAILS && !empty($adminEmail)) {
                                $emailBody = "
                                    <h2 style='color: #dc3545;'>No Valid Subscription - Campaigns Disabled</h2>
                                    <p><strong>Alert Time:</strong> " . Carbon::now('US/Eastern')->format('Y-m-d H:i:s') . "</p>
                                    <p><strong>Issue:</strong> Client does not have a valid Razorpay subscription</p>
                                    <p><strong>Action Taken:</strong> Outbound AI campaigns have been disabled</p>
                                    <hr>
                                    <h3>Details:</h3>
                                    <ul>
                                        <li><strong>Client ID:</strong> {$clientId}</li>
                                        <li><strong>Currency:</strong> {$currency}</li>
                                        <li><strong>Campaigns Disabled:</strong> {$disabled}</li>
                                    </ul>
                                    <hr>
                                    <h3>Disabled Campaigns (for reactivation):</h3>
                                    <table style='border-collapse: collapse; width: 100%; margin-top: 10px;'>
                                        <tr style='background: #f8f8f8;'>
                                            <th style='border: 1px solid #ddd; padding: 8px; text-align: left;'>Campaign ID</th>
                                            <th style='border: 1px solid #ddd; padding: 8px; text-align: left;'>Campaign Name</th>
                                        </tr>
                                        {$campaignListHtml}
                                    </table>
                                    <hr>
                                    <p style='color: #6c757d;'>Please contact the client to renew their subscription to resume service.</p>
                                ";
                                send_sendgrid_mail(
                                    $adminEmail,
                                    "[WARNING] Client {$clientId} - No Valid Subscription - Campaigns Disabled",
                                    $emailBody
                                );
                                Log::info("Admin notification email sent for client {$clientId}");
                            }
                        }

                        continue;
                    }

                    Log::info("Client {$clientId} has valid Razorpay subscription. Proceeding...");
                }
                // ========== END SUBSCRIPTION CHECK ==========

                // Issue #8: Check wallet ONCE per client (not per-lead)
                $walletBalance = (float) (DB::connection('mysql_' . $clientId)->table('wallet')->value('amount') ?? 0);
                if ($walletBalance <= 0) {
                    $msg = "Outbound call skipped due to insufficient wallet balance — Client {$clientId}, balance: {$walletBalance}";
                    Log::info($msg);
                    $this->warn($msg);

                    // ========== AUTO-RECHARGE FOR INR CLIENTS ==========
                    $autoRechargeAttempted = false;

                    if ($currency == 'INR') {
                        $user = DB::connection('master')->table('users')
                            ->where('parent_id', $clientId)
                            ->where('role', 'owner')
                            ->first();

                        if ($user &&
                            $user->auto_reload_enabled == 1 &&
                            !empty($user->razorpay_customer_id) &&
                            !empty($user->razorpay_token_id)) {

                            $rechargeAlreadyPending = false;
                            $cooldownMinutes = 10;

                            if ($user->auto_recharge_pending == 1) {
                                Log::info("Auto-recharge already PENDING for client {$clientId}. Skipping duplicate attempt.");
                                $rechargeAlreadyPending = true;
                            } elseif (!empty($user->auto_recharge_last_attempt)) {
                                $lastAttempt = Carbon::parse($user->auto_recharge_last_attempt);
                                $minutesSinceLastAttempt = $lastAttempt->diffInMinutes(Carbon::now());

                                if ($minutesSinceLastAttempt < $cooldownMinutes) {
                                    Log::info("Auto-recharge attempted {$minutesSinceLastAttempt} min ago for client {$clientId}. Waiting for cooldown ({$cooldownMinutes} min).");
                                    $rechargeAlreadyPending = true;
                                }
                            }

                            if ($rechargeAlreadyPending) {
                                continue;
                            }

                            Log::info("Auto-reload enabled for client {$clientId}. Attempting Razorpay recharge...");
                            $autoRechargeAttempted = true;

                            DB::connection('master')->table('users')
                                ->where('id', $user->id)
                                ->update([
                                    'auto_recharge_pending' => 1,
                                    'auto_recharge_last_attempt' => Carbon::now()
                                ]);

                            $rechargeResult = $this->attemptAutoRecharge($user, $clientId);

                            if ($rechargeResult['success']) {
                                Log::info("Auto-recharge triggered successfully for client {$clientId}. Subscription ID: {$rechargeResult['subscription_id']}");

                                $adminEmail = env('ADMIN_NOTIFY_EMAIL');
                                if ($SEND_ADMIN_EMAILS && !empty($adminEmail)) {
                                    $emailBody = "
                                        <h2 style='color: #28a745;'>Auto-Recharge Triggered Successfully</h2>
                                        <p><strong>Alert Time:</strong> " . Carbon::now('US/Eastern')->format('Y-m-d H:i:s') . "</p>
                                        <p><strong>Issue:</strong> Client wallet balance was low</p>
                                        <p><strong>Action Taken:</strong> Auto-recharge triggered via Razorpay</p>
                                        <hr>
                                        <h3>Details:</h3>
                                        <ul>
                                            <li><strong>Client ID:</strong> {$clientId}</li>
                                            <li><strong>Previous Balance:</strong> {$walletBalance}</li>
                                            <li><strong>Recharge Amount:</strong> {$user->auto_reload_amount}</li>
                                            <li><strong>Subscription ID:</strong> {$rechargeResult['subscription_id']}</li>
                                        </ul>
                                        <hr>
                                        <p style='color: #6c757d;'>Wallet will be credited after payment confirmation via webhook.</p>
                                    ";
                                    send_sendgrid_mail(
                                        $adminEmail,
                                        "[INFO] Client {$clientId} - Auto-Recharge Triggered",
                                        $emailBody
                                    );
                                }

                                continue;
                            } else {
                                Log::warning("Auto-recharge FAILED for client {$clientId}: {$rechargeResult['message']}");

                                DB::connection('master')->table('users')
                                    ->where('id', $user->id)
                                    ->update(['auto_recharge_pending' => 0]);
                            }
                        }
                    }
                    // ========== END AUTO-RECHARGE ==========

                    Log::info("Disabling campaigns for client {$clientId}...");

                    $campaignsToDisable = DB::connection('mysql_'.$clientId)
                        ->table('campaign')
                        ->where('dial_mode', 'outbound_ai')
                        ->where('status', 1)
                        ->select('id', 'title')
                        ->get()
                        ->toArray();

                    $sql = "UPDATE campaign SET status = 0, pause_reason = 'insufficient_wallet' WHERE dial_mode = :dial_mode AND status = 1";
                    $updated = DB::connection('mysql_'.$clientId)->update($sql, ['dial_mode' => 'outbound_ai']);

                    $disableMsg = "Disabled {$updated} outbound_ai campaigns for client {$clientId} due to insufficient balance.";
                    Log::info($disableMsg);
                    $this->warn($disableMsg);

                    $adminEmail = env('ADMIN_NOTIFY_EMAIL');
                    if ($SEND_ADMIN_EMAILS && !empty($adminEmail) && $updated > 0) {
                        $autoRechargeNote = $autoRechargeAttempted
                            ? "<p style='color: #dc3545;'><strong>Auto-recharge was attempted but failed.</strong></p>"
                            : ($currency == 'INR' ? "<p style='color: #6c757d;'>Auto-recharge is not enabled for this client.</p>" : "");

                        $campaignListHtml = '';
                        foreach($campaignsToDisable as $camp) {
                            $campaignListHtml .= "<tr>
                                <td style='border: 1px solid #ddd; padding: 8px;'>{$camp->id}</td>
                                <td style='border: 1px solid #ddd; padding: 8px;'>{$camp->title}</td>
                            </tr>";
                        }

                        $emailBody = "
                            <h2 style='color: #ffc107;'>Insufficient Wallet Balance - Campaigns Disabled</h2>
                            <p><strong>Alert Time:</strong> " . Carbon::now('US/Eastern')->format('Y-m-d H:i:s') . "</p>
                            <p><strong>Issue:</strong> Client wallet balance is zero or negative</p>
                            <p><strong>Action Taken:</strong> Outbound AI campaigns have been disabled for this client</p>
                            {$autoRechargeNote}
                            <hr>
                            <h3>Details:</h3>
                            <ul>
                                <li><strong>Client ID:</strong> {$clientId}</li>
                                <li><strong>Current Balance:</strong> {$walletBalance}</li>
                                <li><strong>Currency:</strong> {$currency}</li>
                                <li><strong>Campaigns Disabled:</strong> {$updated}</li>
                            </ul>
                            <hr>
                            <h3>Disabled Campaigns (for reactivation):</h3>
                            <table style='border-collapse: collapse; width: 100%; margin-top: 10px;'>
                                <tr style='background: #f8f8f8;'>
                                    <th style='border: 1px solid #ddd; padding: 8px; text-align: left;'>Campaign ID</th>
                                    <th style='border: 1px solid #ddd; padding: 8px; text-align: left;'>Campaign Name</th>
                                </tr>
                                {$campaignListHtml}
                            </table>
                            <hr>
                            <p style='color: #6c757d;'>Please contact the client to add funds to their wallet to resume service.</p>
                        ";
                        send_sendgrid_mail(
                            $adminEmail,
                            "[WARNING] Client {$clientId} - Insufficient Balance - Campaigns Disabled",
                            $emailBody
                        );
                        Log::info("Admin notification email sent for client {$clientId}");
                    }

                    continue;
                }

                $asteriskServer = ClientServers::where('client_id', $clientId)->orderBy('id', 'desc')->value('ip_address');

                $outboundCampaigns = Campaign::on('mysql_'.$clientId)
                    ->select('id','title','call_ratio','duration','hopper_mode','redirect_to','redirect_to_dropdown','amd_drop_action','voicedrop_option_user_id','amd','last_time_cron_run')
                    ->where('dial_mode','outbound_ai')
                    ->where('status',1)
                    ->where('is_deleted',0)
                    ->get()
                    ->all();

                if(!empty($outboundCampaigns))
                {
                    foreach($outboundCampaigns as $details)
                    {
                        $call_ratio = max(1, (int) $details->call_ratio);
                        $MAX_CALLS_PER_MINUTE = 20;
                        $effectiveCallsPerMinute = min($call_ratio, $MAX_CALLS_PER_MINUTE);
                        $delayPerCall = ceil(60 / $effectiveCallsPerMinute);

                        $eligibleCampaigns[] = [
                            'clientId'       => $clientId,
                            'currency'       => $currency,
                            'campaignId'     => $details->id,
                            'callRatio'      => $call_ratio,
                            'delayPerCall'   => $delayPerCall,
                            'duration'       => $details->duration,
                            'hopper_mode'    => $details->hopper_mode,
                            'redirect_to'    => $details->redirect_to,
                            'redirect_to_dropdown' => $details->redirect_to_dropdown,
                            'amd'            => $details->amd,
                            'amd_drop_action'         => $details->amd_drop_action,
                            'voicedrop_option_user_id' => $details->voicedrop_option_user_id,
                            'last_time_cron_run'       => $details->last_time_cron_run,
                            'asteriskServer' => $asteriskServer,
                        ];
                    }
                }
            }

            if (empty($eligibleCampaigns)) {
                Log::info('No eligible campaigns found for dispatch.');
                $this->info('No eligible campaigns found for dispatch.');
                return;
            }

            // ======================================================================
            // PHASE 2: Round-robin dispatch
            // Build dispatch slots and interleave so no single client monopolizes.
            // ======================================================================

            // Build slot arrays: each campaign gets call_ratio slots
            $campaignSlots = [];
            foreach ($eligibleCampaigns as $idx => $entry) {
                $campaignSlots[$idx] = [
                    'entry'     => $entry,
                    'remaining' => $entry['callRatio'],
                ];
            }

            // Track delay counter per-campaign for rate limiting
            $delayCounters = array_fill(0, count($eligibleCampaigns), 0);
            // Track campaigns that have run out of leads
            $exhaustedCampaigns = [];

            $dialer = new Dialer;
            $cron = new Cron();
            $template = new SmsTemplete();

            // Round-robin: rotate through campaigns, pick 1 slot each pass
            $hasWork = true;
            while ($hasWork) {
                $hasWork = false;
                foreach ($campaignSlots as $idx => &$slot) {
                    if ($slot['remaining'] <= 0) {
                        continue;
                    }
                    if (isset($exhaustedCampaigns[$idx])) {
                        continue;
                    }

                    $hasWork = true;
                    $slot['remaining']--;

                    $entry = $slot['entry'];
                    $clientId    = $entry['clientId'];
                    $currency    = $entry['currency'];
                    $campaign_id = $entry['campaignId'];
                    $hopper_mode = $entry['hopper_mode'];
                    $extension   = '37873';
                    $asterisk_server_id = $entry['asteriskServer'];
                    $redirect_to = $entry['redirect_to'];
                    $redirect_to_dropdown = $entry['redirect_to_dropdown'];
                    $last_time_cron_run_db = $entry['last_time_cron_run'];
                    $duration    = $entry['duration'];
                    $delayPerCall = $entry['delayPerCall'];

                    // Duration / cron timing logic
                    $add_duration_date = strtotime($last_time_cron_run_db) + $duration;
                    $add_duration_last_time_cron_run_db = date('Y-m-d H:i:s', $add_duration_date);

                    $timestamp1 = strtotime($last_time_cron_run);
                    $timestamp2 = strtotime($add_duration_last_time_cron_run_db);

                    if(is_null($last_time_cron_run_db))
                    {
                        $sql = "UPDATE campaign set last_time_cron_run = :last_time_cron_run WHERE id = :id";
                        DB::connection('mysql_'.$clientId)->update($sql, array('id' => $campaign_id,'last_time_cron_run'=>$last_time_cron_run));
                        $last_time_cron_run_db = $last_time_cron_run;
                    }

                    if($timestamp1 > $timestamp2)
                    {
                        $sql = "UPDATE campaign set last_time_cron_run = :last_time_cron_run WHERE id = :id";
                        DB::connection('mysql_'.$clientId)->update($sql, array('id' => $campaign_id,'last_time_cron_run'=>
                            $add_duration_last_time_cron_run_db));
                    }

                    // AMD setup
                    $amd = $entry['amd'];
                    if($amd == 1)
                    {
                        $amd_drop_action = $entry['amd_drop_action'];
                        $amd_drop_message_output = $entry['voicedrop_option_user_id'];
                    }
                    else
                    {
                        $amd_drop_action = 0;
                        $amd_drop_message_output = 0;
                    }

                    $requestData = array(
                        'hopper_mode'        => $hopper_mode,
                        'extension'          => $extension,
                        'asterisk_server_id' => $asterisk_server_id,
                        'clientId'           => $clientId,
                        'currency_code'      => $currency,
                        'campaign_id'        => $campaign_id,
                        'redirect_to'        => $redirect_to,
                        'redirect_to_dropdown' => $redirect_to_dropdown,
                    );

                    // Select lead
                    $hopperSnapshot = DB::connection('mysql_'.$clientId)->select("SELECT lead_id FROM lead_temp WHERE campaign_id = :cid ORDER BY lead_id ASC", ['cid' => $campaign_id]);
                    Log::info('OutboundUpdateCron: PRE-PICK hopper', ['campaign_id' => $campaign_id, 'leads' => array_map(fn($r) => $r->lead_id, $hopperSnapshot)]);

                    $addResponse = $dialer->addLeadToExtensionLiveOutboundAI($campaign_id, $hopper_mode, $extension, $asterisk_server_id, $clientId);
                    Log::info('OutboundUpdateCron: Dialer response', ['campaign_id' => $campaign_id, 'response' => $addResponse]);

                    // Issue #4: Fix NO_LEADS comparison
                    if(!empty($addResponse['code']) && $addResponse['code'] === 'NO_LEADS')
                    {
                        $result = $cron->addLeadTemp($clientId, $campaign_id);
                        $exhaustedCampaigns[$idx] = true;
                        continue;
                    }

                    $requestData['list_id'] = $addResponse['list_id'];
                    $requestData['lead_id'] = $addResponse['lead_id'];

                    // Immediately claim lead in lead_report with PROCESSING status.
                    // This closes the race window: addLeadTemp() checks lead_report
                    // before re-inserting leads, so a PROCESSING row prevents duplicates
                    // even before the queued OutboundAICallJob executes.
                    DB::connection('mysql_' . $clientId)->insert(
                        "INSERT INTO lead_report (campaign_id, list_id, lead_id, disposition_id, status, picked_at, attempt_count)
                         VALUES (:campaign_id, :list_id, :lead_id, NULL, 'PROCESSING', NOW(), 1)
                         ON DUPLICATE KEY UPDATE status = 'PROCESSING', picked_at = NOW(), attempt_count = attempt_count + 1",
                        [
                            'campaign_id' => $campaign_id,
                            'list_id'     => $addResponse['list_id'],
                            'lead_id'     => $addResponse['lead_id'],
                        ]
                    );

                    // AMD drop action processing
                    if($amd_drop_action == 3) // voice template
                    {
                        $file_name = $template->changeVoiceMessageText($addResponse, $redirect_to, $amd_drop_message_output, $clientId);
                        $amd_drop_message_output = $file_name;
                    }

                    // Issue #12: Initialize $file_name before redirect_to conditionals
                    $file_name = '';

                    if($redirect_to == 1) // audio message
                    {
                        $file_name = $redirect_to_dropdown.'.wav';
                    }
                    else if($redirect_to == 2) // voice template
                    {
                        $file_name = $template->changeVoiceMessageText($addResponse, $redirect_to, $redirect_to_dropdown, $clientId);
                    }
                    else if($redirect_to == 3) // extension
                    {
                        $file_name = $redirect_to_dropdown;
                    }
                    else if($redirect_to == 4) // ring group
                    {
                        $file_name = $redirect_to_dropdown;
                    }
                    else if($redirect_to == 5) // ivr
                    {
                        $file_name = $redirect_to_dropdown.'.wav';
                    }
                    else if($redirect_to == 6) // voice ai
                    {
                        $file_name = $redirect_to_dropdown;
                        clientCampaignLeadPromptRedisCacheSet($clientId, $campaign_id, $requestData['lead_id'], $requestData['list_id'], $redirect_to_dropdown, true);
                    }

                    $requestData['file_name'] = $file_name;
                    $requestData['amd_drop_action'] = $amd_drop_action;
                    $requestData['amd_drop_message_output'] = $amd_drop_message_output;

                    // Fetch lead number and insert line_detail_ai + cdr_ai
                    $sql = "SELECT column_name FROM list_header WHERE list_id = :list_id AND is_dialing = :is_dialing";
                    $listHeader = DB::connection('mysql_' . $clientId)->selectOne($sql, ['list_id' => $requestData['list_id'], 'is_dialing' => 1]);
                    $listHeader = (array)$listHeader;

                    $sql = "SELECT * FROM list_data WHERE id = :id";
                    $listData = DB::connection('mysql_' . $clientId)->selectOne($sql, ['id' => $requestData['lead_id']]);
                    $listData = (array)$listData;
                    $number = $listData[$listHeader['column_name']];

                    $mobile = preg_replace('/[^0-9]/', '', $number);
                    $numberAreacode = substr(trim($mobile), 0, 3);
                    $area_code = $numberAreacode;

                    DB::connection('mysql_' . $clientId)->table('line_detail_ai')->insert([
                        'route'       => 'OUT',
                        'type'        => 'outbound_ai',
                        'number'      => $number,
                        'campaign_id' => $campaign_id,
                        'lead_id'     => $requestData['lead_id']
                    ]);

                    // Issue #13: Uncomment CDR insert so cdr_ai_last_id has a real value
                    $cdrData = [
                        'route'       => 'OUT',
                        'type'        => 'outbound_ai',
                        'campaign_id' => $campaign_id,
                        'number'      => $number,
                        'lead_id'     => $requestData['lead_id'],
                        'area_code'   => $area_code,
                        'cli'         => ''
                    ];

                    //$lastId = DB::connection('mysql_' . $clientId)->table('cdr_ai')->insertGetId($cdrData);
                    $requestData['cdr_ai_last_id'] = 0;//$lastId;

                    // Issue #10: Dispatch to per-client queue
                    dispatch(
                        (new OutboundAICallJob($requestData))
                            ->delay(Carbon::now()->addSeconds($delayCounters[$idx]))
                            ->onConnection('outbound_ai_job')
                            ->onQueue("client_{$clientId}")
                    );

                    $delayCounters[$idx] += $delayPerCall;
                }
                unset($slot);
            }

            Log::info('OutboundUpdateCron: dispatch complete.');
            $this->info('OutboundUpdateCron: dispatch complete.');
        }

        catch(\Exception $e)
        {
            Log::error('OutboundUpdateCron error: ' . $e->getMessage(), [
                'trace' => $e->getTraceAsString()
            ]);
        }
        finally
        {
            // Record heartbeat so health checks can detect stale cron
            Cache::put('outbound_cron_last_run', now()->toDateTimeString(), 600);
            $lock->release();
        }
    }

    /**
     * Attempt auto-recharge via Razorpay for INR clients
     *
     * @param object $user User object with Razorpay mandate details
     * @param int $clientId Client ID
     * @return array ['success' => bool, 'payment_id' => string|null, 'message' => string]
     */
    private function attemptAutoRecharge($user, $clientId)
    {
        try {
            $rzpKey = env('RAZORPAY_KEY');
            $rzpSecret = env('RAZORPAY_SECRET');

            if (empty($rzpKey) || empty($rzpSecret)) {
                return [
                    'success' => false,
                    'message' => 'Razorpay credentials not configured'
                ];
            }

            $api = new Api($rzpKey, $rzpSecret);

            $amount = $user->auto_reload_amount;
            $amountPaise = intval($amount * 100);

            // 1. Create Order
            $order = $api->order->create([
                'amount'   => $amountPaise,
                'currency' => 'INR',
                'receipt'  => 'cron_auto_recharge_' . time(),
                'notes'    => [
                    'type'      => 'auto_recharge',
                    'client_id' => $clientId,
                    'user_id'   => $user->id
                ]
            ]);

            Log::info('Cron Auto-Recharge: Order Created', [
                'client_id' => $clientId,
                'order_id' => $order['id'],
                'amount' => $amount
            ]);

            // 2. Create Recurring Payment
            $payment = $api->payment->createRecurring([
                'email'       => $user->email,
                'contact'     => $user->mobile,
                'amount'      => $amountPaise,
                'currency'    => 'INR',
                'order_id'    => $order['id'],
                'customer_id' => $user->razorpay_customer_id,
                'token'       => $user->razorpay_token_id,
                'description' => 'Wallet Auto-Recharge (Cron)',
                'notes'       => [
                    'type'      => 'auto_recharge',
                    'client_id' => $clientId,
                    'user_id'   => $user->id
                ]
            ]);

            Log::info('Cron Auto-Recharge: Payment Created', [
                'client_id' => $clientId,
                'payment_id' => $payment['razorpay_payment_id'],
                'order_id' => $order['id']
            ]);

            return [
                'success' => true,
                'subscription_id' => $payment['razorpay_payment_id'],
                'message' => 'Auto-recharge triggered successfully'
            ];

        } catch (\Exception $e) {
            Log::error('Cron Auto-Recharge Failed', [
                'client_id' => $clientId,
                'user_id' => $user->id ?? null,
                'error' => $e->getMessage()
            ]);

            return [
                'success' => false,
                'message' => $e->getMessage()
            ];
        }
    }
}
