class-wes-config-module.php

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

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

class WES_Config_Module {
    
    private $db;
    private $table_branches;
    private $table_languages;
    private $table_branch_languages;
    private $table_programs;
    private $table_levels;
    
    public function __construct() {
        global $wpdb;
        $this->db = $wpdb;
        $this->table_branches = $wpdb->prefix . 'wes_branches';
        $this->table_languages = $wpdb->prefix . 'wes_languages';
        $this->table_branch_languages = $wpdb->prefix . 'wes_branch_languages';
        $this->table_programs = $wpdb->prefix . 'wes_programs';
        $this->table_levels = $wpdb->prefix . 'wes_levels';
        
        $this->init_hooks();
    }
    
    /**
     * Inicializar hooks y acciones
     */
    private function init_hooks() {
        // AJAX handlers
        add_action('wp_ajax_wes_get_branches', array($this, 'ajax_get_branches'));
        add_action('wp_ajax_wes_save_branch', array($this, 'ajax_save_branch'));
        add_action('wp_ajax_wes_delete_branch', array($this, 'ajax_delete_branch'));
        
        add_action('wp_ajax_wes_get_languages', array($this, 'ajax_get_languages'));
        add_action('wp_ajax_wes_save_language', array($this, 'ajax_save_language'));
        add_action('wp_ajax_wes_delete_language', array($this, 'ajax_delete_language'));
        
        add_action('wp_ajax_wes_get_programs', array($this, 'ajax_get_programs'));
        add_action('wp_ajax_wes_save_program', array($this, 'ajax_save_program'));
        add_action('wp_ajax_wes_delete_program', array($this, 'ajax_delete_program'));
        
        add_action('wp_ajax_wes_get_levels', array($this, 'ajax_get_levels'));
        add_action('wp_ajax_wes_save_level', array($this, 'ajax_save_level'));
        add_action('wp_ajax_wes_delete_level', array($this, 'ajax_delete_level'));
        
        add_action('wp_ajax_wes_get_countries', array($this, 'ajax_get_countries'));
        add_action('wp_ajax_wes_get_branch_languages', array($this, 'ajax_get_branch_languages'));
        add_action('wp_ajax_wes_toggle_branch_language', array($this, 'ajax_toggle_branch_language'));
        
        // AJAX para frontend (no-priv)
        add_action('wp_ajax_nopriv_wes_get_branches', array($this, 'ajax_get_branches'));
        add_action('wp_ajax_nopriv_wes_save_branch', array($this, 'ajax_save_branch'));
        add_action('wp_ajax_nopriv_wes_delete_branch', array($this, 'ajax_delete_branch'));
        
        add_action('wp_ajax_nopriv_wes_get_languages', array($this, 'ajax_get_languages'));
        add_action('wp_ajax_nopriv_wes_save_language', array($this, 'ajax_save_language'));
        add_action('wp_ajax_nopriv_wes_delete_language', array($this, 'ajax_delete_language'));
        
        add_action('wp_ajax_nopriv_wes_get_programs', array($this, 'ajax_get_programs'));
        add_action('wp_ajax_nopriv_wes_save_program', array($this, 'ajax_save_program'));
        add_action('wp_ajax_nopriv_wes_delete_program', array($this, 'ajax_delete_program'));
        
        add_action('wp_ajax_nopriv_wes_get_levels', array($this, 'ajax_get_levels'));
        add_action('wp_ajax_nopriv_wes_save_level', array($this, 'ajax_save_level'));
        add_action('wp_ajax_nopriv_wes_delete_level', array($this, 'ajax_delete_level'));
        
        add_action('wp_ajax_nopriv_wes_get_countries', array($this, 'ajax_get_countries'));
        add_action('wp_ajax_nopriv_wes_get_branch_languages', array($this, 'ajax_get_branch_languages'));
        add_action('wp_ajax_nopriv_wes_toggle_branch_language', array($this, 'ajax_toggle_branch_language'));
    }
    
    // ==========================================
    // AJAX HANDLERS - PAÍSES
    // ==========================================
    
    public function ajax_get_countries() {
        check_ajax_referer('wes_nonce', 'nonce');
        
        // Obtener países únicos de la tabla de estudiantes
        $countries = $this->db->get_results(
            "SELECT DISTINCT country_of_origin as country 
             FROM {$this->db->prefix}wes_students 
             WHERE country_of_origin IS NOT NULL 
             AND country_of_origin != '' 
             AND deleted_at IS NULL
             ORDER BY country_of_origin"
        );
        
        $country_list = array();
        foreach ($countries as $country) {
            if (!empty(trim($country->country))) {
                $country_list[] = trim($country->country);
            }
        }
        
        // Agregar algunos países adicionales comunes si no están
        $additional_countries = array(
            'Guatemala', 'México', 'Estados Unidos', 'Honduras', 'El Salvador',
            'Nicaragua', 'Costa Rica', 'Panamá', 'Colombia', 'España'
        );
        
        foreach ($additional_countries as $add_country) {
            if (!in_array($add_country, $country_list)) {
                $country_list[] = $add_country;
            }
        }
        
        sort($country_list);
        wp_send_json_success($country_list);
    }
    
    // ==========================================
    // AJAX HANDLERS - SEDES
    // ==========================================
    
    public function ajax_get_branches() {
        check_ajax_referer('wes_nonce', 'nonce');
        
        $branches = $this->db->get_results(
            "SELECT * FROM {$this->table_branches} 
             WHERE deleted_at IS NULL 
             ORDER BY name"
        );
        
        wp_send_json_success($branches);
    }
    
    public function ajax_save_branch() {
    check_ajax_referer('wes_nonce', 'nonce');
    
    $branch_id = intval($_POST['branch_id'] ?? 0);
    $data = array(
        'name' => sanitize_text_field($_POST['name'] ?? ''),
        'code' => sanitize_text_field($_POST['code'] ?? ''),
        'address' => sanitize_textarea_field($_POST['address'] ?? ''),
        'city' => sanitize_text_field($_POST['city'] ?? ''),
        'country' => sanitize_text_field($_POST['country'] ?? ''),
        'manager_name' => sanitize_text_field($_POST['manager_name'] ?? ''),
        'phone' => sanitize_text_field($_POST['phone'] ?? ''),
        'email' => sanitize_email($_POST['email'] ?? ''),
        'status' => sanitize_text_field($_POST['status'] ?? 'active')
    );
    
    // Validar datos requeridos
    if (empty($data['name']) || empty($data['code'])) {
        wp_send_json_error('Nombre y código son requeridos');
    }
    
    // Verificar código único - MEJORADO para detectar sedes eliminadas
    $existing = $this->db->get_row($this->db->prepare(
        "SELECT id, deleted_at FROM {$this->table_branches} 
         WHERE code = %s AND id != %d",
        $data['code'], $branch_id
    ));
    
    if ($existing) {
        if ($existing->deleted_at !== null) {
            // Sede existe pero está eliminada (soft delete)
            wp_send_json_error(array(
                'type' => 'deleted_conflict',
                'message' => 'Este código pertenece a una sede previamente eliminada. Para usar este código nuevamente, contacte al administrador de TI para eliminar definitivamente la sede anterior, o use un código diferente.'
            ));
        } else {
            // Sede existe y está activa
            wp_send_json_error(array(
                'type' => 'active_conflict', 
                'message' => 'El código ya existe en otra sede activa'
            ));
        }
    }
    
    if ($branch_id > 0) {
        // Actualizar
        $data['updated_at'] = current_time('mysql');
        $result = $this->db->update($this->table_branches, $data, array('id' => $branch_id));
        $message = 'Sede actualizada correctamente';
    } else {
        // Crear
        $data['created_at'] = current_time('mysql');
        $data['updated_at'] = current_time('mysql');
        $result = $this->db->insert($this->table_branches, $data);
        $message = 'Sede creada correctamente';
    }
    
    if ($result !== false) {
        wp_send_json_success($message);
    } else {
        wp_send_json_error('Error al guardar la sede');
    }
}
    
    public function ajax_delete_branch() {
        check_ajax_referer('wes_nonce', 'nonce');
        
        $branch_id = intval($_POST['branch_id'] ?? 0);
        
        if ($branch_id <= 0) {
            wp_send_json_error('ID inválido');
        }
        
        // Verificar si tiene cursos activos
        $courses_count = $this->db->get_var($this->db->prepare(
            "SELECT COUNT(*) FROM {$this->db->prefix}wes_courses 
             WHERE branch_id = %d AND deleted_at IS NULL",
            $branch_id
        ));
        
        if ($courses_count > 0) {
            wp_send_json_error('No se puede eliminar: tiene cursos asociados');
        }
        
        // Soft delete
        $result = $this->db->update(
            $this->table_branches,
            array('deleted_at' => current_time('mysql')),
            array('id' => $branch_id)
        );
        
        if ($result !== false) {
            wp_send_json_success('Sede eliminada correctamente');
        } else {
            wp_send_json_error('Error al eliminar la sede');
        }
    }
    
    // ==========================================
    // AJAX HANDLERS - IDIOMAS
    // ==========================================
    
    public function ajax_get_languages() {
        check_ajax_referer('wes_nonce', 'nonce');
        
        $languages = $this->db->get_results(
            "SELECT * FROM {$this->table_languages} 
             WHERE deleted_at IS NULL 
             ORDER BY name"
        );
        
        wp_send_json_success($languages);
    }
    
    public function ajax_save_language() {
    check_ajax_referer('wes_nonce', 'nonce');
    
    $language_id = intval($_POST['language_id'] ?? 0);
    $data = array(
        'name' => sanitize_text_field($_POST['name'] ?? ''),
        'code' => strtoupper(sanitize_text_field($_POST['code'] ?? '')),
        'status' => sanitize_text_field($_POST['status'] ?? 'active')
    );
    
    if (empty($data['name']) || empty($data['code'])) {
        wp_send_json_error('Nombre y código son requeridos');
    }
    
    // Verificar código único - MEJORADO
    $existing = $this->db->get_row($this->db->prepare(
        "SELECT id, deleted_at FROM {$this->table_languages} 
         WHERE code = %s AND id != %d",
        $data['code'], $language_id
    ));
    
    if ($existing) {
        if ($existing->deleted_at !== null) {
            wp_send_json_error(array(
                'type' => 'deleted_conflict',
                'message' => 'Este código pertenece a un idioma previamente eliminado. Para usar este código nuevamente, contacte al administrador de TI para eliminar definitivamente el idioma anterior, o use un código diferente.'
            ));
        } else {
            wp_send_json_error(array(
                'type' => 'active_conflict',
                'message' => 'El código ya existe en otro idioma activo'
            ));
        }
    }
    
    if ($language_id > 0) {
        $data['updated_at'] = current_time('mysql');
        $result = $this->db->update($this->table_languages, $data, array('id' => $language_id));
        $message = 'Idioma actualizado correctamente';
    } else {
        $data['created_at'] = current_time('mysql');
        $data['updated_at'] = current_time('mysql');
        $result = $this->db->insert($this->table_languages, $data);
        $message = 'Idioma creado correctamente';
    }
    
    if ($result !== false) {
        wp_send_json_success($message);
    } else {
        wp_send_json_error('Error al guardar el idioma');
    }
}
    
    public function ajax_delete_language() {
        check_ajax_referer('wes_nonce', 'nonce');
        
        $language_id = intval($_POST['language_id'] ?? 0);
        
        // Verificar si tiene programas
        $programs_count = $this->db->get_var($this->db->prepare(
            "SELECT COUNT(*) FROM {$this->table_programs} 
             WHERE language_id = %d AND deleted_at IS NULL",
            $language_id
        ));
        
        if ($programs_count > 0) {
            wp_send_json_error('No se puede eliminar: tiene programas asociados');
        }
        
        $result = $this->db->update(
            $this->table_languages,
            array('deleted_at' => current_time('mysql')),
            array('id' => $language_id)
        );
        
        if ($result !== false) {
            wp_send_json_success('Idioma eliminado correctamente');
        } else {
            wp_send_json_error('Error al eliminar el idioma');
        }
    }
    
    // ==========================================
    // AJAX HANDLERS - PROGRAMAS
    // ==========================================
    
    public function ajax_get_programs() {
        check_ajax_referer('wes_nonce', 'nonce');
        
        $language_id = intval($_POST['language_id'] ?? 0);
        
        $query = "SELECT p.*, l.name as language_name 
                  FROM {$this->table_programs} p
                  LEFT JOIN {$this->table_languages} l ON p.language_id = l.id
                  WHERE p.deleted_at IS NULL";
        
        if ($language_id > 0) {
            $query .= $this->db->prepare(" AND p.language_id = %d", $language_id);
        }
        
        $query .= " ORDER BY l.name, p.name";
        
        $programs = $this->db->get_results($query);
        
        wp_send_json_success($programs);
    }
    
    public function ajax_save_program() {
    check_ajax_referer('wes_nonce', 'nonce');
    
    $program_id = intval($_POST['program_id'] ?? 0);
    $data = array(
        'language_id' => intval($_POST['language_id'] ?? 0),
        'name' => sanitize_text_field($_POST['name'] ?? ''),
        'code' => strtoupper(sanitize_text_field($_POST['code'] ?? '')),
        'description' => sanitize_textarea_field($_POST['description'] ?? ''),
        'status' => sanitize_text_field($_POST['status'] ?? 'active')
    );
    
    if (empty($data['name']) || empty($data['code']) || $data['language_id'] <= 0) {
        wp_send_json_error('Idioma, nombre y código son requeridos');
    }
    
    // Verificar código único dentro del idioma - MEJORADO
    $existing = $this->db->get_row($this->db->prepare(
        "SELECT id, deleted_at FROM {$this->table_programs} 
         WHERE language_id = %d AND code = %s AND id != %d",
        $data['language_id'], $data['code'], $program_id
    ));
    
    if ($existing) {
        if ($existing->deleted_at !== null) {
            wp_send_json_error(array(
                'type' => 'deleted_conflict',
                'message' => 'Este código pertenece a un programa previamente eliminado en este idioma. Para usar este código nuevamente, contacte al administrador de TI para eliminar definitivamente el programa anterior, o use un código diferente.'
            ));
        } else {
            wp_send_json_error(array(
                'type' => 'active_conflict',
                'message' => 'El código ya existe en otro programa activo de este idioma'
            ));
        }
    }
    
    if ($program_id > 0) {
        $data['updated_at'] = current_time('mysql');
        $result = $this->db->update($this->table_programs, $data, array('id' => $program_id));
        $message = 'Programa actualizado correctamente';
    } else {
        $data['created_at'] = current_time('mysql');
        $data['updated_at'] = current_time('mysql');
        $result = $this->db->insert($this->table_programs, $data);
        $message = 'Programa creado correctamente';
    }
    
    if ($result !== false) {
        wp_send_json_success($message);
    } else {
        wp_send_json_error('Error al guardar el programa');
    }
}
    
    public function ajax_delete_program() {
        check_ajax_referer('wes_nonce', 'nonce');
        
        $program_id = intval($_POST['program_id'] ?? 0);
        
        // Verificar si tiene niveles
        $levels_count = $this->db->get_var($this->db->prepare(
            "SELECT COUNT(*) FROM {$this->table_levels} 
             WHERE program_id = %d AND deleted_at IS NULL",
            $program_id
        ));
        
        if ($levels_count > 0) {
            wp_send_json_error('No se puede eliminar: tiene niveles asociados');
        }
        
        $result = $this->db->update(
            $this->table_programs,
            array('deleted_at' => current_time('mysql')),
            array('id' => $program_id)
        );
        
        if ($result !== false) {
            wp_send_json_success('Programa eliminado correctamente');
        } else {
            wp_send_json_error('Error al eliminar el programa');
        }
    }
    
    // ==========================================
    // AJAX HANDLERS - NIVELES
    // ==========================================
    
    public function ajax_get_levels() {
        check_ajax_referer('wes_nonce', 'nonce');
        
        $program_id = intval($_POST['program_id'] ?? 0);
        
        $query = "SELECT lv.*, p.name as program_name, l.name as language_name
                  FROM {$this->table_levels} lv
                  LEFT JOIN {$this->table_programs} p ON lv.program_id = p.id
                  LEFT JOIN {$this->table_languages} l ON p.language_id = l.id
                  WHERE lv.deleted_at IS NULL";
        
        if ($program_id > 0) {
            $query .= $this->db->prepare(" AND lv.program_id = %d", $program_id);
        }
        
        $query .= " ORDER BY l.name, p.name, lv.order_number";
        
        $levels = $this->db->get_results($query);
        
        wp_send_json_success($levels);
    }
    
    public function ajax_save_level() {
    check_ajax_referer('wes_nonce', 'nonce');
    
    $level_id = intval($_POST['level_id'] ?? 0);
    $data = array(
        'program_id' => intval($_POST['program_id'] ?? 0),
        'name' => sanitize_text_field($_POST['name'] ?? ''),
        'code' => strtoupper(sanitize_text_field($_POST['code'] ?? '')),
        'order_number' => intval($_POST['order_number'] ?? 1),
        'status' => sanitize_text_field($_POST['status'] ?? 'active')
    );
    
    if (empty($data['name']) || empty($data['code']) || $data['program_id'] <= 0) {
        wp_send_json_error('Programa, nombre y código son requeridos');
    }
    
    // Verificar código único dentro del programa - MEJORADO
    $existing = $this->db->get_row($this->db->prepare(
        "SELECT id, deleted_at FROM {$this->table_levels} 
         WHERE program_id = %d AND code = %s AND id != %d",
        $data['program_id'], $data['code'], $level_id
    ));
    
    if ($existing) {
        if ($existing->deleted_at !== null) {
            wp_send_json_error(array(
                'type' => 'deleted_conflict',
                'message' => 'Este código pertenece a un nivel previamente eliminado en este programa. Para usar este código nuevamente, contacte al administrador de TI para eliminar definitivamente el nivel anterior, o use un código diferente.'
            ));
        } else {
            wp_send_json_error(array(
                'type' => 'active_conflict',
                'message' => 'El código ya existe en otro nivel activo de este programa'
            ));
        }
    }
    
    if ($level_id > 0) {
        $data['updated_at'] = current_time('mysql');
        $result = $this->db->update($this->table_levels, $data, array('id' => $level_id));
        $message = 'Nivel actualizado correctamente';
    } else {
        $data['created_at'] = current_time('mysql');
        $data['updated_at'] = current_time('mysql');
        $result = $this->db->insert($this->table_levels, $data);
        $message = 'Nivel creado correctamente';
    }
    
    if ($result !== false) {
        wp_send_json_success($message);
    } else {
        wp_send_json_error('Error al guardar el nivel');
    }
}
    
    public function ajax_delete_level() {
        check_ajax_referer('wes_nonce', 'nonce');
        
        $level_id = intval($_POST['level_id'] ?? 0);
        
        // Verificar si tiene cursos
        $courses_count = $this->db->get_var($this->db->prepare(
            "SELECT COUNT(*) FROM {$this->db->prefix}wes_courses 
             WHERE level_id = %d AND deleted_at IS NULL",
            $level_id
        ));
        
        if ($courses_count > 0) {
            wp_send_json_error('No se puede eliminar: tiene cursos asociados');
        }
        
        $result = $this->db->update(
            $this->table_levels,
            array('deleted_at' => current_time('mysql')),
            array('id' => $level_id)
        );
        
        if ($result !== false) {
            wp_send_json_success('Nivel eliminado correctamente');
        } else {
            wp_send_json_error('Error al eliminar el nivel');
        }
    }
    
    // ==========================================
    // AJAX HANDLERS - ASIGNACIÓN SEDE-IDIOMAS
    // ==========================================
    
    public function ajax_get_branch_languages() {
        check_ajax_referer('wes_nonce', 'nonce');
        
        $branch_id = intval($_POST['branch_id'] ?? 0);
        
        if ($branch_id <= 0) {
            wp_send_json_error('ID de sede requerido');
        }
        
        $query = "SELECT l.id, l.name, l.code,
                         CASE WHEN bl.id IS NOT NULL THEN 1 ELSE 0 END as assigned
                  FROM {$this->table_languages} l
                  LEFT JOIN {$this->table_branch_languages} bl ON l.id = bl.language_id AND bl.branch_id = %d
                  WHERE l.deleted_at IS NULL
                  ORDER BY l.name";
        
        $languages = $this->db->get_results($this->db->prepare($query, $branch_id));
        
        wp_send_json_success($languages);
    }
    
    public function ajax_toggle_branch_language() {
        check_ajax_referer('wes_nonce', 'nonce');
        
        $branch_id = intval($_POST['branch_id'] ?? 0);
        $language_id = intval($_POST['language_id'] ?? 0);
        $assign = intval($_POST['assign'] ?? 0);
        
        if ($branch_id <= 0 || $language_id <= 0) {
            wp_send_json_error('IDs requeridos');
        }
        
        if ($assign) {
            // Asignar idioma a sede
            $result = $this->db->replace(
                $this->table_branch_languages,
                array(
                    'branch_id' => $branch_id,
                    'language_id' => $language_id,
                    'status' => 'active',
                    'created_at' => current_time('mysql'),
                    'updated_at' => current_time('mysql')
                )
            );
            $message = 'Idioma asignado a la sede';
        } else {
            // Desasignar idioma de sede
            $result = $this->db->delete(
                $this->table_branch_languages,
                array(
                    'branch_id' => $branch_id,
                    'language_id' => $language_id
                )
            );
            $message = 'Idioma desasignado de la sede';
        }
        
        if ($result !== false) {
            wp_send_json_success($message);
        } else {
            wp_send_json_error('Error al procesar la asignación');
        }
    }
}

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