<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Support\Facades\Route;
use Illuminate\Support\Facades\DB;
use App\Traits\FileUploadTrait;
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Facades\Storage;
use App\Jobs\RcsSendNumbersJob;
use App\Jobs\RcsSendDynamicNumbersJob;
use App\Models\RcsTemplate;
use App\Models\ContactDetails;
use App\Models\ManageRoute;
use Carbon\Carbon;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Log;

class RcsSendMessage extends Model
{
    use HasFactory, SoftDeletes, FileUploadTrait;

    protected $table = 'rcs_send_messages';

    protected $fillable = [
        'message',
        'template_name',
        'template_type',
        'bot_type',
        'campaign_name',
        'is_picked',
        'numbers_count',
        'msg_credit',
        'ip_address',
        'cut_off_value',
        'route',
        'job_id',
        'cut_off',
        'cut_off_throughput',
        'sent_at',
        'card_title',
        'url_link',
        'button_text',
        'image_path',
        'isActive',
        'isDeleted',
        'created_by',
        'updated_by',
        'deleted_by',
        'method',
        'userid',
        'is_variable',
    ];

    public static function getVariables($message)
    {
        $variables = [];

        if(preg_match_all('/[\[{]([a-zA-Z0-9_]+)[\]}]/', $message, $matches)) 
        {
            $variables = $matches[1];
        }

        return array_values(array_unique($variables));
    }

    public static function assignValueToVariable(string $message, array $variablesToAssign): string
    {
        $processedMessage = $message;

        foreach($variablesToAssign as $variableName => $variableValue) 
        {
            $placeholder = '[' . $variableName . ']';

            $processedMessage = str_replace($placeholder, $variableValue, $processedMessage);
        }

        return $processedMessage;
    }

    // RCS-only preview generator (mirrors SendMessage::generatePreviewMessages but also keeps
    // the raw variable key=>value pairs per row, so RcsSendDynamicNumbersJob's gateway payload
    // builders can send them straight to Vi/Pinnacle/ValueFirst instead of having to guess them
    // back out of the final resolved text).
    public static function generatePreviewMessages(Request $request): array
    {
        ini_set('memory_limit', '-1');
        set_time_limit(0);

        $template = $request->input('template');
        $rows = $request->input('rows');

        if(empty($template))
        {
            throw new \Exception('Template is required');
        }

        if(!is_array($rows) || empty($rows))
        {
            throw new \Exception('Rows must be a non-empty array');
        }

        $messages = [];

        // Auto-detect the phone number column from the first row
        $numberField = 'number';
        if(!empty($rows[0]) && is_array($rows[0]))
        {
            $firstRowKeys = array_keys($rows[0]);
            $lowerKeys = array_map(function($k) { return strtolower(trim($k)); }, $firstRowKeys);

            $possiblePhoneColumns = ['number', 'numbers', 'phone', 'phones', 'mobile', 'mobiles', 'contact_no', 'contactno', 'contact no', 'phone_no', 'phonenumber', 'mobile_number', 'mobilenumber'];

            foreach($possiblePhoneColumns as $possible)
            {
                $foundIndex = array_search($possible, $lowerKeys, true);
                if($foundIndex !== false)
                {
                    $numberField = $firstRowKeys[$foundIndex];
                    break;
                }
            }
        }

        // Placeholder names as written in the template (e.g. "name" from "[name]") — the
        // authoritative casing. CSV/Excel headers are matched to these case-insensitively,
        // since uploaded files commonly use a different case (e.g. "NAME") than the template.
        $placeholderNames = self::getVariables($template);

        foreach($rows as $row)
        {
            if(!is_array($row) || !isset($row[$numberField]))
            {
                continue;
            }

            $lowerRow = [];
            foreach($row as $key => $value)
            {
                if(is_string($key))
                {
                    $lowerRow[strtolower(trim($key))] = $value;
                }
            }

            $message = $template;
            $variables = [];

            foreach($placeholderNames as $placeholder)
            {
                $value = $lowerRow[strtolower($placeholder)] ?? '';
                $message = str_ireplace('{' . $placeholder . '}', $value, $message);
                $message = str_ireplace('[' . $placeholder . ']', $value, $message);
                $variables[$placeholder] = $value;
            }

            $charCount = mb_strlen($message);
            $isUnicode = preg_match('/[^\x00-\x7F]/', $message);
            $limit = $isUnicode ? 70 : 160;
            $bill = ceil($charCount / $limit);

            $messages[] = [
                'number' => $row[$numberField] ?? '',
                'message' => $message,
                'char_count' => $charCount,
                'bill' => $bill,
                'variables' => $variables,
            ];
        }

        return $messages;
    }

    public static function getRcsTemplateDetails($user, $bot_type)
    {
        $query = RcsTemplate::query();

        return $query->where('bot_type', strtolower($bot_type))
                    ->select('id', 'template_type', 'is_variable', 'template_name', 'template_text')
                    ->where('isActive', 1)
                    ->where('user_ID', $user->id)
                    ->get();
    }

    public static function getRcsRcsGatewayName($user_ID)
    {
        $user = User::find($user_ID);
    
        if(!$user) 
        {
            return ['status' => false, 'message' => 'User not found!'];
        }

        //  Get the RCS route ID
        $rcsRouteId = self::getRcsRouteId($user->route_IDs);
        
        if(!$rcsRouteId) 
        {
            return ['status' => false, 'message' => 'RCS Route Not Found!'];
        }

        $planAssignDetail   = PlanAssign::where('user_ID', $user_ID)
                            ->where('isActive', 1)
                            ->first();

        if(!$planAssignDetail)
        {
            return ['status' => false, 'message' => 'Plan Assignment Not Found!'];
        }

        $plan_ID = $planAssignDetail->plan_ID;

        $routingPlanDetail  = RoutingPlan::where('plan_ID', $plan_ID)
                            ->where('route_ID', $rcsRouteId)
                            ->where('isActive', 1)
                            ->first();

        if(!$routingPlanDetail)
        {
            return ['status' => false, 'message' => 'RCS Routing Plan Not Found!'];
        }

        $gatewayIds = $routingPlanDetail->gateway_ID ?? [];
        $gatewayPercents = $routingPlanDetail->gateway_percent ?? [];

        if(empty($gatewayIds)) 
        {
            return ['status' => false, 'message' => 'No gateways configured!'];
        }

        //  Get gateway details
        $rcsGateways = RcsGateway::whereIn('id', $gatewayIds)
                    ->get(['id', 'gateway_name', 'end_point'])
                    ->keyBy('id');

        //  Prepare response with gateway percentages
        $gateways = [];
        
        foreach($gatewayIds as $index => $gatewayId) 
        {
            if(isset($rcsGateways[$gatewayId])) 
            {
                $gateways[] = [
                    'gateway_name' => $rcsGateways[$gatewayId]->gateway_name,
                    'end_point' => $rcsGateways[$gatewayId]->end_point,
                    'gateway_percent' => $gatewayPercents[$index] ?? 0
                ];
            }
        }

        return [
            'status' => true,
            'data' => $gateways
        ];
    }

    private static function getRcsRouteId($routeIds)
    {
        if(empty($routeIds)) 
        {
            return null;
        }

        $routeIdArray = array_map('trim', explode(',', $routeIds));
        
        $rcsRoute   = ManageRoute::whereIn('id', $routeIdArray)
                    ->where(function($query) {
                        $query->where('route_name', 'like', '%RCS%')
                            ->orWhere('route_name', 'like', '%rcs%');
                    })
                    ->first();

        return $rcsRoute ? $rcsRoute->id : null;
    }

    public static function createAndProcess($request, $user)
    {
        //  Fetch Template Details
        $templateDetail = RcsTemplate::where('template_name', $request->template_name)
                        ->where('bot_type', $request->bot_type)
                        ->where('user_ID', $user->id)
                        ->where('isActive', 1)
                        ->first();

        if(!$templateDetail) 
        {
            return [
                'status' => false,
                'message' => 'RCS template not found for the specified bot type.'
            ];
        }

        $template_text = $templateDetail->template_text;
        $template_type = $templateDetail->template_type;
        $is_variable = $templateDetail->is_variable;

        $selectedRouteName = null;

        $creditPerNumber = 1;

        if($template_type === 'rich_card') 
        {
            $creditPerNumber = 2;
        }

        if(!empty($user->route_IDs)) 
        {
            $userRouteIds = explode(',', $user->route_IDs);
            $userRouteIds = array_map('trim', $userRouteIds);
            $userRouteIds = array_filter($userRouteIds);

            if(!empty($userRouteIds)) 
            {
                $matchingRoute = ManageRoute::whereIn('id', $userRouteIds)
                                            ->where('isActive', 1)
                                            ->where(function ($query) {
                                                $query->where('route_name', 'like', '%RCS%')
                                                        ->orWhere('route_name', 'like', '%rcs%');
                                            })
                                            ->first();

                if($matchingRoute) 
                {
                    $selectedRouteName = $matchingRoute->route_name;

                    //  Fetch start time and end time from selected route name
                    $route_id = $matchingRoute->id;
                    $start_time = $matchingRoute->start_time;
                    $end_time = $matchingRoute->end_time;

                    $currentTime = Carbon::now();

                    try 
                    {
                        $startTime = Carbon::parse($start_time)->setDate($currentTime->year, $currentTime->month, $currentTime->day);
                        $endTime = Carbon::parse($end_time)->setDate($currentTime->year, $currentTime->month, $currentTime->day);
                    } 
                    catch(\Exception $e) 
                    {
                        return ['status' => false, 'message' => 'Invalid time format for route start or end time.'];
                    }

                    if(!$currentTime->between($startTime, $endTime)) 
                    {
                        return ['status' => false, 'message' => 'Can not send message for selected route.'];
                    }

                    //  Gateway Name & End Point
                    $planAssignDetail   = PlanAssign::where('user_ID', $user->id)
                                        ->where('isActive', 1)
                                        ->first();

                    if($planAssignDetail)
                    {
                        $plan_ID = $planAssignDetail->plan_ID;

                        $routingPlanDetail  = RoutingPlan::where('plan_ID', $plan_ID)
                                            ->where('route_ID', $route_id)
                                            ->where('isActive', 1)
                                            ->first();

                        if($routingPlanDetail)
                        {
                            $gateway_ID = $routingPlanDetail->gateway_ID;

                            $rcsGatewayDetail   = RcsGateway::where('id', $gateway_ID)
                                                ->where('isActive', 1)
                                                ->first();
                        
                            if($rcsGatewayDetail) 
                            {
                                $gateway_name = $rcsGatewayDetail->gateway_name;
                                $end_point = $rcsGatewayDetail->end_point;

                                $rcsTokenDetail = RcsToken::where('user_ID', $user->id)
                                                ->where('bot_type', $request->bot_type)
                                                ->where('gateway_id', $gateway_ID)
                                                ->where('isActive', 1)
                                                ->first();

                                if($rcsTokenDetail) 
                                {
                                    $client_id = $rcsTokenDetail->client_id;
                                    $client_secret = $rcsTokenDetail->client_secret;

                                    $tokenData = null;

                                    try 
                                    {
                                        if($gateway_name === 'jio') 
                                        {
                                            $tokenData = self::getJioToken($end_point, $client_id, $client_secret);

                                            if(!isset($tokenData['access_token'])) 
                                            {
                                                throw new \Exception('Access token not found in response');
                                            }

                                            $access_token = $tokenData['access_token'];

                                            //  Update access token
                                            $rcsTokenDetail->update([
                                                'access_token' => $access_token,
                                                'updated_at' => now()
                                            ]);
                                        } 
                                        elseif($gateway_name === 'vi') 
                                        {
                                            $tokenData = self::getViToken($end_point, $client_id, $client_secret);

                                            if(!isset($tokenData['access_token'])) 
                                            {
                                               throw new \Exception('Access token not found in response');
                                            }

                                            $access_token = $tokenData['access_token'];

                                            //  Update access token
                                            $rcsTokenDetail->update([
                                                'access_token' => $access_token,
                                                'updated_at' => now()
                                            ]);
                                        }
                                        else
                                        {
                                            $access_token = $rcsTokenDetail->access_token;
                                        }
                                    }
                                    catch(\Exception $e)
                                    {
                                        // Gateway token endpoint unavailable — fall back to stored token
                                        $access_token = $rcsTokenDetail->access_token;

                                        if(empty($access_token))
                                        {
                                            return ['status' => false, 'message' => 'Failed to generate access token.'];
                                        }
                                    }
                                }
                                else
                                {
                                    return ['status' => false, 'message' => 'RCS token details not found!'];
                                }
                            }
                            else
                            {
                                return ['status' => false, 'message' => 'RCS gateway details not found!'];
                            }
                        }
                        else
                        {
                            return ['status' => false, 'message' => 'Routing plan details not found!'];
                        }
                    }
                    else
                    {
                        return ['status' => false, 'message' => 'Plan assign details not found!'];
                    }

                    //  Credit Management
                    $creditManageDetail = CreditManage::where('route_id', $route_id)
                                        ->where('userid', $user->id)
                                        ->where('isActive', 1)
                                        ->first();

                    if($creditManageDetail)
                    {
                        $balance_credit = $creditManageDetail->balance;
                    }
                    else
                    {
                        return ['status' => false, 'message' => 'No credit balance found for this route.'];
                    }

                    //  Calculate estimated message credit cost
                    $charCount = mb_strlen($request->message);
                    $isUnicode = preg_match('/[^\x00-\x7F]/', $request->message);
                    $limit = $isUnicode ? 70 : 160;
                    $msgCredit = ceil($charCount / $limit);

                    $msgCredit = $creditPerNumber;
                    
                    //  Estimate number count based on method
                    $estimatedNumberCount = 0;

                    if($request['method'] === 'Numbers' && !empty($request['numbers'])) 
                    {
                        $numbersToProcess = preg_split('/[, \r\n]+/', trim($request->numbers), -1, PREG_SPLIT_NO_EMPTY);
                        $numbersToProcess = array_map('trim', $numbersToProcess);
                        $numbersToProcess = self::removeDuplicateNumbers($numbersToProcess);
                        $estimatedNumberCount = count($numbersToProcess);
                    }
                    elseif($request->method === 'Groups' && !empty($request->group_IDs)) 
                    {
                        $groupIds = $request->group_IDs;
                        $contacts = ContactDetails::whereIn('group_ID', $groupIds)->pluck('contact_no')->toArray();
                        $numbers = array_unique(array_map('trim', $contacts));
                        $numbers = self::removeDuplicateNumbers($numbers);
                        $estimatedNumberCount = count($numbers);
                    }
                    elseif($request->method === 'CSV' && $request->hasFile('csv_file')) 
                    {
                        $file = $request->file('csv_file');
                        $numbers = extractNumbersFromFile($file);
                        $numbers = self::removeDuplicateNumbers($numbers);
                        $estimatedNumberCount = count($numbers);
                    }
                    
                    $totalCreditCost = $creditPerNumber * $estimatedNumberCount;
                    
                    //  Check if balance is sufficient
                    if($totalCreditCost > $balance_credit) 
                    {
                        return ['status' => false, 'message' => 'Insufficient Balance'];
                    }
                }
            }
        }

        if(is_null($selectedRouteName)) 
        {
            return [
                'status' => false,
                'message' => 'RCS route is not assigned.'
            ];
        }

        //  Image Upload
        $imagePath = null;

        if($request->hasFile('image_path') && $request->file('image_path') instanceof UploadedFile) 
        {
            $instance = new self();
            $file = $request->file('image_path');
            
            try 
            {
                $imagePath = $instance->uploadFile(
                    $file,
                    ['jpg', 'jpeg', 'png', 'webp'],
                    2 * 1024 * 1024,
                    'RcsImages'
                );
            } 
            catch (\Exception $e) {
                return [
                    'status' => false,
                    'message' => 'Image upload failed: ' . $e->getMessage()
                ];
            }
        }

        $route = Route::currentRouteName();

        if($route === 'bulk_rcs_sms.rcsSendMessages') 
        {
            //  Template message validation
            $validationResult = self::validateStrictTemplate($template_text, $request->message);
            
            if($validationResult['status'] === false) 
            {
                return $validationResult;
            }

            $messageData = [
                'route' => $selectedRouteName,
                'template_type' => $template_type,
                'bot_type' => $request->bot_type,
                'template_name' => $request->template_name,
                'campaign_name' => $request->campaign_name,
                'message' => $template_text,
                'job_id' => generateRandomAlphaNumeric(15),
                'ip_address' => $request->ip(),
                'method' => $request->method,
                'userid' => $user->id,
                'msg_credit' => $totalCreditCost,
                'created_by' => $user->id,
                'created_at' => now(),
                'updated_at' => null,
            ];

            if($request->has('card_title')) 
            {
                $messageData['card_title'] = $request->card_title;
            }

            if($request->has('url_link')) 
            {
                $messageData['url_link'] = $request->url_link;
            }

            if($request->has('button_text')) 
            {
                $messageData['button_text'] = $request->button_text;
            }

            if($imagePath) 
            {
                $messageData['image_path'] = $imagePath;
            }

            $message = self::create($messageData);

            //  Deduct credit from balance
            $newBalance = $balance_credit - $totalCreditCost;
            $creditManageDetail->update(['balance' => $newBalance]);
            
            //  Create transaction log
            CampaignTransactionLog::create([
                'job_id' => $message->job_id,
                'campaign_name' => $request->campaign_name,
                'total_numbers' => $estimatedNumberCount,
                'total_credit' => $totalCreditCost,
                'previous_balance' => $balance_credit,
                'new_balance' => $newBalance,
                'created_by' => $user->id,
                'created_at' => now(),
                'updated_at' => null,
            ]);

            $numbersToProcess = [];

            if($request['method'] === 'Numbers' && !empty($request['numbers'])) 
            {
                $numbersToProcess = preg_split('/[, \r\n]+/', trim($request->numbers), -1, PREG_SPLIT_NO_EMPTY);
                $numbersToProcess = array_map('trim', $numbersToProcess);

                //  Remove duplicate mobile numbers
		        $numbersToProcess = self::removeDuplicateNumbers($numbersToProcess);

                dispatch(new RcsSendNumbersJob($numbersToProcess, $message, $user->id, $request->message, $route_id, $creditPerNumber));
            }

            if($request->method === 'Groups' && !empty($request->group_IDs)) 
            {
                $groupIds = $request->group_IDs;

                // Fetch numbers from contact_details
                $contacts = ContactDetails::whereIn('group_ID', $groupIds)->pluck('contact_no')->toArray();

                // Deduplicate & clean
                $numbers = array_unique(array_map('trim', $contacts));

                //  Remove duplicate mobile numbers
		        $numbers = self::removeDuplicateNumbers($numbers);

                dispatch(new RcsSendNumbersJob($numbers, $message, $user->id, $request->message, $route_id, $creditPerNumber));
            }

            if($request->method === 'CSV' && $request->hasFile('csv_file')) 
            {
                $file = $request->file('csv_file');
                $numbers = extractNumbersFromFile($file);

                //  Remove duplicate mobile numbers
		        $numbers = self::removeDuplicateNumbers($numbers);

                dispatch(new RcsSendNumbersJob($numbers, $message, $user->id, $request->message, $route_id, $creditPerNumber));
            }

            return [
                'status' => true,
                'message' => 'RCS message created successfully.',
                'data' => $message
            ];
        }
        else if($route === 'dynamic_rcs_sms.rcsSendDynamicMessages')
        {
            $messagesFromRequest = $request->input('messages');
            $hasPreviewId = $request->filled('preview_id');

            if(!is_array($messagesFromRequest) || empty($messagesFromRequest))
            {
                return [
                    'status' => false,
                    'message' => '',
                    'errors' => ['messages' => ['Messages array is required and must be a valid array.']]
                ];
            }

            // Skip template validation if preview_id is provided (already validated during preview)
            if (!$hasPreviewId) {
                // Quick validation - only validate first few messages for immediate response
                $validateCount = min(5, count($messagesFromRequest));
                for($i = 0; $i < $validateCount; $i++)
                {
                    $msg = $messagesFromRequest[$i];
                    if(!isset($msg['message']))
                    {
                        return [
                            'status' => false,
                            'message' => 'Message content missing for entry '.$i,
                            'errors' => ['messages.' . $i . '.message' => ['Message content is required.']]
                        ];
                    }

                    $validationResult = self::validateStrictTemplate($template_text, $msg['message']);

                    if($validationResult['status'] === false)
                    {
                        return $validationResult;
                    }
                }
            } else {
                Log::info('RCS: Skipping template validation - using preview_id', [
                    'preview_id' => $request->input('preview_id'),
                    'messages_count' => count($messagesFromRequest)
                ]);
            }

            //  Calculate total credit cost for dynamic messages
            $totalCreditCost = $creditPerNumber * count($messagesFromRequest);

            if($totalCreditCost > $balance_credit)
            {
                return ['status' => false, 'message' => 'Insufficient Balance'];
            }

            //  Generate job_id for tracking
            $jobId = generateRandomAlphaNumeric(15);

            //  Return immediate response - processing will happen in background
            return [
                'status' => true,
                'immediate_response' => true,
                'message' => 'RCS dynamic message processing started.',
                'job_id' => $jobId,
                'estimated_count' => count($messagesFromRequest),
                'route_id' => $route_id,
                'credit_per_number' => $creditPerNumber,
                'total_credit_cost' => $totalCreditCost,
                'balance_credit' => $balance_credit,
                'selected_route_name' => $selectedRouteName,
                'template_text' => $template_text,
                'template_type' => $template_type,
                'image_path' => $imagePath
            ];
        }
    }

    /**
     * Process RCS Dynamic messages in background
     */
    public static function processRcsDynamicInBackground($request, $user, $jobId)
    {
        try {
            Log::info('processRcsDynamicInBackground started', [
                'job_id' => $jobId,
                'user_id' => $user->id,
                'messages_count' => count($request->input('messages', []))
            ]);

            //  Fetch Template Details
            $templateDetail = RcsTemplate::where('template_name', $request->template_name)
                            ->where('bot_type', $request->bot_type)
                            ->where('user_ID', $user->id)
                            ->where('isActive', 1)
                            ->first();

            if(!$templateDetail)
            {
                return [
                    'status' => false,
                    'message' => 'RCS template not found for the specified bot type.'
                ];
            }

            $template_text = $templateDetail->template_text;
            $template_type = $templateDetail->template_type;
            $is_variable   = $templateDetail->is_variable;

            $creditPerNumber = 1;
            if($template_type === 'rich_card')
            {
                $creditPerNumber = 2;
            }

            //  Get route details
            $selectedRouteName = null;
            $route_id = null;

            if(!empty($user->route_IDs))
            {
                $userRouteIds = explode(',', $user->route_IDs);
                $userRouteIds = array_map('trim', $userRouteIds);
                $userRouteIds = array_filter($userRouteIds);

                if(!empty($userRouteIds))
                {
                    $matchingRoute = ManageRoute::whereIn('id', $userRouteIds)
                                                ->where('isActive', 1)
                                                ->where(function ($query) {
                                                    $query->where('route_name', 'like', '%RCS%')
                                                            ->orWhere('route_name', 'like', '%rcs%');
                                                })
                                                ->first();

                    if($matchingRoute)
                    {
                        $selectedRouteName = $matchingRoute->route_name;
                        $route_id = $matchingRoute->id;
                    }
                }
            }

            if(is_null($selectedRouteName))
            {
                return [
                    'status' => false,
                    'message' => 'RCS route is not assigned.'
                ];
            }

            //  Credit Management
            $creditManageDetail = CreditManage::where('route_id', $route_id)
                                ->where('userid', $user->id)
                                ->where('isActive', 1)
                                ->first();

            if(!$creditManageDetail)
            {
                return ['status' => false, 'message' => 'No credit balance found for this route.'];
            }

            $balance_credit = $creditManageDetail->balance;

            $messagesFromRequest = $request->input('messages');

            if(!is_array($messagesFromRequest) || empty($messagesFromRequest))
            {
                return [
                    'status' => false,
                    'message' => 'Messages array is required and must be a valid array.'
                ];
            }

            //  Check if preview_id is present - skip template validation if so (already validated during preview)
            $hasPreviewId = $request->input('preview_id') ? true : false;

            if ($hasPreviewId) {
                Log::info('RCS Background: Skipping template validation - using preview_id', [
                    'job_id' => $jobId,
                    'preview_id' => $request->input('preview_id'),
                    'messages_count' => count($messagesFromRequest)
                ]);
            } else {
                //  Validate all messages only if NOT using preview_id
                foreach($messagesFromRequest as $index => $msg)
                {
                    if(!isset($msg['message']))
                    {
                        Log::warning('Message content missing', ['index' => $index, 'job_id' => $jobId]);
                        continue;
                    }

                    $validationResult = self::validateStrictTemplate($template_text, $msg['message']);

                    if($validationResult['status'] === false)
                    {
                        Log::warning('Template validation failed for message', [
                            'index' => $index,
                            'job_id' => $jobId
                        ]);
                    }
                }
            }

            //  Remove duplicate numbers from messages FIRST
            self::removeDuplicateDynamicMessages($request);
            $messagesFromRequest = $request->input('messages');

            //  Calculate total credit cost AFTER removing duplicates
            $uniqueNumbersCount = count($messagesFromRequest);
            $totalCreditCost = $creditPerNumber * $uniqueNumbersCount;

            Log::info('RCS Credit calculation', [
                'unique_numbers' => $uniqueNumbersCount,
                'credit_per_number' => $creditPerNumber,
                'template_type' => $template_type,
                'total_credit_cost' => $totalCreditCost
            ]);

            if($totalCreditCost > $balance_credit)
            {
                return ['status' => false, 'message' => 'Insufficient Balance'];
            }

            $messageData = [
                'route' => $selectedRouteName,
                'template_type' => $template_type,
                'bot_type' => $request->bot_type,
                'template_name' => $request->template_name,
                'campaign_name' => $request->campaign_name,
                'message' => $template_text,
                'is_variable' => $is_variable,
                'job_id' => $jobId,
                'ip_address' => $request->ip() ?? '0.0.0.0',
                'method' => $request->method ?? 'Dynamic',
                'userid' => $user->id,
                'numbers_count' => $uniqueNumbersCount,
                'msg_credit' => $totalCreditCost,
                'created_by' => $user->id,
                'created_at' => now(),
                'updated_at' => null,
            ];

            if($request->has('card_title'))
            {
                $messageData['card_title'] = $request->card_title;
            }

            if($request->has('url_link'))
            {
                $messageData['url_link'] = $request->url_link;
            }

            if($request->has('button_text'))
            {
                $messageData['button_text'] = $request->button_text;
            }

            if($request->has('image_path'))
            {
                $messageData['image_path'] = $request->image_path;
            }

            $message = self::create($messageData);

            //  Update credit balance
            $newBalance = $balance_credit - $totalCreditCost;
            $creditManageDetail->update(['balance' => $newBalance]);

            //  Create transaction log
            CampaignTransactionLog::create([
                'job_id' => $message->job_id,
                'campaign_name' => $request->campaign_name,
                'total_numbers' => count($messagesFromRequest),
                'total_credit' => $totalCreditCost,
                'previous_balance' => $balance_credit,
                'new_balance' => $newBalance,
                'created_by' => $user->id,
                'created_at' => now(),
                'updated_at' => null,
            ]);

            //  Store messages in cache to avoid serialization timeout for large datasets
            $messagesCacheKey = 'rcs_send_numbers_' . $jobId;
            Cache::put($messagesCacheKey, $messagesFromRequest, now()->addHours(4));

            Log::info('RCS messages stored in cache for RcsSendDynamicNumbersJob', [
                'job_id' => $jobId,
                'cache_key' => $messagesCacheKey,
                'messages_count' => count($messagesFromRequest)
            ]);

            //  Dispatch job to send messages (pass cache key instead of full array)
            dispatch(new RcsSendDynamicNumbersJob($messagesCacheKey, $message, $user->id, $route_id, $creditPerNumber));

            Log::info('processRcsDynamicInBackground completed', [
                'job_id' => $jobId,
                'user_id' => $user->id,
                'numbers_count' => count($messagesFromRequest),
                'total_credit' => $totalCreditCost
            ]);

            return [
                'status' => true,
                'message' => 'RCS dynamic messages processed successfully.',
                'numbers_count' => count($messagesFromRequest)
            ];

        } catch (\Exception $e) {
            Log::error('processRcsDynamicInBackground error', [
                'job_id' => $jobId,
                'error' => $e->getMessage(),
                'trace' => $e->getTraceAsString()
            ]);

            return [
                'status' => false,
                'message' => 'Error processing RCS dynamic messages: ' . $e->getMessage()
            ];
        }
    }

    public static function createAndProcessWithAuth(Request $request)
    {
        //  Authenticate user based on request method
        $user = self::authenticateUser($request);
        
        if(!$user) 
        {
            return [
                'status' => false,
                'message' => 'Invalid Username or API Key.'
            ];
        }

        //  Login the user
        Auth::login($user);

        //  Process message creation (your existing logic)
        return static::httpCreateAndProcess($request, $user);
    }

    public static function createAndProcessWithGroupAuth(Request $request)
    {
        //  Authenticate user based on request method
        $user = self::authenticateUser($request);
        
        if(!$user) 
        {
            return [
                'status' => false,
                'message' => 'Invalid Username or API Key.'
            ];
        }

        //  Login the user
        Auth::login($user);

        //  Process message creation (your existing logic)
        return static::httpCreateAndProcessGroup($request, $user);
    }

    private static function authenticateUser(Request $request): ?User
    {
        $username = $request->header('username') ?: $request->input('username');
        $apiKey = $request->header('api_key') ?: $request->input('api_key');

        if(!$username || !$apiKey)
        {
            return null;
        }

        return User::authenticateByApiKey($username, $apiKey);
    }

    public static function httpCreateAndProcess($request, $user)
    {
        //  Fetch Template Details
        $templateDetail = RcsTemplate::where('template_name', $request->template_name)
                        ->where('bot_type', $request->bot_type)
                        ->where('user_ID', $user->id)
                        ->where('isActive', 1)
                        ->first();

        if(!$templateDetail) 
        {
            return [
                'status' => false,
                'message' => 'RCS template not found for the specified bot type.'
            ];
        }

        $template_text = $templateDetail->template_text;
        $template_type = $templateDetail->template_type;
        $is_variable = $templateDetail->is_variable;

        $selectedRouteName = null;

        $creditPerNumber = 1;

        if($template_type === 'rich_card') 
        {
            $creditPerNumber = 2;
        }

        if(!empty($user->route_IDs)) 
        {
            $userRouteIds = explode(',', $user->route_IDs);
            $userRouteIds = array_map('trim', $userRouteIds);
            $userRouteIds = array_filter($userRouteIds);

            if(!empty($userRouteIds)) 
            {
                $matchingRoute = ManageRoute::whereIn('id', $userRouteIds)
                                            ->where('isActive', 1)
                                            ->where(function ($query) {
                                                $query->where('route_name', 'like', '%RCS%')
                                                        ->orWhere('route_name', 'like', '%rcs%');
                                            })
                                            ->first();

                if($matchingRoute) 
                {
                    $selectedRouteName = $matchingRoute->route_name;

                    //  Fetch start time and end time from selected route name
                    $route_id = $matchingRoute->id;
                    $start_time = $matchingRoute->start_time;
                    $end_time = $matchingRoute->end_time;

                    $currentTime = Carbon::now();

                    try 
                    {
                        $startTime = Carbon::parse($start_time)->setDate($currentTime->year, $currentTime->month, $currentTime->day);
                        $endTime = Carbon::parse($end_time)->setDate($currentTime->year, $currentTime->month, $currentTime->day);
                    } 
                    catch(\Exception $e) 
                    {
                        return ['status' => false, 'message' => 'Invalid time format for route start or end time.'];
                    }

                    if(!$currentTime->between($startTime, $endTime)) 
                    {
                        return ['status' => false, 'message' => 'Can not send message for selected route.'];
                    }

                    //  Gateway Name & End Point
                    $planAssignDetail   = PlanAssign::where('user_ID', $user->id)
                                        ->where('isActive', 1)
                                        ->first();

                    if($planAssignDetail)
                    {
                        $plan_ID = $planAssignDetail->plan_ID;

                        $routingPlanDetail  = RoutingPlan::where('plan_ID', $plan_ID)
                                            ->where('route_ID', $route_id)
                                            ->where('isActive', 1)
                                            ->first();

                        if($routingPlanDetail)
                        {
                            $gateway_ID = $routingPlanDetail->gateway_ID;

                            $rcsGatewayDetail   = RcsGateway::where('id', $gateway_ID)
                                                ->where('isActive', 1)
                                                ->first();
                        
                            if($rcsGatewayDetail) 
                            {
                                $gateway_name = $rcsGatewayDetail->gateway_name;
                                $end_point = $rcsGatewayDetail->end_point;

                                $rcsTokenDetail = RcsToken::where('user_ID', $user->id)
                                                ->where('bot_type', $request->bot_type)
                                                ->where('gateway_id', $gateway_ID)
                                                ->where('isActive', 1)
                                                ->first();

                                if($rcsTokenDetail) 
                                {
                                    $client_id = $rcsTokenDetail->client_id;
                                    $client_secret = $rcsTokenDetail->client_secret;

                                    $tokenData = null;

                                    try 
                                    {
                                        if($gateway_name === 'jio') 
                                        {
                                            $tokenData = self::getJioToken($end_point, $client_id, $client_secret);

                                            $access_token = $tokenData['access_token'];

                                            //  Update access token
                                            $rcsTokenDetail->update([
                                                'access_token' => $access_token,
                                                'updated_at' => now()
                                            ]);
                                        } 
                                        elseif($gateway_name === 'vi') 
                                        {
                                            $tokenData = self::getViToken($end_point, $client_id, $client_secret);

                                            

                                            $access_token = $tokenData['access_token'];

                                            //  Update access token
                                            $rcsTokenDetail->update([
                                                'access_token' => $access_token,
                                                'updated_at' => now()
                                            ]);
                                        }
                                        else
                                        {
                                            $access_token = $rcsTokenDetail->access_token;
                                        }
                                    }
                                    catch(\Exception $e)
                                    {
                                        // Gateway token endpoint unavailable — fall back to stored token
                                        $access_token = $rcsTokenDetail->access_token;

                                        if(empty($access_token))
                                        {
                                            return ['status' => false, 'message' => 'Failed to generate access token.'];
                                        }
                                    }
                                }
                                else
                                {
                                    return ['status' => false, 'message' => 'RCS token details not found!'];
                                }
                            }
                            else
                            {
                                return ['status' => false, 'message' => 'RCS gateway details not found!'];
                            }
                        }
                        else
                        {
                            return ['status' => false, 'message' => 'Routing plan details not found!'];
                        }
                    }
                    else
                    {
                        return ['status' => false, 'message' => 'Plan assign details not found!'];
                    }

                    //  Credit Management
                    $creditManageDetail = CreditManage::where('route_id', $route_id)
                                        ->where('userid', $user->id)
                                        ->where('isActive', 1)
                                        ->first();

                    if($creditManageDetail)
                    {
                        $balance_credit = $creditManageDetail->balance;
                    }
                    else
                    {
                        return ['status' => false, 'message' => 'No credit balance found for this route.'];
                    }

                    //  Calculate estimated message credit cost
                    $charCount = mb_strlen($request->message);
                    $isUnicode = preg_match('/[^\x00-\x7F]/', $request->message);
                    $limit = $isUnicode ? 70 : 160;
                    $msgCredit = ceil($charCount / $limit);

                    $msgCredit = $creditPerNumber;
                    
                    //  Estimate number count based on method
                    $estimatedNumberCount = 0;

                    $numbersToProcess = preg_split('/[, \r\n]+/', trim($request->numbers), -1, PREG_SPLIT_NO_EMPTY);
                    $numbersToProcess = array_map('trim', $numbersToProcess);
                    $numbersToProcess = self::removeDuplicateNumbers($numbersToProcess);
                    $estimatedNumberCount = count($numbersToProcess);
                    
                    $totalCreditCost = $creditPerNumber * $estimatedNumberCount;
                    
                    //  Check if balance is sufficient
                    if($totalCreditCost > $balance_credit) 
                    {
                        return ['status' => false, 'message' => 'Insufficient Balance'];
                    }
                }
            }
        }

        if(is_null($selectedRouteName)) 
        {
            return [
                'status' => false,
                'message' => 'RCS route is not assigned.'
            ];
        }

        //  Image Upload
        $imagePath = null;

        if($request->hasFile('image_path') && $request->file('image_path') instanceof UploadedFile) 
        {
            $instance = new self();
            $file = $request->file('image_path');
            
            try 
            {
                $imagePath = $instance->uploadFile(
                    $file,
                    ['jpg', 'jpeg', 'png', 'webp'],
                    2 * 1024 * 1024,
                    'RcsImages'
                );
            } 
            catch (\Exception $e) {
                return [
                    'status' => false,
                    'message' => 'Image upload failed: ' . $e->getMessage()
                ];
            }
        }

        //  Template message validation
        //  "variables" takes priority whenever it's provided, even if "message" also
        //  has content — that lets a caller safely keep both fields populated.
        if($request->filled('variables'))
        {
            $validationResult = self::buildMessageFromVariableList($template_text, $request->input('variables', ''));

            if($validationResult['status'] === false)
            {
                return $validationResult;
            }

            $finalMessage = $validationResult['message'];
            $variables = $validationResult['variables'] ?? [];
        }
        else
        {
            $validationResult = self::validateStrictTemplate($template_text, $request->message);

            if($validationResult['status'] === false)
            {
                return $validationResult;
            }

            $finalMessage = $request->message;

            // Real variable values extracted while matching the message against the
            // template — passed straight to the gateway instead of being guessed back
            // out of the resolved text a second time.
            $variables = $validationResult['variables'] ?? [];
        }

        $messageData = [
            'route' => $selectedRouteName,
            'template_type' => $template_type,
            'bot_type' => $request->bot_type,
            'template_name' => $request->template_name,
            'campaign_name' => $request->campaign_name,
            'message' => $template_text,
            'job_id' => generateRandomAlphaNumeric(15),
            'ip_address' => $request->ip(),
            'method' => 'API',
            'userid' => $user->id,
            'msg_credit' => $totalCreditCost,
            'created_by' => $user->id,
            'created_at' => now(),
            'updated_at' => null,
        ];

        if($request->has('card_title')) 
        {
            $messageData['card_title'] = $request->card_title;
        }

        if($request->has('url_link')) 
        {
            $messageData['url_link'] = $request->url_link;
        }

        if($request->has('button_text')) 
        {
            $messageData['button_text'] = $request->button_text;
        }

        if($imagePath) 
        {
            $messageData['image_path'] = $imagePath;
        }

        $message = self::create($messageData);

        //  Deduct credit from balance
        $newBalance = $balance_credit - $totalCreditCost;
        $creditManageDetail->update(['balance' => $newBalance]);
        
        //  Create transaction log
        CampaignTransactionLog::create([
            'job_id' => $message->job_id,
            'campaign_name' => $request->campaign_name,
            'total_numbers' => $estimatedNumberCount,
            'total_credit' => $totalCreditCost,
            'previous_balance' => $balance_credit,
            'new_balance' => $newBalance,
            'created_by' => $user->id,
            'created_at' => now(),
            'updated_at' => null,
        ]);

        $numbersToProcess = [];

        $numbersToProcess = preg_split('/[, \r\n]+/', trim($request->numbers), -1, PREG_SPLIT_NO_EMPTY);
        $numbersToProcess = array_map('trim', $numbersToProcess);

        //  Remove duplicate mobile numbers
        $numbersToProcess = self::removeDuplicateNumbers($numbersToProcess);

        dispatch(new RcsSendNumbersJob($numbersToProcess, $message, $user->id, $finalMessage, $route_id, $creditPerNumber, $variables));

        return [
            'status' => true,
            'message' => 'RCS message created successfully.',
            'data' => $message
        ];
    }

    public static function httpCreateAndProcessGroup($request, $user)
    {
        //  Fetch Template Details
        $templateDetail = RcsTemplate::where('template_name', $request->template_name)
                        ->where('bot_type', $request->bot_type)
                        ->where('user_ID', $user->id)
                        ->where('isActive', 1)
                        ->first();

        if(!$templateDetail) 
        {
            return [
                'status' => false,
                'message' => 'RCS template not found for the specified bot type.'
            ];
        }

        $template_text = $templateDetail->template_text;
        $template_type = $templateDetail->template_type;
        $is_variable = $templateDetail->is_variable;

        $selectedRouteName = null;

        $creditPerNumber = 1;

        if($template_type === 'rich_card') 
        {
            $creditPerNumber = 2;
        }

        if(!empty($user->route_IDs)) 
        {
            $userRouteIds = explode(',', $user->route_IDs);
            $userRouteIds = array_map('trim', $userRouteIds);
            $userRouteIds = array_filter($userRouteIds);

            if(!empty($userRouteIds)) 
            {
                $matchingRoute = ManageRoute::whereIn('id', $userRouteIds)
                                            ->where('isActive', 1)
                                            ->where(function ($query) {
                                                $query->where('route_name', 'like', '%RCS%')
                                                        ->orWhere('route_name', 'like', '%rcs%');
                                            })
                                            ->first();

                if($matchingRoute) 
                {
                    $selectedRouteName = $matchingRoute->route_name;

                    //  Fetch start time and end time from selected route name
                    $route_id = $matchingRoute->id;
                    $start_time = $matchingRoute->start_time;
                    $end_time = $matchingRoute->end_time;

                    $currentTime = Carbon::now();

                    try 
                    {
                        $startTime = Carbon::parse($start_time)->setDate($currentTime->year, $currentTime->month, $currentTime->day);
                        $endTime = Carbon::parse($end_time)->setDate($currentTime->year, $currentTime->month, $currentTime->day);
                    } 
                    catch(\Exception $e) 
                    {
                        return ['status' => false, 'message' => 'Invalid time format for route start or end time.'];
                    }

                    if(!$currentTime->between($startTime, $endTime)) 
                    {
                        return ['status' => false, 'message' => 'Can not send message for selected route.'];
                    }

                    //  Gateway Name & End Point
                    $planAssignDetail   = PlanAssign::where('user_ID', $user->id)
                                        ->where('isActive', 1)
                                        ->first();

                    if($planAssignDetail)
                    {
                        $plan_ID = $planAssignDetail->plan_ID;

                        $routingPlanDetail  = RoutingPlan::where('plan_ID', $plan_ID)
                                            ->where('route_ID', $route_id)
                                            ->where('isActive', 1)
                                            ->first();

                        if($routingPlanDetail)
                        {
                            $gateway_ID = $routingPlanDetail->gateway_ID;

                            $rcsGatewayDetail   = RcsGateway::where('id', $gateway_ID)
                                                ->where('isActive', 1)
                                                ->first();
                        
                            if($rcsGatewayDetail) 
                            {
                                $gateway_name = $rcsGatewayDetail->gateway_name;
                                $end_point = $rcsGatewayDetail->end_point;

                                $rcsTokenDetail = RcsToken::where('user_ID', $user->id)
                                                ->where('bot_type', $request->bot_type)
                                                ->where('gateway_id', $gateway_ID)
                                                ->where('isActive', 1)
                                                ->first();

                                if($rcsTokenDetail) 
                                {
                                    $client_id = $rcsTokenDetail->client_id;
                                    $client_secret = $rcsTokenDetail->client_secret;

                                    $tokenData = null;

                                    try 
                                    {
                                        if($gateway_name === 'jio') 
                                        {
                                            $tokenData = self::getJioToken($end_point, $client_id, $client_secret);

                                            $access_token = $tokenData['access_token'];

                                            //  Update access token
                                            $rcsTokenDetail->update([
                                                'access_token' => $access_token,
                                                'updated_at' => now()
                                            ]);
                                        } 
                                        elseif($gateway_name === 'vi') 
                                        {
                                            $tokenData = self::getViToken($end_point, $client_id, $client_secret);

                                            $access_token = $tokenData['access_token'];

                                            //  Update access token
                                            $rcsTokenDetail->update([
                                                'access_token' => $access_token,
                                                'updated_at' => now()
                                            ]);
                                        }
                                        else
                                        {
                                            $access_token = $rcsTokenDetail->access_token;
                                        }
                                    }
                                    catch(\Exception $e)
                                    {
                                        // Gateway token endpoint unavailable — fall back to stored token
                                        $access_token = $rcsTokenDetail->access_token;

                                        if(empty($access_token))
                                        {
                                            return ['status' => false, 'message' => 'Failed to generate access token.'];
                                        }
                                    }
                                }
                                else
                                {
                                    return ['status' => false, 'message' => 'RCS token details not found!'];
                                }
                            }
                            else
                            {
                                return ['status' => false, 'message' => 'RCS gateway details not found!'];
                            }
                        }
                        else
                        {
                            return ['status' => false, 'message' => 'Routing plan details not found!'];
                        }
                    }
                    else
                    {
                        return ['status' => false, 'message' => 'Plan assign details not found!'];
                    }

                    //  Credit Management
                    $creditManageDetail = CreditManage::where('route_id', $route_id)
                                        ->where('userid', $user->id)
                                        ->where('isActive', 1)
                                        ->first();

                    if($creditManageDetail)
                    {
                        $balance_credit = $creditManageDetail->balance;
                    }
                    else
                    {
                        return ['status' => false, 'message' => 'No credit balance found for this route.'];
                    }

                    //  Calculate estimated message credit cost
                    $charCount = mb_strlen($request->message);
                    $isUnicode = preg_match('/[^\x00-\x7F]/', $request->message);
                    $limit = $isUnicode ? 70 : 160;
                    $msgCredit = ceil($charCount / $limit);

                    $msgCredit = $creditPerNumber;
                    
                    //  Estimate number count based on method
                    $estimatedNumberCount = 0;

                    $groupNames = explode(',', $request->group_name);
                    $groupNames = array_map('trim', $groupNames);

                    $group_details = MST_group::where('group_name', $groupNames)->where('user_ID', $user->id)->where('isActive', 1)->get();

                    $groupIds = $group_details->pluck('id')->toArray();

                    $contacts = ContactDetails::whereIn('group_ID', $groupIds)->where('user_ID', $user->id)->pluck('contact_no')->toArray();
                    $numbers = array_unique(array_map('trim', $contacts));
                    
                    $numbers = self::removeDuplicateNumbers($numbers);
                    $estimatedNumberCount = count($numbers);
                    
                    $totalCreditCost = $creditPerNumber * $estimatedNumberCount;
                    
                    //  Check if balance is sufficient
                    if($totalCreditCost > $balance_credit) 
                    {
                        return ['status' => false, 'message' => 'Insufficient Balance'];
                    }
                }
            }
        }

        if(is_null($selectedRouteName)) 
        {
            return [
                'status' => false,
                'message' => 'RCS route is not assigned.'
            ];
        }

        //  Image Upload
        $imagePath = null;

        if($request->hasFile('image_path') && $request->file('image_path') instanceof UploadedFile) 
        {
            $instance = new self();
            $file = $request->file('image_path');
            
            try 
            {
                $imagePath = $instance->uploadFile(
                    $file,
                    ['jpg', 'jpeg', 'png', 'webp'],
                    2 * 1024 * 1024,
                    'RcsImages'
                );
            } 
            catch (\Exception $e) {
                return [
                    'status' => false,
                    'message' => 'Image upload failed: ' . $e->getMessage()
                ];
            }
        }

        //  Template message validation
        $validationResult = self::validateStrictTemplate($template_text, $request->message);

        if($validationResult['status'] === false)
        {
            return $validationResult;
        }

        // Real variable values extracted while matching the message against the
        // template — passed straight to the gateway instead of being guessed back
        // out of the resolved text a second time.
        $variables = $validationResult['variables'] ?? [];

        $messageData = [
            'route' => $selectedRouteName,
            'template_type' => $template_type,
            'bot_type' => $request->bot_type,
            'template_name' => $request->template_name,
            'campaign_name' => $request->campaign_name,
            'message' => $template_text,
            'job_id' => generateRandomAlphaNumeric(15),
            'ip_address' => $request->ip(),
            'method' => 'API',
            'userid' => $user->id,
            'msg_credit' => $totalCreditCost,
            'created_by' => $user->id,
            'created_at' => now(),
            'updated_at' => null,
        ];

        if($request->has('card_title')) 
        {
            $messageData['card_title'] = $request->card_title;
        }

        if($request->has('url_link')) 
        {
            $messageData['url_link'] = $request->url_link;
        }

        if($request->has('button_text')) 
        {
            $messageData['button_text'] = $request->button_text;
        }

        if($imagePath) 
        {
            $messageData['image_path'] = $imagePath;
        }

        $message = self::create($messageData);

        //  Deduct credit from balance
        $newBalance = $balance_credit - $totalCreditCost;
        $creditManageDetail->update(['balance' => $newBalance]);
        
        //  Create transaction log
        CampaignTransactionLog::create([
            'job_id' => $message->job_id,
            'campaign_name' => $request->campaign_name,
            'total_numbers' => $estimatedNumberCount,
            'total_credit' => $totalCreditCost,
            'previous_balance' => $balance_credit,
            'new_balance' => $newBalance,
            'created_by' => $user->id,
            'created_at' => now(),
            'updated_at' => null,
        ]);

        $groupNames = explode(',', $request->group_name);
        $groupNames = array_map('trim', $groupNames);

        $group_details = MST_group::whereIn('group_name', $groupNames)->where('user_ID', $user->id)->where('isActive', 1)->get();

        $groupIds = $group_details->pluck('id')->toArray();

        // Fetch numbers from contact_details
        $contacts = ContactDetails::whereIn('group_ID', $groupIds)->where('user_ID', $user->id)->pluck('contact_no')->toArray();

        // Deduplicate & clean
        $numbers = array_unique(array_map('trim', $contacts));

        //  Remove duplicate mobile numbers
        $numbers = self::removeDuplicateNumbers($numbers);

        dispatch(new RcsSendNumbersJob($numbers, $message, $user->id, $request->message, $route_id, $creditPerNumber, $variables));

        return [
            'status' => true,
            'message' => 'RCS message created successfully.',
            'data' => $message
        ];
    }

    public static function quickValidateRequest($request, $user): array
    {
        // Move quick validation logic here from controller
        if(!$user) {
            return ['status' => false, 'message' => 'User not authenticated.'];
        }

        if(empty($user->route_IDs)) {
            return ['status' => false, 'message' => 'No routes assigned to user.'];
        }

        $templateExists = RcsTemplate::where('template_name', $request->template_name)
            ->where('bot_type', $request->bot_type)
            ->where('user_ID', $user->id)
            ->where('isActive', 1)
            ->exists();

        if(!$templateExists) {
            return ['status' => false, 'message' => 'RCS template not found.'];
        }

        return ['status' => true];
    }

    // Fills a template's placeholders in the order they appear using an ordered,
    // comma-separated list of values (e.g. "value one,value two") instead of a
    // pre-built message string. Positional, not named — the caller doesn't need
    // to know each placeholder's name, only their left-to-right order in the
    // approved template text.
    public static function buildMessageFromVariableList(string $template, string $variableList): array
    {
        $values = array_map('trim', explode(',', $variableList));

        preg_match_all('/[\{\[]([^\{\}\[\]]+)[\}\]]/', $template, $matches);
        $placeholderNames = $matches[1];

        if(count($placeholderNames) > count($values))
        {
            return [
                'status' => false,
                'message' => 'Template has ' . count($placeholderNames) . ' variable(s) but only ' . count($values) . ' value(s) were provided.'
            ];
        }

        // Named by the template's own placeholder names (not by list position) —
        // this is what actually gets sent to the gateway, so it has to match the
        // key => value shape RcsSendNumbersJob's payload builders expect.
        $variables = [];
        $remainingValues = $values;

        $finalMessage = preg_replace_callback('/[\{\[]([^\{\}\[\]]+)[\}\]]/', function($match) use (&$remainingValues, &$variables) {
            $name = trim($match[1]);
            $value = array_shift($remainingValues);
            $variables[$name] = $value;
            return $value;
        }, $template);

        return [
            'status' => true,
            'message' => $finalMessage,
            'variables' => $variables
        ];
    }

    public static function validateStrictTemplate(string $template, string $message)
    {
        $template = self::normalizeWhitespace($template);
        $message = self::normalizeWhitespace($message);

        $templateParts = [];
        $buffer = '';
        $insidePlaceholder = false;
        // Real variable values, captured as a side-effect of the same positional
        // matching this function already does — so callers (the HTTP API) can send
        // them straight to the gateway instead of extracting them again by guesswork.
        $variables = [];

        for($i = 0; $i < strlen($template); $i++)
        {
            $char = $template[$i];

            // Support both {param} and [param] as placeholder formats
            if($char === '{' || $char === '[')
            {
                if($buffer !== '')
                {
                    $templateParts[] = ['type' => 'text', 'value' => $buffer];
                    $buffer = '';
                }

                $insidePlaceholder = true;
                $buffer .= $char;
            }
            elseif($char === '}' || $char === ']')
            {
                $buffer .= $char;
                $templateParts[] = ['type' => 'placeholder', 'value' => $buffer];
                $buffer = '';
                $insidePlaceholder = false;
            }
            else
            {
                $buffer .= $char;
            }
        }

        if($buffer !== '') 
        {
            $templateParts[] = ['type' => $insidePlaceholder ? 'placeholder' : 'text', 'value' => $buffer];
        }

        $messageCursor = 0;
        $templateIndex = 0;
        $templateCount = count($templateParts);

        while($templateIndex < $templateCount && $messageCursor < strlen($message)) 
        {
            $part = $templateParts[$templateIndex];

            if($part['type'] === 'text') 
            {
                $expected = $part['value'];
                
                if(trim($expected) === '') 
                {
                    $templateIndex++;
                    continue;
                }
                
                $expectedLength = strlen($expected);
                $actual = substr($message, $messageCursor, $expectedLength);

                if($actual === $expected) 
                {
                    $messageCursor += $expectedLength;
                    $templateIndex++;
                }
                else 
                {
                    $flexibleMatch = self::flexibleTextMatch($expected, $message, $messageCursor);
                    
                    if($flexibleMatch['matched']) 
                    {
                        $messageCursor = $flexibleMatch['newPosition'];
                        $templateIndex++;
                    }
                    else 
                    {
                        $caseInsensitiveMatch = self::caseInsensitiveMatch($expected, $message, $messageCursor);
                        
                        if($caseInsensitiveMatch['matched']) 
                        {
                            $messageCursor = $caseInsensitiveMatch['newPosition'];
                            $templateIndex++;
                        }
                        else 
                        {
                            Log::error('Template mismatch at position:', [
                                'expected' => $expected,
                                'actual' => $actual,
                                'cursor' => $messageCursor,
                                'remaining_message' => substr($message, $messageCursor, 100)
                            ]);
                            
                            return ['status' => false, 'message' => 'Template Mismatched! Your message does not match the selected template format.'];
                        }
                    }
                }
            } 
            else 
            {
                $nextStaticText = '';
                $nextPartIndex = $templateIndex + 1;

                while($nextPartIndex < $templateCount) 
                {
                    if($templateParts[$nextPartIndex]['type'] === 'text' && trim($templateParts[$nextPartIndex]['value']) !== '') 
                    {
                        $nextStaticText = $templateParts[$nextPartIndex]['value'];
                        break;
                    }
                    $nextPartIndex++;
                }

                if($nextStaticText !== '') 
                {
                    $pos = strpos($message, $nextStaticText, $messageCursor);

                    if($pos === false) 
                    {
                        $pos = stripos($message, $nextStaticText, $messageCursor);
                        
                        if($pos === false) 
                        {
                            $flexibleMatch = self::flexibleTextSearch($nextStaticText, $message, $messageCursor);
                            
                            if($flexibleMatch['found']) 
                            {
                                $pos = $flexibleMatch['position'];
                            }
                            else 
                            {
                                if($nextPartIndex === $templateCount)
                                {
                                    $placeholderValue = substr($message, $messageCursor);

                                    if($placeholderValue !== '')
                                    {
                                        $variables[trim($part['value'], '{}[]')] = $placeholderValue;
                                        $messageCursor = strlen($message);
                                        $templateIndex = $templateCount;
                                        break;
                                    }
                                }
                                
                                return ['status' => false, 'message' => 'Template Mismatched! Your message does not match the selected template format.'];
                            }
                        }
                    }

                    $placeholderValue = substr($message, $messageCursor, $pos - $messageCursor);

                    if($placeholderValue === '')
                    {
                        return ['status' => false, 'message' => 'Template Mismatched! Your message does not match the selected template format.'];
                    }

                    $variables[trim($part['value'], '{}[]')] = $placeholderValue;
                    $messageCursor = $pos;
                    $templateIndex++;
                }
                else
                {
                    $placeholderValue = substr($message, $messageCursor);

                    if($placeholderValue === '')
                    {
                        return ['status' => false, 'message' => 'Template Mismatched! Your message does not match the selected template format.'];
                    }

                    $variables[trim($part['value'], '{}[]')] = $placeholderValue;
                    $messageCursor = strlen($message);
                    $templateIndex++;
                }
            }
        }

        while($templateIndex < $templateCount) 
        {
            $part = $templateParts[$templateIndex];
            
            if($part['type'] === 'text' && trim($part['value']) === '') 
            {
                $templateIndex++;
            } 
            else 
            {
                break;
            }
        }

        if($templateIndex !== $templateCount || $messageCursor !== strlen($message)) 
        {
            Log::error('Template validation failed:', [
                'templateIndex' => $templateIndex,
                'templateCount' => $templateCount,
                'messageCursor' => $messageCursor,
                'messageLength' => strlen($message),
                'remainingTemplate' => array_slice($templateParts, $templateIndex)
            ]);
            
            return ['status' => false, 'message' => 'Template Mismatched! Your message does not match the selected template format.'];
        }

        return ['status' => true, 'message' => 'Template matched successfully.', 'variables' => $variables];
    }

    private static function normalizeWhitespace(string $text): string
    {
        $text = preg_replace('/\s+/', ' ', $text);
        
        return trim($text);
    }

    private static function caseInsensitiveMatch(string $expected, string $message, int $position): array
    {
        $expectedLower = strtolower($expected);
        $actualLower = strtolower(substr($message, $position, strlen($expected)));
        
        if($expectedLower === $actualLower) 
        {
            return [
                'matched' => true,
                'newPosition' => $position + strlen($expected)
            ];
        }
        
        return ['matched' => false];
    }

    /* public static function validateStrictTemplate(string $template, string $message)
    {
        $templateParts = [];
        $buffer = '';
        $insidePlaceholder = false;

        for($i = 0; $i < strlen($template); $i++) 
        {
            $char = $template[$i];

            if($char === '{') 
            {
                if($buffer !== '') 
                {
                    $templateParts[] = ['type' => 'text', 'value' => $buffer];
                    $buffer = '';
                }

                $insidePlaceholder = true;
                $buffer .= $char;
            } 
            elseif($char === '}') 
            {
                $buffer .= $char;
                $templateParts[] = ['type' => 'placeholder', 'value' => $buffer];
                $buffer = '';
                $insidePlaceholder = false;
            } 
            else 
            {
                $buffer .= $char;
            }
        }

        if($buffer !== '') 
        {
            $templateParts[] = ['type' => $insidePlaceholder ? 'placeholder' : 'text', 'value' => $buffer];
        }

        $messageCursor = 0;
        $templateIndex = 0;
        $templateCount = count($templateParts);

        while($templateIndex < $templateCount && $messageCursor < strlen($message)) 
        {
            $part = $templateParts[$templateIndex];

            if($part['type'] === 'text') 
            {
                $expected = $part['value'];
                $expectedLength = strlen($expected);
                $actual = substr($message, $messageCursor, $expectedLength);

                if($actual === $expected) 
                {
                    $messageCursor += $expectedLength;
                    $templateIndex++;
                }
                else 
                {
                    $flexibleMatch = self::flexibleTextMatch($expected, $message, $messageCursor);
                    
                    if($flexibleMatch['matched']) 
                    {
                        $messageCursor = $flexibleMatch['newPosition'];
                        $templateIndex++;
                    }
                    else 
                    {
                        return ['status' => false, 'message' => 'Template Mismatched! Your message does not match the selected template format.'];
                    }
                }
            } 
            else 
            {
                $nextStaticText = '';
                $nextPartIndex = $templateIndex + 1;

                while($nextPartIndex < $templateCount) 
                {
                    if($templateParts[$nextPartIndex]['type'] === 'text') 
                    {
                        $nextStaticText = $templateParts[$nextPartIndex]['value'];
                        break;
                    }

                    $nextPartIndex++;
                }

                if($nextStaticText !== '') 
                {
                    $pos = strpos($message, $nextStaticText, $messageCursor);

                    if($pos === false) 
                    {
                        $flexibleMatch = self::flexibleTextSearch($nextStaticText, $message, $messageCursor);
                        
                        if($flexibleMatch['found']) 
                        {
                            $pos = $flexibleMatch['position'];
                        }
                        else 
                        {
                            return ['status' => false, 'message' => 'Template Mismatched! Your message does not match the selected template format.'];
                        }
                    }

                    $placeholderValue = substr($message, $messageCursor, $pos - $messageCursor);

                    if($placeholderValue === '') 
                    {
                        return ['status' => false, 'message' => 'Template Mismatched! Your message does not match the selected template format.'];
                    }

                    $messageCursor = $pos;
                    $templateIndex++;
                } 
                else 
                {
                    $placeholderValue = substr($message, $messageCursor);
                    
                    if($placeholderValue === '') 
                    {
                        return ['status' => false, 'message' => 'Template Mismatched! Your message does not match the selected template format.'];
                    }

                    $messageCursor = strlen($message);
                    $templateIndex++;
                }
            }
        }

        if($templateIndex !== $templateCount || $messageCursor !== strlen($message)) 
        {
            return ['status' => false, 'message' => 'Template Mismatched! Your message does not match the selected template format.'];
        }

        return ['status' => true, 'message' => 'Template matched successfully.'];
    } */

    private static function flexibleTextMatch(string $expected, string $message, int $startPosition): array
    {
        $expectedLength = strlen($expected);
        $messageLength = strlen($message);
        $expectedIndex = 0;
        $messageIndex = $startPosition;

        while($messageIndex < $messageLength && ctype_space($message[$messageIndex]))
        {
            $messageIndex++;
        }

        while($expectedIndex < $expectedLength && $messageIndex < $messageLength)
        {
            $expectedChar = $expected[$expectedIndex];
            $messageChar = $message[$messageIndex];

            if($expectedChar === $messageChar)
            {
                $expectedIndex++;
                $messageIndex++;
            }
            elseif(ctype_space($expectedChar) && !ctype_space($messageChar))
            {
                $expectedIndex++;
            }
            elseif(!ctype_space($expectedChar) && ctype_space($messageChar))
            {
                $messageIndex++;
            }
            else
            {
                return ['matched' => false, 'newPosition' => $messageIndex];
            }
        }

        while($messageIndex < $messageLength && ctype_space($message[$messageIndex]))
        {
            $messageIndex++;
        }

        // Skip any remaining whitespace in expected (handles trailing whitespace when message ends)
        while($expectedIndex < $expectedLength && ctype_space($expected[$expectedIndex]))
        {
            $expectedIndex++;
        }

        if($expectedIndex === $expectedLength)
        {
            return ['matched' => true, 'newPosition' => $messageIndex];
        }

        return ['matched' => false, 'newPosition' => $messageIndex];
    }

    private static function flexibleTextSearch(string $searchText, string $message, int $startPosition): array
    {
        $messageLength = strlen($message);

        // Search through the message, allowing for whitespace variations
        for($i = $startPosition; $i < $messageLength; $i++)
        {
            $match = self::flexibleTextMatch($searchText, $message, $i);

            if($match['matched'])
            {
                return ['found' => true, 'position' => $i, 'endPosition' => $match['newPosition']];
            }
        }

        return ['found' => false, 'position' => -1];
    }

    private static function removeDuplicateDynamicMessages($request)
    {
        $messages = $request->messages;
        // OPTIMIZATION: Use hash map (isset) instead of in_array for O(1) lookup instead of O(n)
        // This is CRITICAL for large datasets - in_array with 150k items = ~11 billion operations!
        $normalizedNumbersMap = [];
        $filteredMessages = [];

        foreach($messages as $message)
        {
            $number = trim($message['number']);
            $cleanNumber = preg_replace('/[^0-9]/', '', $number);

            $normalizedNumber = self::normalizePhoneNumber($cleanNumber);

            // Use isset() for O(1) lookup instead of in_array() which is O(n)
            if(!isset($normalizedNumbersMap[$normalizedNumber]) && !empty($normalizedNumber))
            {
                $normalizedNumbersMap[$normalizedNumber] = true;
                $filteredMessages[] = $message;
            }
        }

        $request->merge(['messages' => $filteredMessages]);

        return $filteredMessages;
    }

    private static function removeDuplicateNumbers($numbers)
    {
        // OPTIMIZATION: Use hash map (isset) instead of in_array for O(1) lookup
        $normalizedNumbersMap = [];
        $uniqueNumbers = [];

        foreach($numbers as $number)
        {
            $cleanNumber = preg_replace('/[^0-9]/', '', $number);

            $normalizedNumber = self::normalizePhoneNumber($cleanNumber);

            // Use isset() for O(1) lookup instead of in_array() which is O(n)
            if(!isset($normalizedNumbersMap[$normalizedNumber]))
            {
                $normalizedNumbersMap[$normalizedNumber] = true;
                $uniqueNumbers[] = $number;
            }
        }

        return $uniqueNumbers;
    }

    private static function normalizePhoneNumber($number)
    {
        $number = ltrim($number, '0');
        
        if(strpos($number, '91') === 0) 
        {
            $withoutCode = substr($number, 2);
            
            if(strlen($withoutCode) === 10) 
            {
                $number = $withoutCode;
            }
        }
        
        if(strlen($number) === 10 && preg_match('/^[6-9]\d{9}$/', $number)) 
        {
            return $number;
        }
        
        return $number;
    }

    private static function getJioToken($endpoint, $clientId, $clientSecret)
    {
        $client = new \GuzzleHttp\Client();

        $fullUrl = 'https://tgs.businessmessaging.jio.com/v1/oauth/token';

        $queryParams = [
            'grant_type' => 'client_credentials',
            'client_id' => $clientId,
            'client_secret' => $clientSecret,
            'scope' => 'read'
        ];

        $fullUrlWithParams = $fullUrl . '?' . http_build_query($queryParams);

        try 
        {
            $response = $client->get($fullUrlWithParams, [
                'headers' => [
                    'Accept' => 'application/json'
                ],
                'timeout' => 30,
                'connect_timeout' => 10,
                'http_errors' => false
            ]);

            $statusCode = $response->getStatusCode();
            $body = $response->getBody()->getContents();
            
            if($statusCode !== 200) 
            {
                throw new \Exception("Jio API returned status: {$statusCode}");
            }
            
            $responseData = json_decode($body, true);
            
            if(!isset($responseData['access_token']) || empty($responseData['access_token'])) 
            {
                throw new \Exception('Access token not found in response');
            }
            
            return $responseData;
        }
        catch(\GuzzleHttp\Exception\RequestException $e) 
        {
            throw $e;
        }
    }

    private static function getViToken($endpoint, $clientId, $clientSecret)
    {
        $client = new \GuzzleHttp\Client();
        
        $authHeader = 'Basic ' . base64_encode($clientId . ':' . $clientSecret);
        
        $fullUrl = 'https://auth.virbm.in/auth/oauth/token?grant_type=client_credentials';

        try 
        {
            $response = $client->post($fullUrl, [
                'headers' => [
                    'Authorization' => $authHeader,
                    'Content-Type' => 'application/x-www-form-urlencoded',
                    'Accept' => 'application/json'
                ],
                'timeout' => 30,
                'connect_timeout' => 10,
                'http_errors' => false
            ]);
            
            $statusCode = $response->getStatusCode();
            $body = $response->getBody()->getContents();
            
            if($statusCode !== 200) 
            {
                throw new \Exception("Vi API returned status: {$statusCode}. Response: {$body}");
            }
            
            $data = json_decode($body, true);
            
            if(json_last_error() !== JSON_ERROR_NONE) 
            {
                throw new \Exception('Failed to parse JSON response: ' . json_last_error_msg());
            }
            
            return $data;
        }
        catch(\GuzzleHttp\Exception\RequestException $e) 
        {
            throw $e;
        }
    }

    // Today rcs report summary
    public static function getTodayRcsReportSummary($user, array $filters)
    {
        $params = getListingParams($filters);

        $limit      = $params['limit'];
        $offset     = $params['offset'];
        $searchText = $params['searchText'];
        $sorting    = $params['sorting'];
        $orderBy    = $params['orderBy'];

        $user_ID   = $filters['user_ID'];

        $query  = RcsSendNumber::join('users', 'users.id', '=', 'rcs_send_numbers.userid')
                ->where('rcs_send_numbers.isActive', 1)
                ->whereDate('rcs_send_numbers.created_at', today())

                ->when($user_ID === 'All', function ($query) 
                {
                    
                }, function ($query) use ($user, $user_ID) {
                    if(!empty($user_ID)) 
                    {
                        $query->where('rcs_send_numbers.userid', $user_ID);
                    } 
                    else 
                    {
                        $query->where('rcs_send_numbers.userid', $user->id);
                    }
                })

                ->when($searchText, function ($query) use ($searchText) {
                    $query->where(function ($q) use ($searchText) {
                        $lowerSearch = strtolower($searchText);

                        if(in_array($lowerSearch, ['active', 'inactive'])) 
                        {
                            $status = $lowerSearch === 'active' ? 1 : 0;
                            $q->where('rcs_send_numbers.isActive', $status);
                        } 
                        else 
                        {
                            $q->whereRaw('LOWER(users.full_name) LIKE ?', ['%' . $lowerSearch . '%'])
                            ->orWhereRaw('LOWER(rcs_send_numbers.status) LIKE ?', ['%' . $lowerSearch . '%'])
                            ->orWhereRaw('rcs_send_numbers.created_at LIKE ?', ['%' . $searchText . '%'])
                            ->orWhereRaw("DATE_FORMAT(rcs_send_numbers.created_at, '%d-%m-%Y') LIKE ?", ['%' . $searchText . '%'])
                            ->orWhereRaw("DATE_FORMAT(rcs_send_numbers.created_at, '%d/%m/%Y') LIKE ?", ['%' . $searchText . '%']);
                        }
                    });
                });

        $totalCount = (clone $query)->distinct('rcs_send_numbers.userid')->count('rcs_send_numbers.userid');

     

      $records = (clone $query)
            ->select(
                'users.id as user_ID',
                'users.full_name as username',
                DB::raw('COUNT(*) as total'),
                DB::raw("COUNT(CASE WHEN rcs_send_numbers.status = 'submitted' THEN 1 ELSE NULL END) as submitted"),
                DB::raw("COUNT(CASE WHEN rcs_send_numbers.status = 'delivered' THEN 1 ELSE NULL END) as delivered"),
                DB::raw("COUNT(CASE WHEN rcs_send_numbers.status = 'failed' THEN 1 ELSE NULL END) as failed")
            )
            ->groupBy('users.id', 'users.full_name')
            ->skip($offset)
            ->take($limit)
            ->get();

        $formattedRecords = $records->map(function ($record) {
            return [
                'user_ID' => $record->user_ID,
                'username' => $record->username,
                'total' => $record->total,
                'submitted' => $record->submitted,
                'delivered' => $record->delivered,
                'failed' => $record->failed,
            ];
        });

        return [['records' => $formattedRecords->toArray()], $totalCount];
    }


    public static function getTodayRcsSmsReport($user, array $filters)
    {
        $params = getListingParams($filters);

        $limit      = $params['limit'];
        $offset     = $params['offset'];
        $searchText = $params['searchText'];
        $sorting    = $params['sorting'];
        $orderBy    = $params['orderBy'];

        $user_ID   = $filters['user_ID'];
        $status   = $filters['status'];

        $query  =  RcsSendNumber::where('rcs_send_numbers.userid', $user_ID)
                ->join('users', 'users.id', '=', 'rcs_send_numbers.userid')
                ->whereNull('rcs_send_numbers.deleted_at')
                ->where('rcs_send_numbers.isActive', 1)
                ->whereDate('rcs_send_numbers.created_at', today())

                ->when($status, function ($query) use ($status) {
                    $query->where('rcs_send_numbers.status', $status);
                })

                ->when($searchText, function ($query) use ($searchText) {
                    $query->where(function ($q) use ($searchText) {
                        $lowerSearch = strtolower($searchText);

                        if(in_array($lowerSearch, ['active', 'inactive'])) 
                        {
                            $status = $lowerSearch === 'active' ? 1 : 0;
                            $q->where('rcs_send_numbers.isActive', $status);
                        } 
                        else 
                        {
                            $q->whereRaw('LOWER(users.full_name) LIKE ?', ['%' . $lowerSearch . '%'])
                            ->orWhereRaw('LOWER(rcs_send_numbers.status) LIKE ?', ['%' . $lowerSearch . '%'])
                            ->orWhereRaw('LOWER(rcs_send_numbers.route) LIKE ?', ['%' . $lowerSearch . '%'])
                            ->orWhereRaw('LOWER(rcs_send_numbers.message) LIKE ?', ['%' . $lowerSearch . '%'])
                            ->orWhereRaw('rcs_send_numbers.mobile_number LIKE ?', ['%' . $searchText . '%'])
                            ->orWhereRaw('rcs_send_numbers.msgcredit LIKE ?', ['%' . $searchText . '%'])
                            ->orWhereRaw('rcs_send_numbers.master_job_id LIKE ?', ['%' . $searchText . '%'])
                            ->orWhereRaw('rcs_send_numbers.msg_job_id LIKE ?', ['%' . $searchText . '%'])
                            ->orWhereRaw('rcs_send_numbers.userid LIKE ?', ['%' . $searchText . '%'])
                            ->orWhereRaw('rcs_send_numbers.created_at LIKE ?', ['%' . $searchText . '%'])
                            ->orWhereRaw("DATE_FORMAT(rcs_send_numbers.created_at, '%d-%m-%Y') LIKE ?", ['%' . $searchText . '%'])
                            ->orWhereRaw("DATE_FORMAT(rcs_send_numbers.created_at, '%d/%m/%Y') LIKE ?", ['%' . $searchText . '%']);
                            }

                    });
                });

        $totalCount = (clone $query)->count();

        if($sorting === 'isActive') 
        {
            $orderBy = strtolower($orderBy) === 'asc' ? 'desc' : 'asc';
        }

        $query->orderBy($sorting, $orderBy);

        $today_rcs_sms_report = $query
                                ->skip($offset)
                                ->take($limit)
                                ->select(
                                'rcs_send_numbers.id',
                                'rcs_send_numbers.message_id',
                                'rcs_send_numbers.mobile_number',
                                'users.full_name as username',
                                'rcs_send_numbers.msgcredit as bill',
                                'rcs_send_numbers.master_job_id as job_id',
                                'rcs_send_numbers.msg_job_id',
                                'rcs_send_numbers.route',
                                'rcs_send_numbers.status',
                                'rcs_send_numbers.message',
                                'rcs_send_numbers.userid',
                                'rcs_send_numbers.created_at'
                                )
                               ->get();

        return [$today_rcs_sms_report, $totalCount];
    }
    
    //RCS Download Status
    public static function getRcsDownloadStatus($user, array $filters)
    {
        $params = getListingParams($filters);

        $limit          = $params['limit'];
        $offset         = $params['offset'];
        $searchText     = $params['searchText'];
        $sorting        = $params['sorting'];
        $orderBy        = $params['orderBy'];
        $fromDate       = $filters['from_date'];
	    $toDate         = $filters['to_date'];
        $mobile_number  = $filters['mobile_number'];

        $user_ID   = $filters['user_ID'];

        $query  = RcsSendNumber::join('users', 'users.id', '=', 'rcs_send_numbers.userid')
                ->whereNull('rcs_send_numbers.deleted_at')
                ->where('rcs_send_numbers.isActive', 1)

                ->when($user_ID === 'All', function ($query) 
                {
                    
                }, function ($query) use ($user, $user_ID) {
                    if(!empty($user_ID)) 
                    {
                        $query->where('rcs_send_numbers.userid', $user_ID);
                    } 
                    else 
                    {
                        $query->where('rcs_send_numbers.userid', $user->id);
                    }
                })

                ->when($searchText, function ($query) use ($searchText) {
                    $query->where(function ($q) use ($searchText) {
                        $lowerSearch = strtolower($searchText);

                        if(in_array($lowerSearch, ['active', 'inactive'])) 
                        {
                            $status = $lowerSearch === 'active' ? 1 : 0;
                            $q->where('rcs_send_numbers.isActive', $status);
                        } 
                        else 
                        {
                            $q->whereRaw('LOWER(users.full_name) LIKE ?', ['%' . $lowerSearch . '%'])
                            ->orWhereRaw('rcs_send_numbers.mobile_number LIKE ?', ['%' . $searchText . '%'])
                            ->orWhereRaw('rcs_send_numbers.msgcredit LIKE ?', ['%' . $searchText . '%'])
                            ->orWhereRaw('rcs_send_numbers.created_at LIKE ?', ['%' . $searchText . '%'])
                            ->orWhereRaw("DATE_FORMAT(rcs_send_numbers.created_at, '%d-%m-%Y') LIKE ?", ['%' . $searchText . '%'])
                            ->orWhereRaw("DATE_FORMAT(rcs_send_numbers.created_at, '%d/%m/%Y') LIKE ?", ['%' . $searchText . '%']);
                        }
                    });
                })
                ->when($fromDate, function ($query) use ($fromDate) {
                    $query->whereDate('rcs_send_numbers.created_at', '>=', $fromDate);
                })
                ->when($toDate, function ($query) use ($toDate) {
                    $query->whereDate('rcs_send_numbers.created_at', '<=', $toDate);
                })
                ->when($mobile_number, function ($query) use ($mobile_number) {
                    $query->where('rcs_send_numbers.mobile_number', '=', $mobile_number);
                });

        $totalCount = (clone $query)->count();

        if($sorting === 'isActive') 
        {
            $orderBy = strtolower($orderBy) === 'asc' ? 'desc' : 'asc';
        }

        $query->orderBy($sorting, $orderBy);

        $rcs_download_center  = $query
                                ->skip($offset)
                                ->take($limit)
                                ->select(
                                    'rcs_send_numbers.id',
                                    'users.full_name as username',
                                    'rcs_send_numbers.mobile_number',
                                    'rcs_send_numbers.msgcredit as bill_credit',
                                    'rcs_send_numbers.created_at',
                                )
                                ->get();

        return [$rcs_download_center, $totalCount];
    }
    
    //RCS Jobs Report
    public static function getRcsJobsReport($user, array $filters)
    {
        $params = getListingParams($filters);

        $limit      = $params['limit'];
        $offset     = $params['offset'];
        $searchText = $params['searchText'];
        $sorting    = $params['sorting'];
        $orderBy    = $params['orderBy'];
        $fromDate   = $filters['from_date'];
	    $toDate     = $filters['to_date'];
        $user_ID    = $filters['user_ID'];

        // Default to the current date when no date range is provided
        if(empty($fromDate) && empty($toDate))
        {
            $fromDate = today()->toDateString();
            $toDate   = today()->toDateString();
        }

        $query  = self::join('users', 'users.id', '=', 'rcs_send_messages.userid')
                ->join('rcs_send_numbers', 'rcs_send_numbers.msg_job_id', '=', 'rcs_send_messages.job_id')
                ->whereNull('rcs_send_messages.deleted_at')
                ->where('rcs_send_messages.isActive', 1)

                ->when($user_ID === 'All', function ($query) 
                {
                    
                }, function ($query) use ($user, $user_ID) {
                    if(!empty($user_ID)) 
                    {
                        $query->where('rcs_send_messages.userid', $user_ID);
                    } 
                    else 
                    {
                        $query->where('rcs_send_messages.userid', $user->id);
                    }
                })

                ->when($searchText, function ($query) use ($searchText) {
                    $query->where(function ($q) use ($searchText) {
                        $lowerSearch = strtolower($searchText);

                        if(in_array($lowerSearch, ['active', 'inactive'])) 
                        {
                            $status = $lowerSearch === 'active' ? 1 : 0;
                            $q->where('rcs_send_messages.isActive', $status);
                        } 
                        else 
                        {
                            $q->whereRaw('LOWER(users.full_name) LIKE ?', ['%' . $lowerSearch . '%'])
                            ->orWhereRaw('LOWER(rcs_send_messages.template_name) LIKE ?', ['%' . $lowerSearch . '%'])
                            
                            ->orWhereRaw('rcs_send_messages.msg_credit LIKE ?', ['%' . $searchText . '%'])
                            ->orWhereRaw('rcs_send_messages.job_id LIKE ?', ['%' . $searchText . '%'])
                            ->orWhereRaw('rcs_send_messages.numbers_count LIKE ?', ['%' . $searchText . '%'])
                            ->orWhereRaw('rcs_send_messages.created_at LIKE ?', ['%' . $searchText . '%'])
                            ->orWhereRaw("DATE_FORMAT(rcs_send_messages.created_at, '%d-%m-%Y') LIKE ?", ['%' . $searchText . '%'])
                            ->orWhereRaw("DATE_FORMAT(rcs_send_messages.created_at, '%d/%m/%Y') LIKE ?", ['%' . $searchText . '%']);
                        }
                    });
                })
                ->when($fromDate, function ($query) use ($fromDate) {
                    $query->whereDate('rcs_send_messages.created_at', '>=', $fromDate);
                })
                ->when($toDate, function ($query) use ($toDate) {
                    $query->whereDate('rcs_send_messages.created_at', '<=', $toDate);
                });

        $totalCount = (clone $query)->distinct()->count('rcs_send_messages.id');

        if($sorting === 'isActive') 
        {
            $orderBy = strtolower($orderBy) === 'asc' ? 'desc' : 'asc';
        }

        $query->orderBy($sorting, $orderBy);

        $rcs_jobs_report   = $query
                            ->skip($offset)
                            ->take($limit)
                            ->select(
                                'rcs_send_messages.id',
                                'rcs_send_messages.job_id',
                                'users.full_name as username',
                                'rcs_send_messages.template_name',
                                'rcs_send_messages.msg_credit as bill_credit',
                                'rcs_send_messages.numbers_count',
                                'rcs_send_messages.created_at',
                               
                            )
                            ->groupBy(
                                'rcs_send_messages.id',
                                'rcs_send_messages.job_id',
                                'users.full_name',
                                'rcs_send_messages.template_name',
                                'rcs_send_messages.msg_credit',
                                'rcs_send_messages.numbers_count',
                                'rcs_send_messages.created_at'
                            )
                            ->get();

        return [$rcs_jobs_report, $totalCount];
    }

    public static function getRcsJobsBasicDetails($request, $user)
    {
        $job_id = $request->job_id;

        $rcs_jobs_report  = self::where('rcs_send_messages.job_id', $job_id)
                            ->select(
                                'rcs_send_messages.job_id',
                                'rcs_send_messages.campaign_name',
                                
                                'rcs_send_messages.ip_address',
                                'rcs_send_messages.created_at',
                                'rcs_send_messages.msg_credit as total_bill',
                                'rcs_send_messages.method',
                                
                               
                            )
                            ->first();

        if($rcs_jobs_report) 
        {
            $statuses   = DB::table('rcs_send_numbers')
                        ->join('rcs_send_messages', 'rcs_send_messages.id', '=', 'rcs_send_numbers.message_id')
                        ->where('rcs_send_messages.job_id', $job_id)
                        ->groupBy('rcs_send_numbers.status')
                        ->select('rcs_send_numbers.status', DB::raw('sum(rcs_send_numbers.msgcredit) as sum'))
                        ->get()
                        ->keyBy('status')
                        ->map(fn($item) => $item->sum);

            $rcs_jobs_report->statuses = $statuses;
        }

        return $rcs_jobs_report;
    }

    public static function getRcsJobsNumbersReport($user, array $filters)
{
    $params = getListingParams($filters);

    $limit      = $params['limit'];
    $offset     = $params['offset'];
    $searchText = $params['searchText'];
    $sorting    = $params['sorting'];
    $orderBy    = $params['orderBy'];

    $job_id   = $filters['job_id'];

    $query  =  RcsSendNumber::where('rcs_send_numbers.msg_job_id', $job_id)
            ->join('users', 'users.id', '=', 'rcs_send_numbers.userid')
            ->join('rcs_send_messages', 'rcs_send_messages.job_id', '=', 'rcs_send_numbers.msg_job_id')
            ->whereNull('rcs_send_numbers.deleted_at')
            ->where('rcs_send_numbers.isActive', 1)

            ->when($searchText, function ($query) use ($searchText) {
                $query->where(function ($q) use ($searchText) {
                    $lowerSearch = strtolower($searchText);

                    if(in_array($lowerSearch, ['active', 'inactive'])) 
                    {
                        $status = $lowerSearch === 'active' ? 1 : 0;
                        $q->where('rcs_send_numbers.isActive', $status);
                    } 
                    else 
                    {
                        $q->whereRaw('LOWER(rcs_send_numbers.route) LIKE ?', ['%' . $lowerSearch . '%'])
                        ->orWhereRaw('LOWER(rcs_send_messages.template_name) LIKE ?', ['%' . $lowerSearch . '%'])
                        ->orWhereRaw('LOWER(rcs_send_numbers.status) LIKE ?', ['%' . $lowerSearch . '%'])
                        ->orWhereRaw('rcs_send_numbers.mobile_number LIKE ?', ['%' . $searchText . '%'])
                        ->orWhereRaw('rcs_send_numbers.master_job_id LIKE ?', ['%' . $searchText . '%'])
                        ->orWhereRaw('rcs_send_numbers.created_at LIKE ?', ['%' . $searchText . '%'])
                        ->orWhereRaw("DATE_FORMAT(rcs_send_numbers.created_at, '%d-%m-%Y') LIKE ?", ['%' . $searchText . '%'])
                        ->orWhereRaw("DATE_FORMAT(rcs_send_numbers.created_at, '%d/%m/%Y') LIKE ?", ['%' . $searchText . '%']);
                    }
                });
            });

    $totalCount = (clone $query)->count();

    if($sorting === 'isActive') 
    {
        $orderBy = strtolower($orderBy) === 'asc' ? 'desc' : 'asc';
    }

    $query->orderBy($sorting, $orderBy);

    $rcs_jobs_numbers_report  = $query
                                ->skip($offset)
                                ->take($limit)
                                ->select(
                                    'rcs_send_numbers.id', 
                                    'rcs_send_numbers.route',
                                    'rcs_send_numbers.mobile_number', 
                                    'rcs_send_messages.template_name',
                                    'rcs_send_numbers.master_job_id as message_id',
                                    'rcs_send_numbers.status',
                                    'rcs_send_numbers.created_at'
                                )
                                ->get();

    return [$rcs_jobs_numbers_report, $totalCount];
}

}
