<?php
/**
 * VALIDATORS - Sistema de Validación del Lado Servidor
 * 
 * Este archivo contiene todas las funciones de validación backend
 * para el sistema GGEE. NUNCA confíes solo en validaciones frontend,
 * siempre valida en el servidor.
 * 
 * EXPLICACIÓN PARA JUNIOR:
 * Las validaciones frontend pueden ser deshabilitadas por el usuario
 * (desactivando JavaScript). Las validaciones backend son la última
 * línea de defensa para asegurar datos correctos en la base de datos.
 * 
 * @author Sistema GGEE
 * @version 1.0
 */

// ============================================
// VALIDACIÓN DE RUT CHILENO
// ============================================

/**
 * Valida un RUT chileno completo (formato y dígito verificador)
 * 
 * @param string $rut RUT a validar
 * @return array ['valid' => bool, 'message' => string, 'sanitized' => string]
 */
function validateRUT($rut) {
    // Limpiar RUT
    $cleanRut = cleanRUT($rut);
    
    // Validar que no esté vacío
    if (empty($cleanRut)) {
        return validationResponse(false, 'RUT es requerido');
    }
    
    // Validar longitud (mínimo 8, máximo 9 caracteres)
    if (strlen($cleanRut) < 8 || strlen($cleanRut) > 9) {
        return validationResponse(false, 'RUT debe tener entre 8 y 9 dígitos');
    }
    
    // Separar número y dígito verificador
    $rutNumber = substr($cleanRut, 0, -1);
    $verifier = strtoupper(substr($cleanRut, -1));
    
    // Calcular dígito verificador esperado
    $expectedVerifier = calculateRUTVerifier($rutNumber);
    
    // Validar
    if ($verifier !== $expectedVerifier) {
        return validationResponse(false, 'RUT inválido (dígito verificador incorrecto)');
    }
    
    // Formatear
    $formatted = formatRUT($cleanRut);
    
    return validationResponse(true, 'RUT válido', $formatted);
}

/**
 * Calcula el dígito verificador de un RUT usando el algoritmo módulo 11
 * 
 * @param string $rutNumber Número del RUT sin el dígito verificador
 * @return string Dígito verificador calculado ('0'-'9' o 'K')
 */
function calculateRUTVerifier($rutNumber) {
    $sum = 0;
    $multiplier = 2;
    
    // Recorrer el RUT de derecha a izquierda
    for ($i = strlen($rutNumber) - 1; $i >= 0; $i--) {
        $sum += intval($rutNumber[$i]) * $multiplier;
        $multiplier = $multiplier === 7 ? 2 : $multiplier + 1;
    }
    
    $remainder = $sum % 11;
    $verifier = 11 - $remainder;
    
    if ($verifier === 11) return '0';
    if ($verifier === 10) return 'K';
    return strval($verifier);
}

/**
 * Formatea un RUT al formato estándar chileno: XX.XXX.XXX-X
 * 
 * @param string $rut RUT limpio (solo números y K)
 * @return string RUT formateado
 */
function formatRUT($rut) {
    $clean = cleanRUT($rut);
    if (empty($clean)) return '';
    
    $number = substr($clean, 0, -1);
    $verifier = substr($clean, -1);
    
    // Agregar puntos cada 3 dígitos
    $formatted = number_format(intval($number), 0, '', '.');
    
    return $formatted . '-' . $verifier;
}

/**
 * Limpia un RUT dejando solo números y K
 * 
 * @param string $rut RUT a limpiar
 * @return string RUT limpio
 */
function cleanRUT($rut) {
    if (empty($rut)) return '';
    return strtoupper(preg_replace('/[^0-9kK]/', '', $rut));
}

// ============================================
// VALIDACIÓN DE EMAIL
// ============================================

/**
 * Valida un email según el estándar RFC 5322
 * 
 * @param string $email Email a validar
 * @return array ['valid' => bool, 'message' => string, 'sanitized' => string]
 */
function validateEmail($email) {
    $trimmed = trim($email);
    
    // Validar que no esté vacío
    if (empty($trimmed)) {
        return validationResponse(false, 'Email es requerido');
    }
    
    // Validar formato usando filtro nativo de PHP
    if (!filter_var($trimmed, FILTER_VALIDATE_EMAIL)) {
        return validationResponse(false, 'Formato de email inválido');
    }
    
    // Validar longitud máxima (estándar RFC 5321)
    if (strlen($trimmed) > 254) {
        return validationResponse(false, 'Email demasiado largo (máximo 254 caracteres)');
    }
    
    // Sanitizar y normalizar
    $sanitized = filter_var($trimmed, FILTER_SANITIZE_EMAIL);
    $sanitized = strtolower($sanitized);
    
    return validationResponse(true, 'Email válido', $sanitized);
}

/**
 * Sanitiza un email (trim + lowercase + filter)
 * 
 * @param string $email Email a sanitizar
 * @return string Email sanitizado
 */
function sanitizeEmail($email) {
    if (empty($email)) return '';
    $trimmed = trim($email);
    $sanitized = filter_var($trimmed, FILTER_SANITIZE_EMAIL);
    return strtolower($sanitized);
}

// ============================================
// VALIDACIÓN DE TELÉFONO CHILENO
// ============================================

/**
 * Valida un teléfono chileno (móvil o fijo)
 * 
 * Formatos válidos:
 * - Móvil: 9 XXXX XXXX (9 dígitos empezando con 9)
 * - Fijo: 2 XXXX XXXX (9 dígitos empezando con 2)
 * 
 * @param string $phone Teléfono a validar
 * @return array ['valid' => bool, 'message' => string, 'sanitized' => string, 'type' => string]
 */
function validatePhone($phone) {
    // Limpiar teléfono
    $cleanPhone = cleanPhone($phone);
    
    // Validar que no esté vacío
    if (empty($cleanPhone)) {
        return validationResponse(false, 'Teléfono es requerido');
    }
    
    // Validar longitud (debe ser 9 dígitos)
    if (strlen($cleanPhone) !== 9) {
        return validationResponse(false, 'Teléfono debe tener 9 dígitos');
    }
    
    // Validar primer dígito (9 para móvil, 2 para fijo)
    $firstDigit = $cleanPhone[0];
    if ($firstDigit !== '9' && $firstDigit !== '2') {
        return validationResponse(false, 'Teléfono debe comenzar con 9 (móvil) o 2 (fijo)');
    }
    
    // Teléfono válido
    $type = $firstDigit === '9' ? 'móvil' : 'fijo';
    $formatted = formatPhone($cleanPhone);
    
    return [
        'valid' => true,
        'message' => "Teléfono $type válido",
        'sanitized' => $formatted,
        'type' => $type
    ];
}

/**
 * Formatea un teléfono chileno al formato: +56 9 XXXX XXXX
 * 
 * @param string $phone Teléfono limpio (9 dígitos)
 * @return string Teléfono formateado
 */
function formatPhone($phone) {
    $clean = cleanPhone($phone);
    if (strlen($clean) !== 9) return $phone;
    
    // Formato: +56 9 XXXX XXXX
    return '+56 ' . $clean[0] . ' ' . substr($clean, 1, 4) . ' ' . substr($clean, 5);
}

/**
 * Limpia un teléfono dejando solo números
 * 
 * @param string $phone Teléfono a limpiar
 * @return string Teléfono limpio
 */
function cleanPhone($phone) {
    if (empty($phone)) return '';
    // Quitar +56 si existe, luego quitar todo excepto números
    $withoutPrefix = preg_replace('/^\+?56\s*/', '', $phone);
    return preg_replace('/[^0-9]/', '', $withoutPrefix);
}

// ============================================
// HELPERS GENERALES
// ============================================

/**
 * Crea una respuesta estándar de validación
 * 
 * @param bool $isValid Si la validación fue exitosa
 * @param string $message Mensaje descriptivo
 * @param mixed $sanitized Valor sanitizado/formateado (opcional)
 * @return array Respuesta estándar
 */
function validationResponse($isValid, $message = '', $sanitized = null) {
    $response = [
        'valid' => $isValid,
        'message' => $message
    ];
    
    if ($sanitized !== null) {
        $response['sanitized'] = $sanitized;
    }
    
    return $response;
}

/**
 * Valida múltiples campos a la vez
 * 
 * @param array $data Datos a validar ['campo' => 'valor']
 * @param array $rules Reglas de validación ['campo' => 'tipo']
 * @return array ['valid' => bool, 'errors' => array, 'sanitized' => array]
 * 
 * EJEMPLO DE USO:
 * $result = validateMultiple(
 *     ['rut' => '12.345.678-5', 'email' => 'test@example.com'],
 *     ['rut' => 'rut', 'email' => 'email']
 * );
 */
function validateMultiple($data, $rules) {
    $errors = [];
    $sanitized = [];
    $allValid = true;
    
    foreach ($rules as $field => $type) {
        $value = $data[$field] ?? '';
        $result = null;
        
        switch ($type) {
            case 'rut':
                $result = validateRUT($value);
                break;
            case 'email':
                $result = validateEmail($value);
                break;
            case 'phone':
            case 'telefono':
                $result = validatePhone($value);
                break;
            default:
                continue 2; // Skip unknown types
        }
        
        if (!$result['valid']) {
            $errors[$field] = $result['message'];
            $allValid = false;
        } else {
            $sanitized[$field] = $result['sanitized'] ?? $value;
        }
    }
    
    return [
        'valid' => $allValid,
        'errors' => $errors,
        'sanitized' => $sanitized
    ];
}

/**
 * Sanitiza una cadena de texto general
 * 
 * @param string $text Texto a sanitizar
 * @param int $maxLength Longitud máxima (opcional)
 * @return string Texto sanitizado
 */
function sanitizeText($text, $maxLength = null) {
    if (empty($text)) return '';
    
    // Trim y quitar espacios múltiples
    $sanitized = trim($text);
    $sanitized = preg_replace('/\s+/', ' ', $sanitized);
    
    // Escapar HTML
    $sanitized = htmlspecialchars($sanitized, ENT_QUOTES, 'UTF-8');
    
    // Limitar longitud si se especifica
    if ($maxLength !== null && strlen($sanitized) > $maxLength) {
        $sanitized = substr($sanitized, 0, $maxLength);
    }
    
    return $sanitized;
}

/**
 * Valida que un campo no esté vacío
 * 
 * @param mixed $value Valor a validar
 * @param string $fieldName Nombre del campo (para mensaje de error)
 * @return array Respuesta de validación
 */
function validateRequired($value, $fieldName = 'Campo') {
    $trimmed = is_string($value) ? trim($value) : $value;
    
    if (empty($trimmed)) {
        return validationResponse(false, "$fieldName es requerido");
    }
    
    return validationResponse(true, '', $trimmed);
}

/**
 * Valida longitud de una cadena
 * 
 * @param string $value Valor a validar
 * @param int $min Longitud mínima
 * @param int $max Longitud máxima
 * @param string $fieldName Nombre del campo
 * @return array Respuesta de validación
 */
function validateLength($value, $min, $max, $fieldName = 'Campo') {
    $length = strlen($value);
    
    if ($length < $min) {
        return validationResponse(false, "$fieldName debe tener al menos $min caracteres");
    }
    
    if ($length > $max) {
        return validationResponse(false, "$fieldName debe tener máximo $max caracteres");
    }
    
    return validationResponse(true, '', $value);
}

// ============================================
// LOGGING DE VALIDACIONES (OPCIONAL)
// ============================================

/**
 * Registra un intento de validación fallido
 * 
 * @param string $type Tipo de validación (rut, email, phone)
 * @param string $value Valor que falló
 * @param string $message Mensaje de error
 * @param string $ip IP del usuario (opcional)
 */
function logValidationFailure($type, $value, $message, $ip = null) {
    // Solo registrar en desarrollo o si hay muchos intentos fallidos
    // Esto ayuda a detectar intentos de ataque o problemas en el sistema
    
    $logFile = __DIR__ . '/../logs/validation_failures.log';
    $logDir = dirname($logFile);
    
    // Crear directorio de logs si no existe
    if (!is_dir($logDir)) {
        @mkdir($logDir, 0755, true);
    }
    
    $ip = $ip ?? ($_SERVER['REMOTE_ADDR'] ?? 'unknown');
    $timestamp = date('Y-m-d H:i:s');
    $sanitizedValue = substr($value, 0, 50); // Limitar longitud para el log
    
    $logEntry = "[$timestamp] [$ip] [$type] Failed: $sanitizedValue - $message\n";
    
    // Escribir al log (silenciosamente, sin interrumpir la aplicación)
    @file_put_contents($logFile, $logEntry, FILE_APPEND);
}
