class-wes-students-module.php

<?php
/**
 * Working English System - Módulo de Estudiantes
 * 
 * @package WorkingEnglishSystem
 * @subpackage Modules/Students
 * @version 1.0.0
 */

if (!defined('ABSPATH')) {
    exit;
}

class WES_Students_Module {
    
    private $db;
    private $table_students;
    private $table_enrollments;
    private $table_payments;
    
    public function __construct() {
        global $wpdb;
        $this->db = $wpdb;
        $this->table_students = $wpdb->prefix . 'wes_students';
        $this->table_enrollments = $wpdb->prefix . 'wes_enrollments';
        $this->table_payments = $wpdb->prefix . 'wes_payments';
        
        $this->init_hooks();
    }
    
    /**
     * Inicializar hooks y acciones
     */
    private function init_hooks() {
        add_action('wp_ajax_wes_get_students', array($this, 'ajax_get_students'));
        add_action('wp_ajax_wes_save_student', array($this, 'ajax_save_student'));
        add_action('wp_ajax_wes_delete_student', array($this, 'ajax_delete_student'));
        add_action('wp_ajax_wes_get_student', array($this, 'ajax_get_student'));
        add_action('wp_ajax_wes_search_students', array($this, 'ajax_search_students'));
        add_action('wp_ajax_wes_export_students', array($this, 'ajax_export_students'));
        add_action('wp_ajax_wes_update_student_status', array($this, 'ajax_update_student_status'));
        add_action('wp_ajax_wes_get_students_stats', array($this, 'ajax_get_students_stats'));
        // AJAX para frontend (no-priv significa usuarios en frontend)
        add_action('wp_ajax_nopriv_wes_get_students', array($this, 'ajax_get_students'));
        add_action('wp_ajax_nopriv_wes_save_student', array($this, 'ajax_save_student'));
        add_action('wp_ajax_nopriv_wes_delete_student', array($this, 'ajax_delete_student'));
        add_action('wp_ajax_nopriv_wes_get_student', array($this, 'ajax_get_student'));
        add_action('wp_ajax_nopriv_wes_search_students', array($this, 'ajax_search_students'));
        add_action('wp_ajax_nopriv_wes_export_students', array($this, 'ajax_export_students'));
        add_action('wp_ajax_nopriv_wes_update_student_status', array($this, 'ajax_update_student_status'));
        add_action('wp_ajax_nopriv_wes_get_students_stats', array($this, 'ajax_get_students_stats'));
        // Registrar scripts y estilos específicos del módulo
        add_action('admin_enqueue_scripts', array($this, 'enqueue_scripts'));
    }
    
    /**
     * Cargar scripts y estilos del módulo
     */
    public function enqueue_scripts($hook) {
        if (strpos($hook, 'working-english-system') !== false) {
            wp_enqueue_script(
                'wes-students-js',
                WES_PLUGIN_URL . 'modules/students/assets/students.js',
                array('jquery', 'wes-main-js'),
                WES_VERSION,
                true
            );
            
            wp_enqueue_style(
                'wes-students-css',
                WES_PLUGIN_URL . 'modules/students/assets/students.css',
                array('wes-main-css'),
                WES_VERSION
            );
        }
    }
    
    /**
     * Renderizar la página principal del módulo de estudiantes
     */
    public function render_students_page() {
        // Verificar permisos
        if (!current_user_can('manage_options')) {
            wp_die(__('No tienes permisos para acceder a esta página.', 'working-english-system'));
        }
        
        include WES_PLUGIN_PATH . 'modules/students/templates/students-main.php';
    }
    
    /**
     * AJAX: Obtener lista de estudiantes con paginación y filtros
     */
    public function ajax_get_students() {
        check_ajax_referer('wes_nonce', 'nonce');
        
        if (!current_user_can('manage_options')) {
            wp_send_json_error('Permisos insuficientes');
        }
        
        $page = intval($_POST['page'] ?? 1);
        $per_page = intval($_POST['per_page'] ?? 20);
        $search = sanitize_text_field($_POST['search'] ?? '');
        $status = sanitize_text_field($_POST['status'] ?? '');
        $country = sanitize_text_field($_POST['country'] ?? '');
        $sort_by = sanitize_text_field($_POST['sort_by'] ?? 'created_at');
        $sort_order = sanitize_text_field($_POST['sort_order'] ?? 'DESC');
        
        $offset = ($page - 1) * $per_page;
        
        // Construir consulta base
        $where_conditions = array("s.deleted_at IS NULL");
        $params = array();
        
        // Filtro de búsqueda ampliado
        if (!empty($search)) {
            $where_conditions[] = "(s.first_name LIKE %s OR s.last_name LIKE %s OR s.email LIKE %s OR s.phone LIKE %s OR s.student_id LIKE %s OR s.city LIKE %s OR s.identity_document LIKE %s)";
            $search_term = '%' . $this->db->esc_like($search) . '%';
            $params = array_merge($params, array($search_term, $search_term, $search_term, $search_term, $search_term, $search_term, $search_term));
        }
        
        // Filtro de estado
        if (!empty($status)) {
            $where_conditions[] = "s.status = %s";
            $params[] = $status;
        }
        
        // Filtro por país
        if (!empty($country)) {
            $where_conditions[] = "s.country_of_origin = %s";
            $params[] = $country;
        }
        
        $where_clause = 'WHERE ' . implode(' AND ', $where_conditions);
        
        // Validar columnas de ordenamiento
        $allowed_sort_columns = array('student_id', 'first_name', 'last_name', 'email', 'country_of_origin', 'city', 'status', 'created_at');
        if (!in_array($sort_by, $allowed_sort_columns)) {
            $sort_by = 'created_at';
        }
        
        $sort_order = strtoupper($sort_order) === 'ASC' ? 'ASC' : 'DESC';
        
        // Consulta principal
        $query = "
            SELECT s.*
            FROM {$this->table_students} s
            {$where_clause}
            ORDER BY s.{$sort_by} {$sort_order}
            LIMIT %d OFFSET %d
        ";
        
        $params[] = $per_page;
        $params[] = $offset;
        
        $students = $this->db->get_results($this->db->prepare($query, $params));
        
        // Consulta para el total de registros
        $count_query = "
            SELECT COUNT(s.id) as total
            FROM {$this->table_students} s
            {$where_clause}
        ";
        
        $count_params = array_slice($params, 0, -2); // Remover LIMIT y OFFSET
        $total_students = $this->db->get_var($this->db->prepare($count_query, $count_params));
        
        // Formatear datos de estudiantes
        $formatted_students = array();
        foreach ($students as $student) {
            $formatted_students[] = array(
                'id' => $student->id,
                'student_id' => $student->student_id,
                'first_name' => $student->first_name,
                'last_name' => $student->last_name,
                'full_name' => $student->first_name . ' ' . $student->last_name,
                'email' => $student->email,
                'phone' => $student->phone,
                'date_of_birth' => $student->date_of_birth,
                'gender' => $student->gender,
                'country_of_origin' => $student->country_of_origin,
                'nationality' => $student->nationality,
                'identity_document' => $student->identity_document,
                'city' => $student->city,
                'address' => $student->address,
                'emergency_contact' => $student->emergency_contact,
                'emergency_email' => $student->emergency_email,
                'emergency_phone' => $student->emergency_phone,
                'emergency_relationship' => $student->emergency_relationship,
                'how_did_you_hear' => $student->how_did_you_hear,
                'referred_by' => $student->referred_by,
                'special_notes' => $student->special_notes,
                'current_level' => $student->current_level,
                'status' => $student->status,
                'notes' => $student->notes,
                'created_at' => $student->created_at,
                'updated_at' => $student->updated_at,
                'status_label' => $this->get_status_label($student->status)
            );
        }
        
        wp_send_json_success(array(
            'students' => $formatted_students,
            'pagination' => array(
                'current_page' => $page,
                'per_page' => $per_page,
                'total_items' => intval($total_students),
                'total_pages' => ceil($total_students / $per_page)
            )
        ));
    }
    
    /**
     * AJAX: Guardar estudiante (crear o actualizar)
     */
    public function ajax_save_student() {
        check_ajax_referer('wes_nonce', 'nonce');
        
        if (!current_user_can('manage_options')) {
            wp_send_json_error('Permisos insuficientes');
        }
        
        $student_id = intval($_POST['student_id'] ?? 0);
        $student_data = $this->sanitize_student_data($_POST);
        
        // Validar datos requeridos
        $validation_result = $this->validate_student_data($student_data, $student_id);
        if (!$validation_result['valid']) {
            wp_send_json_error($validation_result['message']);
        }
        
        if ($student_id > 0) {
            // Actualizar estudiante existente
            $result = $this->update_student($student_id, $student_data);
        } else {
            // Crear nuevo estudiante
            $result = $this->create_student($student_data);
        }
        
        if ($result['success']) {
            wp_send_json_success($result['data']);
        } else {
            wp_send_json_error($result['message']);
        }
    }
    
    /**
     * AJAX: Eliminar estudiante (soft delete)
     */
    public function ajax_delete_student() {
        check_ajax_referer('wes_nonce', 'nonce');
        
        if (!current_user_can('manage_options')) {
            wp_send_json_error('Permisos insuficientes');
        }
        
        $student_id = intval($_POST['student_id'] ?? 0);
        
        if ($student_id <= 0) {
            wp_send_json_error('ID de estudiante inválido');
        }
        
        // Verificar si el estudiante existe
        $student = $this->get_student_by_id($student_id);
        if (!$student) {
            wp_send_json_error('Estudiante no encontrado');
        }
        
        // Realizar soft delete
        $result = $this->db->update(
            $this->table_students,
            array(
                'deleted_at' => current_time('mysql'),
                'updated_at' => current_time('mysql')
            ),
            array('id' => $student_id),
            array('%s', '%s'),
            array('%d')
        );
        
        if ($result !== false) {
            wp_send_json_success('Estudiante eliminado correctamente');
        } else {
            wp_send_json_error('Error al eliminar el estudiante');
        }
    }
    
    /**
     * AJAX: Obtener datos de un estudiante específico
     */
    public function ajax_get_student() {
        check_ajax_referer('wes_nonce', 'nonce');
        
        if (!current_user_can('manage_options')) {
            wp_send_json_error('Permisos insuficientes');
        }
        
        $student_id = intval($_POST['student_id'] ?? 0);
        
        if ($student_id <= 0) {
            wp_send_json_error('ID de estudiante inválido');
        }
        
        $student = $this->get_student_by_id($student_id);
        
        if (!$student) {
            wp_send_json_error('Estudiante no encontrado');
        }
        
        wp_send_json_success($student);
    }
    
    /**
     * AJAX: Búsqueda rápida de estudiantes
     */
    public function ajax_search_students() {
        check_ajax_referer('wes_nonce', 'nonce');
        
        if (!current_user_can('manage_options')) {
            wp_send_json_error('Permisos insuficientes');
        }
        
        $search_term = sanitize_text_field($_POST['search'] ?? '');
        $limit = intval($_POST['limit'] ?? 10);
        
        if (strlen($search_term) < 2) {
            wp_send_json_success(array());
        }
        
        $query = "
            SELECT id, student_id, first_name, last_name, email, phone, city, country_of_origin, status
            FROM {$this->table_students}
            WHERE deleted_at IS NULL
            AND (first_name LIKE %s OR last_name LIKE %s OR email LIKE %s OR student_id LIKE %s OR city LIKE %s OR phone LIKE %s)
            ORDER BY first_name ASC, last_name ASC
            LIMIT %d
        ";
        
        $search_like = '%' . $this->db->esc_like($search_term) . '%';
        $results = $this->db->get_results($this->db->prepare(
            $query, 
            $search_like, 
            $search_like, 
            $search_like, 
            $search_like, 
            $search_like,
            $search_like,
            $limit
        ));
        
        $formatted_results = array();
        foreach ($results as $student) {
            $formatted_results[] = array(
                'id' => $student->id,
                'student_id' => $student->student_id,
                'name' => $student->first_name . ' ' . $student->last_name,
                'email' => $student->email,
                'phone' => $student->phone,
                'city' => $student->city,
                'country' => $student->country_of_origin,
                'status' => $student->status
            );
        }
        
        wp_send_json_success($formatted_results);
    }
    
    /**
     * AJAX: Actualizar estado de estudiante
     */
    public function ajax_update_student_status() {
        check_ajax_referer('wes_nonce', 'nonce');
        
        if (!current_user_can('manage_options')) {
            wp_send_json_error('Permisos insuficientes');
        }
        
        $student_id = intval($_POST['student_id'] ?? 0);
        $new_status = sanitize_text_field($_POST['status'] ?? '');
        
        if ($student_id <= 0) {
            wp_send_json_error('ID de estudiante inválido');
        }
        
        $allowed_statuses = array('active', 'inactive', 'graduated', 'suspended', 'retired');
        if (!in_array($new_status, $allowed_statuses)) {
            wp_send_json_error('Estado inválido');
        }
        
        $result = $this->db->update(
            $this->table_students,
            array(
                'status' => $new_status,
                'updated_at' => current_time('mysql')
            ),
            array('id' => $student_id),
            array('%s', '%s'),
            array('%d')
        );
        
        if ($result !== false) {
            wp_send_json_success('Estado actualizado correctamente');
        } else {
            wp_send_json_error('Error al actualizar el estado');
        }
    }
    
    /**
     * AJAX: Obtener estadísticas de estudiantes
     */
    public function ajax_get_students_stats() {
        check_ajax_referer('wes_nonce', 'nonce');
        
        if (!current_user_can('manage_options')) {
            wp_send_json_error('Permisos insuficientes');
        }
        
        global $wpdb;
        
        // Consulta optimizada para obtener todas las estadísticas
        $stats_query = "
            SELECT 
                COUNT(*) as total_students,
                SUM(CASE WHEN status = 'active' THEN 1 ELSE 0 END) as active_students,
                SUM(CASE WHEN status = 'graduated' THEN 1 ELSE 0 END) as graduated_students,
                SUM(CASE WHEN status = 'inactive' THEN 1 ELSE 0 END) as inactive_students,
                SUM(CASE WHEN status = 'suspended' THEN 1 ELSE 0 END) as suspended_students,
                SUM(CASE WHEN status = 'retired' THEN 1 ELSE 0 END) as retired_students,
                SUM(CASE WHEN created_at >= DATE_SUB(CURDATE(), INTERVAL 30 DAY) THEN 1 ELSE 0 END) as new_students_30_days,
                SUM(CASE WHEN created_at >= DATE_SUB(CURDATE(), INTERVAL 7 DAY) THEN 1 ELSE 0 END) as new_students_7_days
            FROM {$this->table_students} 
            WHERE deleted_at IS NULL
        ";
        
        $stats = $this->db->get_row($stats_query);
        
        if (!$stats) {
            wp_send_json_error('Error al obtener estadísticas');
        }
        
        wp_send_json_success(array(
            'total_students' => intval($stats->total_students),
            'active_students' => intval($stats->active_students),
            'graduated_students' => intval($stats->graduated_students),
            'inactive_students' => intval($stats->inactive_students),
            'suspended_students' => intval($stats->suspended_students),
            'retired_students' => intval($stats->retired_students),
            'new_students_30_days' => intval($stats->new_students_30_days),
            'new_students_7_days' => intval($stats->new_students_7_days),
            'timestamp' => current_time('mysql')
        ));
    }

    /**
     * Crear nuevo estudiante
     */
    private function create_student($data) {
        // Generar ID único de estudiante
        $data['student_id'] = $this->generate_student_id();
        $data['created_at'] = current_time('mysql');
        $data['updated_at'] = current_time('mysql');
        
        $result = $this->db->insert($this->table_students, $data);
        
        if ($result) {
            $student_id = $this->db->insert_id;
            $student = $this->get_student_by_id($student_id);
            
            return array(
                'success' => true,
                'data' => $student,
                'message' => 'Estudiante creado correctamente'
            );
        } else {
            return array(
                'success' => false,
                'message' => 'Error al crear el estudiante'
            );
        }
    }
    
    /**
     * Actualizar estudiante existente
     */
    private function update_student($student_id, $data) {
        $data['updated_at'] = current_time('mysql');
        
        $result = $this->db->update(
            $this->table_students,
            $data,
            array('id' => $student_id),
            null,
            array('%d')
        );
        
        if ($result !== false) {
            $student = $this->get_student_by_id($student_id);
            
            return array(
                'success' => true,
                'data' => $student,
                'message' => 'Estudiante actualizado correctamente'
            );
        } else {
            return array(
                'success' => false,
                'message' => 'Error al actualizar el estudiante'
            );
        }
    }
    
    /**
     * Obtener estudiante por ID
     */
    private function get_student_by_id($student_id) {
        $query = "SELECT * FROM {$this->table_students} WHERE id = %d AND deleted_at IS NULL";
        return $this->db->get_row($this->db->prepare($query, $student_id));
    }
    
    /**
     * Generar ID único de estudiante
     */
    private function generate_student_id() {
        $prefix = 'WES';
        $year = date('Y');
        
        // Obtener el último número secuencial del año
        $last_student = $this->db->get_row($this->db->prepare(
            "SELECT student_id FROM {$this->table_students} 
             WHERE student_id LIKE %s 
             ORDER BY student_id DESC LIMIT 1",
            $prefix . $year . '%'
        ));
        
        if ($last_student) {
            $last_number = intval(substr($last_student->student_id, -4));
            $new_number = $last_number + 1;
        } else {
            $new_number = 1;
        }
        
        return $prefix . $year . str_pad($new_number, 4, '0', STR_PAD_LEFT);
    }
    
    /**
     * Sanitizar datos del estudiante
     */
    private function sanitize_student_data($data) {
        return array(
            // Datos Personales
            'first_name' => sanitize_text_field($data['first_name'] ?? ''),
            'last_name' => sanitize_text_field($data['last_name'] ?? ''),
            'date_of_birth' => sanitize_text_field($data['date_of_birth'] ?? ''),
            'gender' => sanitize_text_field($data['gender'] ?? ''),
            'country_of_origin' => sanitize_text_field($data['country_of_origin'] ?? ''),
            'nationality' => sanitize_text_field($data['nationality'] ?? ''),
            'identity_document' => sanitize_text_field($data['identity_document'] ?? ''),
            
            // Información de Contacto
            'email' => sanitize_email($data['email'] ?? ''),
            'phone' => sanitize_text_field($data['phone'] ?? ''),
            'city' => sanitize_text_field($data['city'] ?? ''),
            'address' => sanitize_textarea_field($data['address'] ?? ''),
            
            // Responsables/Contacto de Emergencia
            'emergency_contact' => sanitize_text_field($data['emergency_contact'] ?? ''),
            'emergency_email' => sanitize_email($data['emergency_email'] ?? ''),
            'emergency_phone' => sanitize_text_field($data['emergency_phone'] ?? ''),
            'emergency_relationship' => sanitize_text_field($data['emergency_relationship'] ?? ''),
            
            // Información Adicional
            'how_did_you_hear' => sanitize_text_field($data['how_did_you_hear'] ?? ''),
            'referred_by' => sanitize_text_field($data['referred_by'] ?? ''),
            'special_notes' => sanitize_textarea_field($data['special_notes'] ?? ''),
            
            // Campos del sistema
            'current_level' => sanitize_text_field($data['current_level'] ?? 'beginner'),
            'status' => sanitize_text_field($data['status'] ?? 'active'),
            'notes' => sanitize_textarea_field($data['notes'] ?? '')
        );
    }
    
    /**
     * Validar datos del estudiante
     */
    private function validate_student_data($data, $student_id = 0) {
        if (empty($data['first_name'])) {
            return array('valid' => false, 'message' => 'El nombre es requerido');
        }
        
        if (empty($data['last_name'])) {
            return array('valid' => false, 'message' => 'El apellido es requerido');
        }
        
        // Email opcional - solo validar si se proporciona
        if (!empty($data['email']) && !is_email($data['email'])) {
            return array('valid' => false, 'message' => 'Formato de email inválido');
        }
        
        // Verificar email único solo si se proporciona
        if (!empty($data['email'])) {
            $email_exists = $this->db->get_var($this->db->prepare(
                "SELECT id FROM {$this->table_students} 
                 WHERE email = %s AND id != %d AND deleted_at IS NULL",
                $data['email'],
                $student_id
            ));
            
            if ($email_exists) {
                return array('valid' => false, 'message' => 'El email ya está registrado');
            }
        }
        
        // Validar email de emergencia si se proporciona
        if (!empty($data['emergency_email']) && !is_email($data['emergency_email'])) {
            return array('valid' => false, 'message' => 'Email de emergencia inválido');
        }
        
        if (!empty($data['date_of_birth']) && !$this->validate_date($data['date_of_birth'])) {
            return array('valid' => false, 'message' => 'Fecha de nacimiento inválida');
        }
        
        return array('valid' => true);
    }
    
    /**
     * Validar formato de fecha
     */
    private function validate_date($date) {
        $d = DateTime::createFromFormat('Y-m-d', $date);
        return $d && $d->format('Y-m-d') === $date;
    }
    
    /**
     * Obtener etiqueta de estado
     */
    private function get_status_label($status) {
        $labels = array(
            'active' => 'Activo',
            'inactive' => 'Inactivo',
            'graduated' => 'Graduado',
            'suspended' => 'Suspendido',    
            'retired' => 'Retirado'
        );
        
        return $labels[$status] ?? $status;
    }
    
    /**
     * Obtener etiqueta de relación de emergencia
     */
    private function get_relationship_label($relationship) {
        $labels = array(
            'parent' => 'Padre/Madre',
            'guardian' => 'Tutor',
            'spouse' => 'Cónyuge',
            'sibling' => 'Hermano/a',
            'friend' => 'Amigo/a',
            'other' => 'Otro'
        );
        
        return $labels[$relationship] ?? $relationship;
    }
}

// Inicializar el módulo
new WES_Students_Module();