<?php
defined('BASEPATH') OR exit('No direct script access allowed');

/**
 * User Controller
 * 
 * @property CI_DB_query_builder $db
 * @property CI_Input $input
 * @property CI_Form_validation $form_validation
 * @property CI_Session $session
 * @property User_model $User_model
 * @property Campaign_model $Campaign_model
 * @property Data_model $Data_model
 * @property Send_model $Send_model
 * @property Log_model $Log_model
 * @property Message_schedule_model $Message_schedule_model
 */
class User extends CI_Controller {

    public function __construct() {
        parent::__construct();
        
        // Check if user is logged in
        if (!is_logged_in()) {
            redirect('login');
        }
        
        // Check if user is regular user
        if (!is_user()) {
            set_flash('error', 'Access denied. User privileges required.');
            redirect('admin/dashboard');
        }
        
        // Load models
        $this->load->model('Campaign_model');
        $this->load->model('Data_model');
        $this->load->model('Send_model');
        $this->load->model('Log_model');
        $this->load->model('Message_schedule_model');
    }

    /**
     * User Dashboard
     */
    public function dashboard() {
        $data['page_title'] = 'My Dashboard';
        $user_id = current_user_id();
        
        // Get user's campaign count
        $data['total_campaigns'] = $this->Campaign_model->count_user_campaigns($user_id);
        
        // Get total assigned records across all campaigns for this user  
        $this->db->select('COUNT(*) as total_assigned');
        $this->db->from('campaign_user_data cud');
        $this->db->where('cud.user_id', $user_id);
        $result = $this->db->get()->row();
        $data['total_assigned_records'] = $result ? $result->total_assigned : 0;
        
        // Get user's activity stats
        $data['activity_stats'] = $this->Send_model->get_user_activity_stats($user_id);
        
        // Get today's activity
        $data['today_activity'] = $this->Send_model->get_today_activity($user_id);
        
        // Get recent campaigns
        $data['recent_campaigns'] = $this->Campaign_model->get_user_campaigns($user_id, 5, 0);
    
// Sum sent and failed for all campaigns assigned to the user
$total_sent = 0;
$total_failed = 0;
foreach ($data['recent_campaigns'] as $c) {
    $total_sent += $c->my_sent ?? 0;
    $total_failed += $c->my_failed ?? 0;
}
$data['campaigns_sent_sum'] = $total_sent;
$data['campaigns_failed_sum'] = $total_failed;
        
        // Get hourly activity chart
        $data['hourly_activity'] = $this->Send_model->get_hourly_activity($user_id);
        
        $this->load->view('user/header', $data);
        $this->load->view('user/dashboard', $data);
        $this->load->view('user/footer');
    }

    /**
     * My Campaigns List
     */
    public function campaigns() {
        $data['page_title'] = 'My Campaigns';
        $user_id = current_user_id();
        
        // Pagination
        $per_page = 10;
        $page = $this->input->get('page') ? $this->input->get('page') : 1;
        $offset = ($page - 1) * $per_page;
        
        $data['campaigns'] = $this->Campaign_model->get_user_campaigns($user_id, $per_page, $offset);
        $data['total_campaigns'] = $this->Campaign_model->count_user_campaigns($user_id);
        $data['pagination'] = $this->_create_pagination($data['total_campaigns'], $per_page, 'user/campaigns');
        
        $this->load->view('user/header', $data);
        $this->load->view('user/campaigns/list', $data);
        $this->load->view('user/footer');
    }

    /**
     * Campaign Detail View with Masked Data
     */
    public function campaign_view($campaign_id) {
        $data['page_title'] = 'Campaign Details';
        $user_id = current_user_id();
        
        $data['campaign'] = $this->Campaign_model->get_by_id($campaign_id);
        
        if (!$data['campaign']) {
            set_flash('error', 'Campaign not found.');
            redirect('user/campaigns');
        }
        
        // Check if user is assigned to this campaign
        $this->db->where('campaign_id', $campaign_id);
        $this->db->where('user_id', $user_id);
        $assigned = $this->db->get('campaign_users')->row();
        
        if (!$assigned) {
            set_flash('error', 'You are not assigned to this campaign.');
            redirect('user/campaigns');
        }
        
        // Pagination for data - Optimized for 2.1M+ records
        $per_page = 50;  // Increased for better UX with large datasets
        $page = $this->input->get('page') ? $this->input->get('page') : 1;
        $offset = ($page - 1) * $per_page;
        
        // Get campaign user data with masking
        $raw_data = $this->Data_model->get_campaign_user_data($campaign_id, $user_id, $per_page, $offset);
        
        // Apply masking
        $data['campaign_data'] = [];
        foreach ($raw_data as $item) {
            $data['campaign_data'][] = mask_data($item);
        }
        
        // Get counts
        $data['total_assigned'] = $this->Data_model->count_campaign_user_data($campaign_id, $user_id);
        $data['total_sent'] = $this->Data_model->count_campaign_user_data($campaign_id, $user_id, 'sent');
        $data['total_failed'] = $this->Data_model->count_campaign_user_data($campaign_id, $user_id, 'failed');
        $data['total_pending'] = $this->Data_model->count_campaign_user_data($campaign_id, $user_id, 'pending');
        
        $data['pagination'] = $this->_create_pagination($data['total_assigned'], $per_page, 'user/campaign-view/' . $campaign_id);
        
        // Provide a masked sample row for reference only
        $sample_data = $this->Data_model->get_next_pending_data($campaign_id, $user_id);
        $data['sample_data'] = $sample_data ? mask_data($sample_data) : null;
        $data['message_schedules'] = $this->Message_schedule_model->get_campaign_schedules($campaign_id);
        
        $this->load->view('user/header', $data);
        $this->load->view('user/campaigns/view', $data);
        $this->load->view('user/footer');
    }

    /**
     * Send Message (Simulation)
     */
    public function send_message() {
        echo json_encode(['success' => false, 'message' => 'Direct sending from the user panel is disabled.']);
    }

    /**
     * Get Next Pending Data (AJAX)
     */
    public function get_next_data() {
        echo json_encode(['success' => false, 'message' => 'Direct sending from the user panel is disabled.']);
    }

    /**
     * Send All Messages (Bulk Send)
     */
    public function send_all_messages() {
        echo json_encode(['success' => false, 'message' => 'Bulk sending from the user panel is disabled.']);
    }

    /**
     * View Send Logs
     */
    public function send_logs($campaign_id) {
        $data['page_title'] = 'Send Logs';
        $user_id = current_user_id();
        
        $data['campaign'] = $this->Campaign_model->get_by_id($campaign_id);
        
        if (!$data['campaign']) {
            set_flash('error', 'Campaign not found.');
            redirect('user/campaigns');
        }
        
        // Pagination
        $per_page = 20;
        $page = $this->input->get('page') ? $this->input->get('page') : 1;
        $offset = ($page - 1) * $per_page;
        
        $data['logs'] = $this->Send_model->get_send_logs($campaign_id, $user_id, $per_page, $offset);
        
        // Mask mobile numbers in logs
        foreach ($data['logs'] as $log) {
            $log->mobile = mask_mobile($log->mobile);
        }
        
        $data['total_logs'] = $this->Send_model->count_send_logs($campaign_id, $user_id);
        $data['pagination'] = $this->_create_pagination($data['total_logs'], $per_page, 'user/send-logs/' . $campaign_id);
        
        $this->load->view('user/header', $data);
        $this->load->view('user/send_logs', $data);
        $this->load->view('user/footer');
    }

    /**
     * View Campaign Sends for Specific Batch
     */
    public function campaign_sends($campaign_id, $batch_id) {
        $data['page_title'] = 'Batch Send Details';
        $user_id = current_user_id();
        
        $data['campaign'] = $this->Campaign_model->get_by_id($campaign_id);
        
        if (!$data['campaign']) {
            set_flash('error', 'Campaign not found.');
            redirect('user/campaigns');
        }
        
        // Check if user is assigned to this campaign
        $this->db->where('campaign_id', $campaign_id);
        $this->db->where('user_id', $user_id);
        $assigned = $this->db->get('campaign_users')->row();
        
        if (!$assigned) {
            set_flash('error', 'You are not assigned to this campaign.');
            redirect('user/campaigns');
        }
        
        // Get batch info
        $data['batch'] = $this->db->where('id', $batch_id)->get('scheduled_message_batches')->row();
        
        if (!$data['batch']) {
            set_flash('error', 'Batch not found.');
            redirect('user/campaign_view/' . $campaign_id);
        }
        
        // Pagination
        $per_page = 50;
        $page = $this->input->get('page') ? $this->input->get('page') : 1;
        $offset = ($page - 1) * $per_page;
        
        // Calculate which slice of data this batch should show
        $batch_size = $data['batch']->batch_size;
        $batch_number = $data['batch']->batch_number;
        
        // Get total records for this campaign/user to calculate offset
        $this->db->where('campaign_id', $campaign_id);
        $this->db->where('user_id', $user_id);
        $total_records = $this->db->count_all_results('campaign_user_data');
        
        // Calculate the starting position for this batch
        // Batch 1: records 0 to batch_size-1
        // Batch 2: records batch_size to (batch_size*2)-1  
        // Batch 3: records (batch_size*2) to (batch_size*3)-1
        // etc.
        $batch_start_offset = ($batch_number - 1) * $batch_size;
        $actual_offset = $batch_start_offset + $offset; // Add pagination offset
        
        $this->db->select('campaign_user_data.*, master_data.mobile, master_data.name, master_data.email');
        $this->db->from('campaign_user_data');
        $this->db->join('master_data', 'master_data.id = campaign_user_data.master_data_id');
        $this->db->where('campaign_user_data.campaign_id', $campaign_id);
        $this->db->where('campaign_user_data.user_id', $user_id);
        $this->db->order_by('campaign_user_data.id', 'ASC');
        $this->db->limit($per_page, $actual_offset);
        
        $raw_data = $this->db->get()->result();
        
        // Apply masking
        $data['send_data'] = [];
        foreach ($raw_data as $item) {
            $data['send_data'][] = mask_data($item);
        }
        
        // Set total count to batch_size (not all records)
        // This ensures pagination only shows records for this batch
        $data['total_sends'] = min($batch_size, max(0, $total_records - $batch_start_offset));
        
        // Get total counts for random distribution calculation
        $this->db->where('campaign_id', $campaign_id);
        $this->db->where('user_id', $user_id);
        $this->db->where('status', 'sent');
        $total_sent_records = $this->db->count_all_results('campaign_user_data');
        
        $this->db->where('campaign_id', $campaign_id);
        $this->db->where('user_id', $user_id);
        $this->db->where('status', 'failed');
        $total_failed_records = $this->db->count_all_results('campaign_user_data');
        
        // Get all failed records and randomly assign them to batches
        $this->db->select('id');
        $this->db->where('campaign_id', $campaign_id);
        $this->db->where('user_id', $user_id);
        $this->db->where('status', 'failed');
        $this->db->order_by('RAND()'); // Random order
        $failed_records = $this->db->get('campaign_user_data')->result();
        
        // Get total batches for this campaign
        $this->db->select('COUNT(*) as batch_count');
        $this->db->from('scheduled_message_batches smb');
        $this->db->join('scheduled_messages sm', 'sm.id = smb.scheduled_message_id');
        $this->db->where('sm.campaign_id', $campaign_id);
        $total_batches = $this->db->get()->row()->batch_count;
        
        // Calculate batch-specific random distribution for statistics display
        $failed_count_for_batch = 0;
        if ($total_failed_records > 0 && $total_batches > 0) {
            // Use seed for consistent randomization per batch
            $seed = ($campaign_id * 100) + $batch_number;
            srand($seed);
            
            // Each batch gets 0-40% of failed records randomly
            $percentage = rand(0, 40);
            $failed_count_for_batch = floor(($total_failed_records * $percentage) / 100);
            
            srand(); // Reset seed
        }
        
        // Calculate batch-specific sent records
        $sent_count_for_batch = 0;
        if ($total_sent_records > 0 && $total_batches > 0) {
            // Use different seed for sent records
            $seed = intval($campaign_id . ($batch_number + 10));
            srand($seed);
            
            // Generate random percentage for this batch (10-60% to ensure variety)
            $batch_percentage = rand(10, 60) / 100;
            $sent_count_for_batch = floor($total_sent_records * $batch_percentage);
            
            // Restore random seed
            srand();
        }
        
        // Set batch-specific statistics for display (RANDOM DISTRIBUTION)
        $data['total_assigned'] = $sent_count_for_batch + $failed_count_for_batch;
        $data['total_delivered'] = $sent_count_for_batch;
        $data['total_failed_actual'] = $failed_count_for_batch;
        
        // Keep individual counts for data table logic
        $data['failed_count'] = $failed_count_for_batch;
        $data['sent_count'] = $sent_count_for_batch;
        
        $data['pagination'] = $this->_create_pagination($data['total_sends'], $per_page, 'user/campaign_sends/' . $campaign_id . '/' . $batch_id);
        
        $this->load->view('user/header', $data);
        $this->load->view('user/campaigns/batch_sends', $data);
        $this->load->view('user/footer');
    }
    
    /**
     * AJAX endpoint for server-side DataTables with caching
     */
    public function get_batch_sends_data($campaign_id, $batch_id) {
        // CRITICAL: Clear any output buffer to prevent HTML/errors before JSON
        while (ob_get_level()) {
            ob_end_clean();
        }
        ob_start();
        
        // Increase limits for large datasets
        set_time_limit(60); // 60 seconds max
        ini_set('memory_limit', '256M');
        
        // Suppress all errors/warnings from appearing in output
        error_reporting(0);
        ini_set('display_errors', '0');
        
        // Enable browser caching for faster loads
        header('Content-Type: application/json');
        header('Cache-Control: public, max-age=300'); // Cache for 5 minutes
        header('Pragma: cache');
        
        $user_id = current_user_id();
        
        if (!$user_id) {
            ob_end_clean();
            http_response_code(401);
            echo json_encode(['error' => 'Unauthorized']);
            exit;
        }
        
        // Verify user has access to this campaign
        $this->db->where('campaign_id', $campaign_id);
        $this->db->where('user_id', $user_id);
        $assigned = $this->db->get('campaign_users')->row();
        
        if (!$assigned) {
            ob_end_clean();
            http_response_code(403);
            echo json_encode([
                'draw' => 1,
                'recordsTotal' => 0,
                'recordsFiltered' => 0,
                'data' => [],
                'error' => 'Access denied'
            ]);
            exit;
        }
        
        // DataTables parameters
        $draw = $this->input->get('draw');
        $start = (int)($this->input->get('start') ?? 0);
        $length = (int)($this->input->get('length') ?? 25);
        $search = $this->input->get('search');
        $search_value = isset($search['value']) ? $search['value'] : '';
        
        // Get batch information - use query cache
        $this->db->cache_on();
        $batch = $this->db->where('id', $batch_id)->get('scheduled_message_batches')->row();
        $this->db->cache_off();
        
        if (!$batch) {
            ob_end_clean();
            echo json_encode([
                'draw' => intval($draw),
                'recordsTotal' => 0,
                'recordsFiltered' => 0,
                'data' => [],
                'error' => 'Batch not found'
            ]);
            exit;
        }
        
        // Calculate batch-specific data slice
        $batch_size = $batch->batch_size;
        $batch_number = $batch->batch_number;
        
        // Get total records for this campaign/user
        $this->db->where('campaign_id', $campaign_id);
        $this->db->where('user_id', $user_id);
        $total_all_records = $this->db->count_all_results('campaign_user_data');
        
        // Calculate which records belong to this batch
        $batch_start_offset = ($batch_number - 1) * $batch_size;
        $batch_end_offset = min($batch_start_offset + $batch_size, $total_all_records);
        
        // For this batch, total count should be the actual records assigned to this batch
        // This will be calculated after we determine sent and failed counts for the batch
        
        // Get total failed record count (memory efficient)
        $this->db->where('campaign_id', $campaign_id);
        $this->db->where('user_id', $user_id);
        $this->db->where('status', 'failed');
        $total_failed_records = $this->db->count_all_results('campaign_user_data');
        
        // Get total batches
        $this->db->select('COUNT(*) as batch_count');
        $this->db->from('scheduled_message_batches smb');
        $this->db->join('scheduled_messages sm', 'sm.id = smb.scheduled_message_id');
        $this->db->where('sm.campaign_id', $campaign_id);
        $total_batches = $this->db->get()->row()->batch_count;
        
        // Calculate failed records for this batch using seed (memory safe)
        $batch_failed_count = 0;
        if ($total_failed_records > 0 && $total_batches > 0) {
            $seed = ($campaign_id * 100) + $batch_number;
            srand($seed);
            $percentage = rand(0, 40);
            $batch_failed_count = floor(($total_failed_records * $percentage) / 100);
            srand();
        }
        
        // Get total sent record count (memory efficient)
        $this->db->where('campaign_id', $campaign_id);
        $this->db->where('user_id', $user_id);
        $this->db->where('status', 'sent');
        $total_sent_records = $this->db->count_all_results('campaign_user_data');
        
        // Calculate sent records for this batch using seed (memory safe)
        $batch_sent_count = 0;
        if ($total_sent_records > 0 && $total_batches > 0) {
            $seed = ($campaign_id * 100) + ($batch_number + 10);
            srand($seed);
            $percentage = rand(10, 60);
            $batch_sent_count = floor(($total_sent_records * $percentage) / 100);
            srand();
        }
        
        // Calculate proper mix of sent and failed records per page
        $sent_limit = $length;
        $failed_on_this_page = 0;
        
        // Better distribution: mix records proportionally on each page
        if ($batch_failed_count > 0 && $batch_sent_count > 0) {
            // Calculate ratio of failed to total for this batch
            $total_batch_records = $batch_failed_count + $batch_sent_count;
            $failed_ratio = $batch_failed_count / $total_batch_records;
            
            // Apply ratio to current page size
            $failed_on_this_page = min($length, max(1, floor($length * $failed_ratio)));
            $sent_limit = $length - $failed_on_this_page;
        } else if ($batch_failed_count > 0) {
            // Only failed records available for this batch
            $failed_on_this_page = min($length, max(0, $batch_failed_count - $start));
            $sent_limit = $length - $failed_on_this_page;
        } else {
            // Only sent records for this batch
            $failed_on_this_page = 0;
            $sent_limit = $length;
        }
        
        // Calculate the actual total records for this batch (for pagination)
        $total_count = $batch_sent_count + $batch_failed_count;
        
        // Use seed-based offset for sent records (memory efficient)
        $sent_records = [];
        if ($sent_limit > 0 && $batch_sent_count > 0) {
            // Calculate offset using batch seed
            $seed = intval($campaign_id . ($batch_number + 20));
            srand($seed);
            $sent_offset = rand(0, max(0, $total_sent_records - $batch_sent_count));
            srand();
            
            $this->db->select('campaign_user_data.id, campaign_user_data.status, master_data.mobile, master_data.name, master_data.email, users.username as assigned_username', FALSE);
            $this->db->from('campaign_user_data');
            $this->db->join('master_data', 'master_data.id = campaign_user_data.master_data_id', 'left');
            $this->db->join('users', 'users.id = campaign_user_data.user_id', 'left');
            $this->db->where('campaign_user_data.campaign_id', $campaign_id);
            $this->db->where('campaign_user_data.user_id', $user_id);
            $this->db->where('campaign_user_data.status', 'sent');
            
            if (!empty($search_value)) {
                $this->db->group_start();
                $this->db->like('master_data.mobile', $search_value);
                $this->db->or_like('master_data.name', $search_value);
                $this->db->or_like('master_data.email', $search_value);
                $this->db->group_end();
            }
            
            $this->db->order_by('campaign_user_data.id', 'ASC');
            $this->db->limit($sent_limit, $sent_offset);
            $sent_records = $this->db->get()->result();
        }
        
        // Use seed-based offset for failed records (memory efficient)
        $failed_records = [];
        if ($failed_on_this_page > 0 && $batch_failed_count > 0) {
            $seed = intval($campaign_id . ($batch_number + 30));
            srand($seed);
            $failed_offset = rand(0, max(0, $total_failed_records - $batch_failed_count));
            srand();
            
            $this->db->select('campaign_user_data.id, campaign_user_data.status, master_data.mobile, master_data.name, master_data.email, users.username as assigned_username', FALSE);
            $this->db->from('campaign_user_data');
            $this->db->join('master_data', 'master_data.id = campaign_user_data.master_data_id', 'left');
            $this->db->join('users', 'users.id = campaign_user_data.user_id', 'left');
            $this->db->where('campaign_user_data.campaign_id', $campaign_id);
            $this->db->where('campaign_user_data.user_id', $user_id);
            $this->db->where('campaign_user_data.status', 'failed');
            
            if (!empty($search_value)) {
                $this->db->group_start();
                $this->db->like('master_data.mobile', $search_value);
                $this->db->or_like('master_data.name', $search_value);
                $this->db->or_like('master_data.email', $search_value);
                $this->db->group_end();
            }
            
            $this->db->order_by('campaign_user_data.id', 'ASC');
            $this->db->limit($failed_on_this_page, $failed_offset);
            $failed_records = $this->db->get()->result();
        }
        
        // Combine and shuffle records for random display
        $paginated_records = array_merge($sent_records, $failed_records);
        shuffle($paginated_records);
        
        // Apply masking - optimized
        $data = [];
        $base_index = $start + 1;
        
        foreach ($paginated_records as $idx => $record) {
            // Handle empty email
            if (empty($record->email)) {
                $record->email = substr(str_shuffle('abcdefghijklmnopqrstuvwxyz'), 0, 3) . '***********@gmail.com';
            }
            
            $masked = mask_data($record);
            
            $data[] = [
                $base_index + $idx,
                $masked->mobile ?? 'N/A',
                $masked->name ?? 'N/A',
                $masked->email ?? 'N/A',
                $masked->status ?? 'pending',
                $record->assigned_username ?? 'N/A'
            ];
        }
        
        // Clean output and send pure JSON
        if (ob_get_level()) {
            ob_end_clean();
        }
        
        // Ensure clean JSON output
        header('Content-Type: application/json; charset=utf-8');
        
        $response = json_encode([
            'draw' => intval($draw),
            'recordsTotal' => $total_count,
            'recordsFiltered' => $total_count,
            'data' => $data
        ]);
        
        if ($response === false) {
            http_response_code(500);
            echo json_encode(['error' => 'JSON encoding failed: ' . json_last_error_msg()]);
        } else {
            echo $response;
        }
        exit; // Prevent any further output
    }

    /**
     * Export all batch sends data as JSON with caching
     */
    public function export_batch_sends($campaign_id, $batch_id) {
        // Increase limits for large exports - UNLIMITED
        set_time_limit(0);
        ini_set('memory_limit', '2048M');
        ini_set('max_execution_time', '0');
        ini_set('max_input_time', '0');
        
        // Clean any existing output buffers
        while (ob_get_level()) {
            ob_end_clean();
        }
        
        // Enable aggressive browser caching for exports
        header('Content-Type: application/json; charset=utf-8');
        header('Cache-Control: public, max-age=1800'); // Cache for 30 minutes
        header('Pragma: cache');
        header('Access-Control-Allow-Origin: *');
        header('Access-Control-Allow-Methods: GET, POST, OPTIONS');
        header('Access-Control-Allow-Headers: Content-Type');
        
        try {
            $user_id = current_user_id();
            
            if (!$user_id) {
                http_response_code(401);
                echo json_encode(['error' => 'Unauthorized']);
                return;
            }
            
            // Verify campaign access
            $this->db->where('campaign_id', $campaign_id);
            $this->db->where('user_id', $user_id);
            $assigned = $this->db->get('campaign_users')->row();
            
            if (!$assigned) {
                http_response_code(403);
                echo json_encode(['error' => 'Access denied']);
                return;
            }
            
            // Get batch info for range calculation
            $batch = $this->db->where('id', $batch_id)->get('scheduled_message_batches')->row();
            
            if (!$batch) {
                http_response_code(404);
                echo json_encode(['error' => 'Batch not found']);
                return;
            }
            
            // Get total batches for random distribution
            $this->db->select('COUNT(*) as batch_count');
            $this->db->from('scheduled_message_batches smb');
            $this->db->join('scheduled_messages sm', 'sm.id = smb.scheduled_message_id');
            $this->db->where('sm.campaign_id', $campaign_id);
            $total_batches = $this->db->get()->row()->batch_count;
            
            // Use standard batch slicing for export (safer approach)
            $batch_start_offset = ($batch->batch_number - 1) * $batch->batch_size;
            
            $this->db->select('campaign_user_data.status, master_data.mobile, master_data.name, master_data.email, users.username as assigned_username', FALSE);
            $this->db->from('campaign_user_data');
            $this->db->join('master_data', 'master_data.id = campaign_user_data.master_data_id', 'left');
            $this->db->join('users', 'users.id = campaign_user_data.user_id', 'left');
            $this->db->where('campaign_user_data.campaign_id', $campaign_id);
            $this->db->where('campaign_user_data.user_id', $user_id);
            $this->db->order_by('campaign_user_data.id', 'ASC');
            $this->db->limit($batch->batch_size, $batch_start_offset);
            
            $records = $this->db->get()->result();
            
            // Fast masking with minimal processing
            $data = [];
            foreach ($records as $record) {
                // Generate random mobile number if empty
                if (empty($record->mobile)) {
                    $record->mobile = '9' . rand(100000000, 999999999);
                }
                
                if (empty($record->email)) {
                    $record->email = substr(str_shuffle('abcdefghijklmnopqrstuvwxyz'), 0, 3) . '***********@gmail.com';
                }
                
                $masked = mask_data($record);
                $data[] = [
                    'mobile' => $masked->mobile ?? 'N/A',
                    'name' => $masked->name ?? 'N/A',
                    'email' => $masked->email ?? 'N/A',
                    'status' => $masked->status ?? 'pending',
                    'username' => $record->assigned_username ?? 'N/A'
                ];
            }
            
            // Add ETag for better caching
            $json_data = json_encode($data);
            
            if ($json_data === false) {
                http_response_code(500);
                echo json_encode(['error' => 'JSON encoding failed: ' . json_last_error_msg()]);
                return;
            }
            
            $etag = md5($json_data);
            header("ETag: \"$etag\"");
            
            echo $json_data;
        } catch (Exception $e) {
            http_response_code(500);
            echo json_encode(['error' => 'Server error: ' . $e->getMessage()]);
        }
    }

    /**
     * STREAM CSV EXPORT - Memory efficient for unlimited records
     * Direct download without loading everything into memory
     */
    public function stream_batch_sends_csv($campaign_id, $batch_id) {
        // Unlimited execution with minimal memory
        set_time_limit(0);
        ini_set('memory_limit', '256M'); // Only needs minimal memory!
        
        $user_id = current_user_id();
        
        if (!$user_id) {
            show_404();
            return;
        }
        
        // Verify campaign access
        $this->db->where('campaign_id', $campaign_id);
        $this->db->where('user_id', $user_id);
        $assigned = $this->db->get('campaign_users')->row();
        
        if (!$assigned) {
            show_404();
            return;
        }
        
        // Get batch info
        $batch = $this->db->where('id', $batch_id)->get('scheduled_message_batches')->row();
        
        if (!$batch) {
            show_404();
            return;
        }
        
        // Clean all output buffers
        while (ob_get_level()) {
            ob_end_clean();
        }
        
        // Set headers for CSV download
        $filename = 'Delivery_' . $batch->batch_number . '_' . date('Y-m-d') . '.csv';
        header('Content-Type: text/csv; charset=utf-8');
        header('Content-Disposition: attachment; filename="' . $filename . '"');
        header('Pragma: no-cache');
        header('Expires: 0');
        
        // Open output stream
        $output = fopen('php://output', 'w');
        
        // Write UTF-8 BOM for Excel compatibility
        fprintf($output, chr(0xEF).chr(0xBB).chr(0xBF));
        
        // Write CSV header
        fputcsv($output, ['#', 'Mobile', 'Name', 'Email', 'Status', 'Username']);
        
        // Use simple batch-based export (memory safe)
        $batch_start_offset = ($batch->batch_number - 1) * $batch->batch_size;
        
        // Stream records in chunks
        $chunk_size = 1000;
        $offset = 0;
        $row_number = 1;
        $total_exported = 0;
        
        while ($total_exported < $batch->batch_size) {
            // Fetch chunk from this batch's range
            $this->db->select('campaign_user_data.status, master_data.mobile, master_data.name, master_data.email, users.username as assigned_username', FALSE);
            $this->db->from('campaign_user_data');
            $this->db->join('master_data', 'master_data.id = campaign_user_data.master_data_id', 'left');
            $this->db->join('users', 'users.id = campaign_user_data.user_id', 'left');
            $this->db->where('campaign_user_data.campaign_id', $campaign_id);
            $this->db->where('campaign_user_data.user_id', $user_id);
            $this->db->order_by('campaign_user_data.id', 'ASC');
            $this->db->limit($chunk_size, $batch_start_offset + $offset);
            
            $records = $this->db->get()->result();
            
            if (empty($records)) {
                break;
            }
                
                // Write each record immediately to output
                foreach ($records as $record) {
                    // Generate random mobile if empty
                    if (empty($record->mobile)) {
                        $record->mobile = '9' . rand(100000000, 999999999);
                    }
                    
                    // Generate random email if empty  
                    if (empty($record->email)) {
                        $record->email = substr(str_shuffle('abcdefghijklmnopqrstuvwxyz'), 0, 3) . '***********@gmail.com';
                    }
                    
                    // Mask data
                    $masked = mask_data($record);
                    
                    // Write row
                    fputcsv($output, [
                        $row_number++,
                        $masked->mobile ?? 'N/A',
                        $masked->name ?? 'N/A',
                        $masked->email ?? 'N/A',
                        $masked->status ?? 'pending',
                        $record->assigned_username ?? 'N/A'
                    ]);
                    
                    // Flush output buffer every 100 records
                    if ($row_number % 100 == 0) {
                        flush();
                    }
                }
                
                $count = count($records);
                $total_exported += $count;
                $offset += $count;
            }
        
        // Close output stream
        fclose($output);
        exit;
    }

    /**
     * User Reports (View Only)
     */
    public function reports() {
        $data['page_title'] = 'My Reports';
        $user_id = current_user_id();
        
        // Get user's overall stats
        $data['activity_stats'] = $this->Send_model->get_user_activity_stats($user_id);
        
        // Get campaign-wise breakdown
        $data['campaigns'] = $this->Campaign_model->get_user_campaigns($user_id, 100, 0);
        
        $this->load->view('user/header', $data);
        $this->load->view('user/reports', $data);
        $this->load->view('user/footer');
    }

    /**
     * User Profile Management
     */
    public function profile() {
        $data['page_title'] = 'My Profile';
        $data['user'] = $this->db->where('id', current_user_id())->get('users')->row();
        
        if ($this->input->method() == 'post') {
            $this->form_validation->set_rules('username', 'Username', 'required|trim');
            $this->form_validation->set_rules('email', 'Email', 'required|valid_email');
            
            if ($this->input->post('new_password')) {
                $this->form_validation->set_rules('new_password', 'New Password', 'min_length[6]');
                $this->form_validation->set_rules('confirm_password', 'Confirm Password', 'matches[new_password]');
            }
            
            if ($this->form_validation->run() === TRUE) {
                $update_data = [
                    'username' => $this->input->post('username'),
                    'email' => $this->input->post('email'),
                    'updated_at' => date('Y-m-d H:i:s')
                ];
                
                if ($this->input->post('new_password')) {
                    $update_data['password'] = password_hash($this->input->post('new_password'), PASSWORD_BCRYPT);
                }
                
                if ($this->db->where('id', current_user_id())->update('users', $update_data)) {
                    $this->Log_model->log_audit(current_user_id(), 'Update Profile', 'Profile Management', 'Updated own profile');
                    set_flash('success', 'Profile updated successfully.');
                    redirect('user/profile');
                } else {
                    set_flash('error', 'Failed to update profile.');
                }
            }
        }
        
        $this->load->view('user/header', $data);
        $this->load->view('user/profile', $data);
        $this->load->view('user/footer');
    }

    /**
     * Get batch sends data (AJAX)
     */
    public function get_batch_sends() {
        if ($this->input->method() !== 'post') {
            echo json_encode(['success' => false, 'message' => 'Invalid request method']);
            return;
        }
        
        $batch_id = $this->input->post('batch_id');
        $schedule_id = $this->input->post('schedule_id');
        $campaign_id = $this->input->post('campaign_id');
        $user_id = current_user_id();
        
        // Verify user has access to this campaign
        $this->db->where('campaign_id', $campaign_id);
        $this->db->where('user_id', $user_id);
        $assigned = $this->db->get('campaign_users')->row();
        
        if (!$assigned) {
            echo json_encode(['success' => false, 'message' => 'Access denied']);
            return;
        }
        
        // Get batch information
        $batch = $this->db->where('id', $batch_id)
                          ->where('scheduled_message_id', $schedule_id)
                          ->get('scheduled_message_batches')
                          ->row();
        
        if (!$batch) {
            echo json_encode(['success' => false, 'message' => 'Batch not found']);
            return;
        }
        
        // Get send statistics for this batch
        // Since we don't have a direct batch_id in send_logs, we'll use the time range
        $batch_start = $batch->sent_at ? date('Y-m-d H:i:s', strtotime($batch->sent_at . ' -5 minutes')) : $batch->scheduled_for;
        $batch_end = $batch->sent_at ? date('Y-m-d H:i:s', strtotime($batch->sent_at . ' +5 minutes')) : date('Y-m-d H:i:s', strtotime($batch->scheduled_for . ' +10 minutes'));
        
        // Get overall stats
        $this->db->select('COUNT(*) as total, 
                          SUM(CASE WHEN status = "sent" THEN 1 ELSE 0 END) as sent,
                          SUM(CASE WHEN status = "failed" THEN 1 ELSE 0 END) as failed');
        $this->db->from('send_logs');
        $this->db->where('campaign_id', $campaign_id);
        $this->db->where('created_at >=', $batch_start);
        $this->db->where('created_at <=', $batch_end);
        $overall_stats = $this->db->get()->row();
        
        // Calculate success rate
        $success_rate = $overall_stats->total > 0 
            ? round(($overall_stats->sent / $overall_stats->total) * 100, 2) 
            : 0;
        
        // Get stats by user
        $this->db->select('users.username, users.id,
                          COUNT(*) as total,
                          SUM(CASE WHEN send_logs.status = "sent" THEN 1 ELSE 0 END) as sent,
                          SUM(CASE WHEN send_logs.status = "failed" THEN 1 ELSE 0 END) as failed');
        $this->db->from('send_logs');
        $this->db->join('users', 'users.id = send_logs.user_id');
        $this->db->where('send_logs.campaign_id', $campaign_id);
        $this->db->where('send_logs.created_at >=', $batch_start);
        $this->db->where('send_logs.created_at <=', $batch_end);
        $this->db->group_by('send_logs.user_id');
        $this->db->order_by('total', 'DESC');
        $user_stats = $this->db->get()->result();
        
        // Calculate success rate for each user
        foreach ($user_stats as $stat) {
            $stat->success_rate = $stat->total > 0 
                ? round(($stat->sent / $stat->total) * 100, 2) 
                : 0;
        }
        
        echo json_encode([
            'success' => true,
            'data' => [
                'stats' => [
                    'total' => (int)$overall_stats->total,
                    'sent' => (int)$overall_stats->sent,
                    'failed' => (int)$overall_stats->failed,
                    'success_rate' => $success_rate
                ],
                'user_stats' => $user_stats
            ]
        ]);
    }

    /**
     * Create pagination helper
     */
    private function _create_pagination($total_rows, $per_page, $base_url) {
        return [
            'total_rows' => $total_rows,
            'per_page' => $per_page,
            'total_pages' => ceil($total_rows / $per_page),
            'current_page' => $this->input->get('page') ? $this->input->get('page') : 1,
            'base_url' => base_url($base_url)
        ];
    }
}
