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

class Data_model extends CI_Model {

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

    /**
     * Insert master data from CSV/Excel - Optimized for 2.1M+ records
     */
    public function insert_bulk_data($data_array) {
        set_time_limit(600);
        ini_set('memory_limit', '1024M');
        
        $this->db->trans_start();
        
        // Process in chunks of 5000 for efficiency
        $chunks = array_chunk($data_array, 5000);
        $inserted = 0;
        
        foreach ($chunks as $chunk) {
            // Build bulk insert for efficiency
            $values = [];
            $timestamp = date('Y-m-d H:i:s');
            
            foreach ($chunk as $row) {
                // Escape values for safety
                $mobile = $this->db->escape($row['mobile']);
                $name = $this->db->escape($row['name'] ?? '');
                $email = $this->db->escape($row['email'] ?? '');
                $address = $this->db->escape($row['address'] ?? '');
                $cf1 = $this->db->escape($row['custom_field1'] ?? '');
                $cf2 = $this->db->escape($row['custom_field2'] ?? '');
                $cf3 = $this->db->escape($row['custom_field3'] ?? '');
                
                $values[] = "($mobile, $name, $email, $address, $cf1, $cf2, $cf3, '$timestamp')";
            }
            
            if (!empty($values)) {
                // Use INSERT IGNORE to skip duplicates
                $sql = "INSERT IGNORE INTO master_data (mobile, name, email, address, custom_field1, custom_field2, custom_field3, uploaded_at) VALUES " . implode(',', $values);
                $this->db->query($sql);
                $inserted += $this->db->affected_rows();
            }
            
            // Free memory
            unset($values, $chunk);
        }
        
        $this->db->trans_complete();
        return $this->db->trans_status() ? $inserted : false;
    }
    
    /**
     * Insert bulk data with progress callback for very large files (2.1M+)
     */
    public function insert_bulk_data_batch($data_array, $batch_num = 0, $total_batches = 1) {
        set_time_limit(300);
        ini_set('memory_limit', '1024M');
        
        $this->db->trans_start();
        
        $values = [];
        $timestamp = date('Y-m-d H:i:s');
        
        foreach ($data_array as $row) {
            $mobile = $this->db->escape($row['mobile']);
            $name = $this->db->escape($row['name'] ?? '');
            $email = $this->db->escape($row['email'] ?? '');
            $address = $this->db->escape($row['address'] ?? '');
            $cf1 = $this->db->escape($row['custom_field1'] ?? '');
            $cf2 = $this->db->escape($row['custom_field2'] ?? '');
            $cf3 = $this->db->escape($row['custom_field3'] ?? '');
            
            $values[] = "($mobile, $name, $email, $address, $cf1, $cf2, $cf3, '$timestamp')";
        }
        
        $inserted = 0;
        if (!empty($values)) {
            $sql = "INSERT IGNORE INTO master_data (mobile, name, email, address, custom_field1, custom_field2, custom_field3, uploaded_at) VALUES " . implode(',', $values);
            $this->db->query($sql);
            $inserted = $this->db->affected_rows();
        }
        
        $this->db->trans_complete();
        
        return [
            'success' => $this->db->trans_status(),
            'inserted' => $inserted,
            'batch' => $batch_num,
            'total_batches' => $total_batches
        ];
    }

    /**
     * Get all master data with pagination
     */
    public function get_all_data($limit = 10, $offset = 0, $is_assigned = null) {
        if ($is_assigned !== null) {
            $this->db->where('is_assigned', $is_assigned);
        }
        
        $this->db->limit($limit, $offset);
        $this->db->order_by('uploaded_at', 'DESC');
        
        return $this->db->get('master_data')->result();
    }

    /**
     * Count master data
     */
    public function count_data($is_assigned = null) {
        if ($is_assigned !== null) {
            $this->db->where('is_assigned', $is_assigned);
        }
        
        return $this->db->count_all_results('master_data');
    }

    /**
     * Get available data for campaign
     */
    public function get_available_data_for_campaign($campaign_id, $limit = null, $exclude_ids = [], $gender = null) {
        $this->db->select('master_data.*');
        $this->db->from('master_data');
        $this->db->join('campaign_user_data AS cud', 'cud.master_data_id = master_data.id AND cud.campaign_id = ' . (int) $campaign_id, 'left');
        $this->db->where('cud.id IS NULL', null, false);
        
        if (!empty($exclude_ids)) {
            $this->db->where_not_in('master_data.id', $exclude_ids);
        }
        
        if ($gender !== null) {
            $this->db->where('master_data.gender', $gender);
        }
        
        if ($limit) {
            $this->db->limit($limit);
        }
        
        $this->db->order_by('master_data.id', 'ASC');
        
        return $this->db->get()->result();
    }

    /**
     * Assign data to campaign user with fail percentage calculation
     * For small datasets (< 50000), uses the old synchronous method
     * For large datasets, returns info for batch processing
     */
    public function assign_data_to_campaign($campaign_id, $user_id, $count, $fail_percentage) {
        set_time_limit(600);
        ini_set('memory_limit', '1024M');
        
        // Calculate fail count
        $fail_count = ceil(($count * $fail_percentage) / 100);
        $total_count = $count + $fail_count;
        
        // For large datasets (>25000), return batch info for async processing
        if ($total_count > 25000) {
            // Count available data
            $available = $this->count_available_for_campaign($campaign_id);
            
            if ($available < 1) {
                return ['success' => false, 'message' => 'No unassigned data available for this campaign.', 'available' => 0];
            }
            
            return [
                'success' => true,
                'batch_required' => true,
                'total_count' => min($total_count, $available),
                'original_count' => $count,
                'fail_count' => $fail_count,
                'available' => $available,
                'batch_size' => 25000  // Optimized for 2.1M+ records
            ];
        }
        
        // For small datasets, process synchronously
        return $this->process_assignment_batch($campaign_id, $user_id, $total_count, 0, $total_count);
    }
    
    /**
     * Count available data for campaign
     */
    public function count_available_for_campaign($campaign_id) {
        $sql = "SELECT COUNT(*) as cnt FROM master_data 
                LEFT JOIN campaign_user_data cud ON cud.master_data_id = master_data.id AND cud.campaign_id = ?
                WHERE cud.id IS NULL";
        $result = $this->db->query($sql, [$campaign_id])->row();
        return $result ? (int)$result->cnt : 0;
    }
    
    /**
     * Process a batch of data assignment
     * @param int $campaign_id
     * @param int $user_id
     * @param int $batch_size Number of records to process in this batch
     * @param int $offset Starting offset (records already processed)
     * @param int $total_count Total records to be assigned
     * @return array
     */
    public function process_assignment_batch($campaign_id, $user_id, $batch_size, $offset, $total_count) {
        set_time_limit(300);
        ini_set('memory_limit', '1024M');
        
        // Get available data IDs for this batch
        $sql = "SELECT master_data.id FROM master_data 
                LEFT JOIN campaign_user_data cud ON cud.master_data_id = master_data.id AND cud.campaign_id = ?
                WHERE cud.id IS NULL
                ORDER BY master_data.id ASC
                LIMIT ?";
        
        $query = $this->db->query($sql, [(int)$campaign_id, (int)$batch_size]);
        $data_ids = array_column($query->result_array(), 'id');
        
        if (count($data_ids) < 1) {
            return [
                'success' => true, 
                'assigned_count' => 0, 
                'message' => 'No more data available.',
                'completed' => true,
                'total_processed' => $offset
            ];
        }
        
        $actual_count = count($data_ids);
        
        $this->db->trans_start();
        
        // Build and insert values in chunks of 5000 for faster bulk inserts (optimized for 2.1M+ records)
        $timestamp = date('Y-m-d H:i:s');
        $chunks = array_chunk($data_ids, 5000);
        
        foreach ($chunks as $chunk) {
            $values = [];
            foreach ($chunk as $data_id) {
                $values[] = "($campaign_id, $user_id, $data_id, 'pending', 0, '$timestamp')";
            }
            
            $values_str = implode(',', $values);
            $sql = "INSERT INTO campaign_user_data (campaign_id, user_id, master_data_id, status, is_auto_added, assigned_at) 
                    VALUES $values_str";
            $this->db->query($sql);
            
            // Update master_data for this chunk
            $ids_str = implode(',', $chunk);
            $this->db->query("UPDATE master_data SET is_assigned = 1 WHERE id IN ($ids_str)");
        }
        
        $this->db->trans_complete();
        
        if ($this->db->trans_status()) {
            $new_total = $offset + $actual_count;
            $completed = ($actual_count < $batch_size) || ($new_total >= $total_count);
            
            return [
                'success' => true,
                'assigned_count' => $actual_count,
                'total_processed' => $new_total,
                'total_count' => $total_count,
                'completed' => $completed,
                'progress' => min(100, round(($new_total / $total_count) * 100, 1))
            ];
        }
        
        return ['success' => false, 'message' => 'Database error while assigning data.'];
    }

    /**
     * Get next pending data for user in campaign
     */
    public function get_next_pending_data($campaign_id, $user_id) {
        $this->db->select('campaign_user_data.id as cud_id, campaign_user_data.status, 
                          campaign_user_data.is_auto_added, campaign_user_data.assigned_at,
                          master_data.id as master_data_id, master_data.mobile, master_data.name, 
                          master_data.email, master_data.address, master_data.custom_field1, 
                          master_data.custom_field2, master_data.custom_field3');
        $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->where('campaign_user_data.status', 'pending');
        $this->db->limit(1);
        
        return $this->db->get()->row();
    }

    /**
     * Get batch of pending data for bulk processing
     */
    public function get_pending_batch($campaign_id, $user_id, $limit = 10) {
        $this->db->select('campaign_user_data.id as cud_id, campaign_user_data.status, 
                          campaign_user_data.is_auto_added, campaign_user_data.assigned_at,
                          master_data.id as master_data_id, master_data.mobile, master_data.name, 
                          master_data.email, master_data.address');
        $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->where('campaign_user_data.status', 'pending');
        $this->db->limit($limit);
        
        return $this->db->get()->result();
    }

    /**
     * Get campaign user data with masking
     */
    public function get_campaign_user_data($campaign_id, $user_id, $limit = 10, $offset = 0, $status = null) {
        $this->db->select('campaign_user_data.id as cud_id, campaign_user_data.status, 
                          campaign_user_data.sent_at, campaign_user_data.is_auto_added,
                          master_data.id, master_data.mobile, master_data.name, 
                          master_data.email, master_data.address');
        $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);
        
        if ($status) {
            $this->db->where('campaign_user_data.status', $status);
        }
        
        $this->db->limit($limit, $offset);
        $this->db->order_by('campaign_user_data.assigned_at', 'DESC');
        
        return $this->db->get()->result();
    }

    /**
     * Count campaign user data
     */
    public function count_campaign_user_data($campaign_id, $user_id, $status = null) {
        $this->db->from('campaign_user_data');
        $this->db->where('campaign_id', $campaign_id);
        $this->db->where('user_id', $user_id);
        
        if ($status) {
            $this->db->where('status', $status);
        }
        
        return $this->db->count_all_results();
    }

    /**
     * Update campaign user data status
     */
    public function update_data_status($cud_id, $status, $message_content = null) {
        $update_data = [
            'status' => $status,
            'sent_at' => date('Y-m-d H:i:s')
        ];
        
        if ($message_content) {
            $update_data['message_content'] = $message_content;
        }
        
        $this->db->where('id', $cud_id);
        return $this->db->update('campaign_user_data', $update_data);
    }

    /**
     * Auto-add new data when message fails
     */
    public function auto_add_on_fail($campaign_id, $user_id, $original_data_id) {
        // Get one unassigned data
        $new_data = $this->get_available_data_for_campaign($campaign_id, 1, [$original_data_id]);
        
        if (empty($new_data)) {
            return false;
        }
        
        $new_data_item = $new_data[0];
        
        $this->db->trans_start();
        
        // Insert into campaign_user_data
        $this->db->insert('campaign_user_data', [
            'campaign_id' => $campaign_id,
            'user_id' => $user_id,
            'master_data_id' => $new_data_item->id,
            'status' => 'pending',
            'is_auto_added' => 1
        ]);
        
        $new_cud_id = $this->db->insert_id();
        
        // Mark as assigned
        $this->db->where('id', $new_data_item->id);
        $this->db->update('master_data', ['is_assigned' => 1]);
        
        // Log the auto-add action
        $this->db->insert('fail_auto_add', [
            'campaign_id' => $campaign_id,
            'user_id' => $user_id,
            'original_data_id' => $original_data_id,
            'new_data_id' => $new_cud_id
        ]);
        
        $this->db->trans_complete();
        
        return $this->db->trans_status() ? $new_cud_id : false;
    }

    /**
     * Get master data by ID
     */
    public function get_master_data_by_id($id) {
        return $this->db->get_where('master_data', ['id' => $id])->row();
    }

    /**
     * Delete master data
     */
    public function delete_master_data($id) {
        return $this->db->delete('master_data', ['id' => $id, 'is_assigned' => 0]);
    }

    /**
     * Search master data
     */
    public function search_data($keyword, $limit = 10, $offset = 0) {
        $this->db->like('mobile', $keyword);
        $this->db->or_like('name', $keyword);
        $this->db->or_like('email', $keyword);
        $this->db->limit($limit, $offset);
        
        return $this->db->get('master_data')->result();
    }

    /**
     * Get dashboard stats
     */
    public function get_dashboard_stats() {
        return [
            'total_data' => $this->count_data(),
            'assigned_data' => $this->count_data(1),
            'unassigned_data' => $this->count_data(0)
        ];
    }
}
