HEX
Server: Apache/2.4.58 (Ubuntu)
System: Linux Bradford-Sitios 6.14.0-1017-azure #17~24.04.1-Ubuntu SMP Mon Dec 1 20:10:50 UTC 2025 x86_64
User: www-data (33)
PHP: 7.4.33
Disabled: pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_get_handler,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,pcntl_async_signals,pcntl_unshare,
Upload Files
File: /var/www/api_matriculas/app/Helpers/functions_helper.php
<?php

use App\Repositories\ConfigurationRepository;
use App\Models\Configuration;
use Jenssegers\Agent\Agent;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Request;

function genericResponse($data, $code)
{
	// Forzar conversión a entero para evitar errores de tipo
	$code = (int) $code;

	if ($code < 100 || $code > 599) {
		$code = 500;
	}

	$data['hash'] = getToken();
	// if ($code == 500) {
	//     $message = 'Error interno del servidor';
	//     $message = env('APP_ENV') == 'development' ? $message :  'Error interno del servidor';
	// }
	return response()->json($data, $code, [], JSON_INVALID_UTF8_SUBSTITUTE);
}

function getNoImage()
{
	return getImagePath() . 'no-image.png';
}

function logoEmail()
{
	return 'https://static.wixstatic.com/media/c6f5a1_897b296403f641159cfed8188b7f6537~mv2.png/v1/crop/x_190,y_556,w_1655,h_909/fill/w_350,h_184,al_c,q_85,usm_0.66_1.00_0.01,enc_avif,quality_auto/IMG_4168_PNG.png';
	return env('APP_URL') . getImagePath() . 'LogoBDF.png';
}

function getTemplate($template)
{
	$public_folder = '';
	if (env('TYPE_MODE') == 'PRODUCTION' || env('TYPE_MODE') == 'TEST') {
		$public_folder = '/';
	}

	return env('APP_URL') . $public_folder . '/excel/' . $template;
}

function getImagePath()
{
	$public_folder = '';
	if (env('TYPE_MODE') == 'PRODUCTION' || env('TYPE_MODE') == 'TEST') {
		$public_folder = '';
	}

	return $public_folder . 'assets/images/';
}

function validateRut($rut)
{
	$r = strtoupper(preg_replace('/[^Kk0-9]/i', '', $rut));
	// if ($r == '111111111' || $r == '222222222' || $r == '333333333' || $r == '444444444' || $r == '555555555' || $r == '666666666' || $r == '777777777' || $r == '888888888' || $r == '999999999') {
	// 	return false;
	// }
	if (strlen($r) < 7) {
		return false;
	}
	$sub_rut = substr($r, 0, strlen($r) - 1);
	$sub_dv = substr($r, -1);
	$x = 2;
	$s = 0;
	for ($i = strlen($sub_rut) - 1; $i >= 0; $i--) {
		if ($x > 7) {
			$x = 2;
		}
		$s += $sub_rut[$i] * $x;
		$x++;
	}
	$dv = 11 - ($s % 11);
	if ($dv == 10) {
		$dv = 'K';
	}
	if ($dv == 11) {
		$dv = '0';
	}
	if ($dv == $sub_dv) {
		return true;
	} else {
		return false;
	}
}

function strUpper($str)
{
	$str = Str::upper(trim($str));
	$str = str_replace('á', 'Á', $str);
	$str = str_replace('é', 'É', $str);
	$str = str_replace('í', 'Í', $str);
	$str = str_replace('ó', 'Ó', $str);
	$str = str_replace('ú', 'Ú', $str);
	$str = str_replace('ñ', 'Ñ', $str);
	return $str;
}

function strUpperSinTildes($str)
{
	$str = Str::upper(trim($str));
	$str = str_replace('á', 'A', $str);
	$str = str_replace('Á', 'A', $str);
	$str = str_replace('é', 'E', $str);
	$str = str_replace('É', 'E', $str);
	$str = str_replace('í', 'I', $str);
	$str = str_replace('Í', 'I', $str);
	$str = str_replace('ó', 'O', $str);
	$str = str_replace('Ó', 'O', $str);
	$str = str_replace('ú', 'U', $str);
	$str = str_replace('Ú', 'U', $str);
	$str = str_replace('ñ', 'Ñ', $str);
	return $str;
}

function strLower($str)
{
	$str = Str::lower(trim($str));
	$str = str_replace('Á', 'á', $str);
	$str = str_replace('É', 'é', $str);
	$str = str_replace('Í', 'í', $str);
	$str = str_replace('Ó', 'ó', $str);
	$str = str_replace('Ú', 'ú', $str);
	$str = str_replace('Ñ', 'ñ', $str);
	return $str;
}

function strCapital($str)
{
	$str = Str::lower(trim($str));
	$str = str_replace('Á', 'á', $str);
	$str = str_replace('É', 'é', $str);
	$str = str_replace('Í', 'í', $str);
	$str = str_replace('Ó', 'ó', $str);
	$str = str_replace('Ú', 'ú', $str);
	$str = str_replace('Ñ', 'ñ', $str);
	return ucwords($str);
}

function formatterRut($rut, $dots = false)
{
	$rutLimpio = str_replace('.', '', $rut);
	$rutLimpio = trim(str_replace('-', '', $rutLimpio));
	$dvRut = substr($rutLimpio, -1);
	$rutLimpio = substr($rutLimpio, 0, -1);
	$rutLimpio = clearMoney($rutLimpio);
	if ($dots) {
		$rutFormateado = format_number($rutLimpio) . '-' . $dvRut;
	} else {
		$rutFormateado = ($rutLimpio) . '-' . $dvRut;
	}
	return $rutFormateado;
}

function str_limit($value, $limit = '', $end = '')
{
	if (empty($limit)) {
		$limit = 100;
	}
	if (mb_strwidth($value, 'UTF-8') <= $limit) {
		return $value;
	}
	return rtrim(mb_strimwidth($value, 0, $limit, '', 'UTF-8')) . $end;
}

function getFormatMoney($amount, $currencyType)
{
	// Asegurar tipo numérico
	$amount = (float)$amount;

	switch (strtoupper($currencyType)) {
		case 'CLP':
		case '$':
			// Formato chileno sin decimales y con separador de miles
			return $amount > 0
				? '$' . number_format($amount, 0, ',', '.')
				: '$0';

		case 'UF':
			// Mostrar hasta 3 decimales, eliminando ceros innecesarios
			$formatted = number_format($amount, 3, '.', '');
			[$integer, $decimal] = explode('.', $formatted);
			$decimal = rtrim($decimal, '0');
			$formattedUF = $decimal
				? "{$integer},{$decimal} UF"
				: "{$integer} UF";
			return $formattedUF;

		default:
			// Genérico para otras monedas (ej: USD, EUR)
			return number_format($amount, 2, ',', '.') . " {$currencyType}";
	}
}

function format_money($numero)
{
	if (!empty($numero)) {
		$pesos = '$' . number_format($numero, 0, ',', '.');
	} else {
		$pesos = "No aplica";
	}
	return $pesos;
}

function format_number($numero)
{
	if (!empty($numero)) {
		$pesos = '' . number_format($numero, 0, ',', '.');
	} else {
		$pesos = "No aplica";
	}
	return $pesos;
}

function isUuid(string $value): bool
{
	$pattern = '/^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-5][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$/';
	return preg_match($pattern, $value) === 1;
}

function generateSecurePassword($length = 15)
{
	// Definir los caracteres permitidos
	$upperCase = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
	$lowerCase = 'abcdefghijklmnopqrstuvwxyz';
	$numbers = '0123456789';
	$specialCharacters = '!@#$%^&*()_+-=[]{}|;:,.<>?';

	// Combinar todos los caracteres en un solo string
	$allCharacters = $upperCase . $lowerCase . $numbers . $specialCharacters;

	// Asegurarse de que la contraseña contenga al menos un carácter de cada tipo
	$password = '';
	$password .= $upperCase[random_int(0, strlen($upperCase) - 1)];
	$password .= $lowerCase[random_int(0, strlen($lowerCase) - 1)];
	$password .= $numbers[random_int(0, strlen($numbers) - 1)];
	$password .= $specialCharacters[random_int(0, strlen($specialCharacters) - 1)];

	// Llenar el resto de la contraseña con caracteres aleatorios
	for ($i = 4; $i < $length; $i++) {
		$password .= $allCharacters[random_int(0, strlen($allCharacters) - 1)];
	}

	// Mezclar la contraseña para evitar un patrón predecible
	return str_shuffle($password);
}

function getToken()
{
	return sha1(strtotime(now()));
}


function sortDateHourForHuman($date)
{
	$explode = explode(" ", $date);
	$fecha[] = implode('-', array_reverse(explode('-', $explode[0])));
	$tiempo  = explode(":", $explode[1]);
	$fecha[] = $tiempo[0] . ':' . $tiempo[1];
	return implode(' ', $fecha);
}

function limpiarStr($str)
{
	$str = trim($str);
	$str = str_replace('á', 'a', $str);
	$str = str_replace('é', 'e', $str);
	$str = str_replace('í', 'i', $str);
	$str = str_replace('ó', 'o', $str);
	$str = str_replace('ú', 'u', $str);
	$str = str_replace('año', 'annio', $str);
	$str = str_replace('ñ', 'n', $str);
	return $str;
}

function getSlug($str, $lower = true, $separator = '-')
{
	$str = trim($str);
	if ($lower) {
		$str = strLower($str);
	}
	$str = str_replace('"', '', $str);
	$str = limpiarStr($str);
	$str = preg_replace('([^A-Za-z0-9])', $separator, $str);
	return $str;
}

function ordenar_fechaHoraServidor($date = '')
{
	if (empty($date)) {
		$date = date('Y-m-d');
	}
	$date = new DateTime($date);
	$fechaFormat = $date->format('Y-m-d H:i:s');
	return $fechaFormat;
}

function sort_dateServer($date)
{
	$date = new DateTime($date);
	$fechaFormat = $date->format('Y-m-d H:i:s');
	return $fechaFormat;
}

function sort_datetimeHuman($date)
{
	$date = new DateTime($date);
	$fechaFormat = $date->format('d-m-Y H:i:s');
	return $fechaFormat;
}

function ordenar_fechaHumano($date)
{
	$explode = explode(" ", $date);
	$fecha = implode('-', array_reverse(explode('-', $explode[0])));
	return $fecha;
}
function ordenarFechaHumanoSlash($date)
{
	$explode = explode(" ", $date);
	$fecha = implode('/', array_reverse(explode('-', $explode[0])));
	return $fecha;
}
function ordenar_fechaHoraHumano($date)
{
	$explode = explode(" ", $date);
	$fecha[] = implode('-', array_reverse(explode('-', $explode[0])));
	$tiempo  = explode(":", $explode[1]);
	$fecha[] = $tiempo[0] . ':' . $tiempo[1];
	return implode(' ', $fecha);
}

function ordenar_fechaHoraMinutoHumano($date)
{
	$explode = explode(" ", $date);
	$fecha[] = implode('-', array_reverse(explode('-', $explode[0])));
	$fecha[] = $explode[1];
	return implode(' ', $fecha);
}

function transformDateTextMonth($fecha = '', $shortMonth = false, $separator = ' ')
{
	if (empty($fecha)) {
		$fecha = date('Y-m-d');
	}

	$day = date("d", strtotime($fecha));
	$month = date("m", strtotime($fecha));
	if ($shortMonth) {
		$month = getMonthShort($month);
	} else {
		$month = getMonth($month);
	}
	$year = date("Y", strtotime($fecha));
	$date_formatted = $day . $separator . ($month) . $separator . $year;
	return trim($date_formatted);
}

function getMonthShort($mes)
{
	$return = '';
	$meses = array(
		1 => "Ene",
		2 => "Feb",
		3 => "Mar",
		4 => "Abr",
		5 => "May",
		6 => "Jun",
		7 => "Jul",
		8 => "Ago",
		9 => "Sep",
		10 => "Oct",
		11 => "Nov",
		12 => "Dic"
	);
	foreach ($meses as $key => $value) {
		if ($key == $mes) {
			$return = $value;
		}
	}
	return $return;
}

function getMonth($mes)
{
	$return = '';
	$meses = array(
		1 => "Enero",
		2 => "Febrero",
		3 => "Marzo",
		4 => "Abril",
		5 => "Mayo",
		6 => "Junio",
		7 => "Julio",
		8 => "Agosto",
		9 => "Septiembre",
		10 => "Octubre",
		11 => "Noviembre",
		12 => "Diciembre"
	);
	foreach ($meses as $key => $value) {
		if ($key == $mes) {
			$return = $value;
		}
	}
	return $return;
}



function getUserConnection()
{
	$agent = new Agent();

	return (object)[
		'ip' => Request::ip(),
		'login_at' => now()->format('Y-m-d H:i:s'),
		'navegador' => $agent->browser() . ' Version: ' . $agent->version($agent->browser()),
		'dispositivo' => $agent->isMobile() ? 'Dispositivo Móvil' : 'Escritorio',
	];
}

function clearMoney($str)
{
	$str = trim($str);
	$str = str_replace('$', '', $str);
	$str = str_replace('.', '', $str);
	$str = preg_replace('([^0-9])', '', $str);
	return $str;
}


/**
 * Calcula el precio neto (sin IVA) desde un precio con IVA, redondeado a entero.
 *
 * @param float|int $price Precio con IVA
 * @param float $tax Porcentaje de IVA (por defecto 19%)
 * @return int Precio sin IVA
 */
function priceWithoutTax($price, $tax = 19)
{
	return round($price / (1 + $tax / 100), 0);
}


function getStatusOrders()
{
	return [
		'auto-draft' => 'Borrador',
		'cancelled'  => 'Cancelado',
		'completed'  => 'Completado',
		'failed'     => 'Fallido',
		'processing' => 'En Proceso',
		'refunded'   => 'Reembolsado',
		'pendient'   => 'En Curso'
	];
}

function cleanDescription($text)
{
	// Reemplazar \r\n, \n, \r, tabulaciones y espacios no rompibles por espacios normales
	$text = str_replace(["\r\n", "\r", "\n", "\t", "\u{A0}"], ' ', $text);

	// Eliminar múltiples espacios seguidos
	$text = preg_replace('/\s+/', ' ', $text);

	// Recortar espacios al inicio y final
	return trim($text);
}

function getMonthText($month)
{
	$return = '';
	$months = array(
		1 => "Enero",
		2 => "Febrero",
		3 => "Marzo",
		4 => "Abril",
		5 => "Mayo",
		6 => "Junio",
		7 => "Julio",
		8 => "Agosto",
		9 => "Septiembre",
		10 => "Octubre",
		11 => "Noviembre",
		12 => "Diciembre"
	);

	if ($months[$month]) {
		$return = $months[$month];
	}
	return $return;
}

function formattedStudents($students = [])
{
	if (empty($students)) return '';

	$html = '<table style="width:100%; font-family: Arial, sans-serif; font-size:9pt; border-collapse:collapse; line-height:1.4; margin-top:-20px;margin-bottom:-5px">';

	foreach ($students as $student) {
		$full_name = trim("{$student->first_name} {$student->second_name} {$student->last_name} {$student->second_last_name}");
		$rut = formatterRut($student->rut, true);
		$course = $student->course_id > 0 ? $student->course->course : "Sin información";

		$html .= '
		<tr>
			<td style="width:50%; white-space:nowrap;">
				<span>Nombre alumno:&nbsp;</span>
				<span style="display:inline-block; width:65%; border-bottom:1px solid #000;margin-top:2px">
					<strong style="padding:0px; margin:0px;font-size:8.5pt;">' . htmlspecialchars($full_name) . '</strong>
				</span>
			</td>
			<td style="width:25%; white-space:nowrap;">
				<span>RUT N°:&nbsp;</span>
				<span style="display:inline-block; width:65%; border-bottom:1px solid #000;margin-top:2px">
					<strong style="padding:0px; margin:0px;font-size:8.5pt;">' . htmlspecialchars($rut) . '</strong>
				</span>
			</td>
			<td style="width:25%; white-space:nowrap;">
				<span>Curso:&nbsp;</span>
				<span style="display:inline-block; width:70%; border-bottom:1px solid #000;margin-top:2px">
					<strong style="padding:0px; margin:0px;font-size:8.5pt;">' . htmlspecialchars(strCapital($course)) . '</strong>
				</span>
			</td>
		</tr>';
	}

	$html .= '</table>';

	return $html;
}



function  formattedTableConcepts($paymentConcept, $typePrice = 'regular')
{
	$paymentConceptDetails = $paymentConcept->details ?? [];
	$html = "";
	$table = '<table border="1" cellpadding="1" cellspacing="1" style="width:500px">
					<tbody>
						<tr>
							<td style="text-align:center"><span style="font-size:11pt"><span style="font-family:&quot;Arial MT&quot;,sans-serif"><strong><span style="font-size:9.0pt"><span style="font-family:&quot;Arial&quot;,sans-serif">Niveles</span></span></strong></span></span></td>
							<td style="text-align:center"><span style="font-size:11pt"><span style="font-family:&quot;Arial MT&quot;,sans-serif"><strong><span style="font-size:9.0pt"><span style="font-family:&quot;Arial&quot;,sans-serif">Colegiatura Anual</span></span></strong></span></span></td>
						</tr>
						#details
					</tbody>
				</table>';
	$formatMoney = $paymentConcept->currency_type_id > 0 ? $paymentConcept->currency_type->currency_symbol : '$';

	foreach ($paymentConceptDetails as $key => $row) {
		$price = $typePrice == 'exceptional' ? $row->price_exceptional : ($typePrice  == 'cae' ? $row->price_cae : $row->price_regular);

		$html .= '<tr>
						<td style="text-align:center"><span style="font-size:11pt"><span style="font-family:&quot;Arial MT&quot;,sans-serif"><span style="font-size:9.0pt">' . $row->description . '</span></span></span></td>
						<td style="text-align:center"><span style="font-size:11pt"><span style="font-family:&quot;Arial MT&quot;,sans-serif"><span style="font-size:9.0pt"><span style="font-family:&quot;Arial&quot;,sans-serif">' . $formatMoney . ' ' . ($formatMoney == '$' ? format_number($price) : $price) . '</span></span></span></span></td>
				  </tr>';
	}

	$table = str_replace('#details', $html, $table);

	return $table;
}


function signatureSection(){
	return '<table align="left" cellspacing="0" class="Table" style="border-collapse:collapse; border:none; margin-left:6px; margin-right:6px">
	<tbody>
		<tr>
			<td style="border-bottom:1px solid black; border-left:1px solid black; border-right:1px solid black; border-top:1px solid black; height:120px; vertical-align:top; width:256px">
			<p style="margin-left:40px"><span style="font-size:11pt"><span style="font-family:&quot;Arial MT&quot;,sans-serif"><span style="font-size:9.0pt">Por</span></span></span><span style="font-size:11pt"><span style="font-family:&quot;Arial MT&quot;,sans-serif"><span style="font-size:9.0pt">&nbsp;</span></span></span><span style="font-size:11pt"><span style="font-family:&quot;Arial MT&quot;,sans-serif"><span style="font-size:9.0pt">CORPORACION</span> <span style="font-size:9.0pt">EDUCACIONAL</span> <span style="font-size:9.0pt">BRADFORD:</span></span></span></p>

			<p>&nbsp;</p>

			<p style="margin-left:40px"><span style="font-size:11pt"><span style="font-family:&quot;Arial MT&quot;,sans-serif"><span style="font-size:10.0pt">Rectora:</span></span></span></p>

			<p style="margin-left:40px">&nbsp;</p>

			<p style="margin-left:40px">&nbsp;</p>

			<p style="margin-left:40px"><span style="font-size:11pt"><span style="font-family:&quot;Arial MT&quot;,sans-serif"><span style="font-size:10.0pt">Presidente Directorio:</span></span></span></p>

			<p style="margin-left:40px">&nbsp;</p>

			<p style="margin-left:40px">&nbsp;</p>

			<p style="margin-left:40px; margin-right:74px">&nbsp;</p>
			</td>
			<td style="border-bottom:1px solid black; border-left:none; border-right:1px solid black; border-top:1px solid black; height:120px; vertical-align:top; width:245px">
			<p>&nbsp;</p>

			<p style="margin-left:40px">&nbsp;</p>

			<p style="margin-left:40px"><span style="font-size:11pt"><span style="font-family:&quot;Arial MT&quot;,sans-serif"><span style="font-size:9.0pt">Apoderado de Cuentas:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span></p>

			<p style="margin-left:7px">&nbsp;</p>
			</td>
		</tr>
	</tbody>
</table>';
}
function pre($data = [])
{
	echo '<pre>';
	print_r($data);
	echo '</pre>';
}

function pre_die($data = [])
{
	echo '<pre>';
	print_r($data);
	echo '</pre>';
	die();
}


function getConfiguration($code)
{
	$configurationRepository = new ConfigurationRepository();
	$configuration = $configurationRepository->getByCode($code);
	$data = $configuration->data_fields ? json_decode($configuration->data_fields, TRUE) : null;
	if (empty($data)) {
		return null;
	}

	switch ($code) {
		case 'general':
			return [
				'whatsapp' => $data['whatsapp'] ?? null,
				'email' => $data['email'] ?? null,
				'cae_title' => $data['cae_title'] ?? null,
				'cae_message' => $data['cae_message'] ?? null,
				'cfe_title' => $data['cfe_title'] ?? null,
				'cfe_message' => $data['cfe_message'] ?? null,
				'inactive_title' => $data['inactive_title'] ?? null,
				'inactive_message' => $data['inactive_message'] ?? null,
				'enrollment_start_date' => $data['enrollment_start_date'] ?? null,
				'enrollment_end_date' => $data['enrollment_end_date'] ?? null,
			];
		case 'contract_template':
			return [
				'template' => $data['template'] ?? null,
			];
		case 'toku':
			$fields = [
				'status' => $data['status'] ?? false,
				'api_token' => $data['api_token'] ?? '',
				'url_toku' => $data['url_toku'] ?? null,
				'url_portal' => !empty($data['url_portal']) ? $data['url_portal'] . '/customer-portal?user=#PARENT_CODE' :  null,
				'url_portal_base' => $data['url_portal'] ?? null,
				'account_code' => $data['account_code'] ?? null,
				'generic_rut' => $data['generic_rut'] ?? null,
				'url_pac' => null,
				'url_onetime' => null,
			];
			if (!empty($data['url_portal']) && !empty($data['account_code'])) {
				$fields['url_pac'] = $data['url_portal'] . '/recurring?accessSource=priv0&portal=0&user=#PARENT_CODE&account=' . $data['account_code'] . '&subscriptions=#SUBSCRIPTION_IDS';
				$fields['url_onetime'] = $data['url_portal'] . '/onetime?accessSource=priv0&user=#PARENT_CODE&account=' . $data['account_code'] . '&invoices=#INVOICE_IDS';
			}
			return $fields;
		case 'firmaki':
			return [
				'status' => $data['status'] ?? false,
				'environment' => $data['environment'] ?? 'integration',
				'url_sandbox' => $data['url_sandbox'] ?? 'https://api-sandbox.signapis.com',
				'url_production' => $data['url_production'] ?? 'https://api.signapis.com',
				'api_key' => $data['api_key'] ?? null,
				'email_account' => $data['email_account'] ?? null,
				'password_account' => $data['password_account'] ?? null,
			];

		case 'mindicador':
			return [
				'uf' => $data['uf'] ?? ['codigo' => 'uf', 'valor' => 0, 'fecha' => null],
			];

		default:
			return null;
	}
}


/**
 * Obtiene el valor actual de la UF desde mindicador.cl con cache diario en BD.
 * @return float Valor de la UF en CLP (ej: 39694.31)
 */
function getUfValue(): float
{
	$config = getConfiguration('mindicador');
	$uf = $config['uf'] ?? null;

	// Verificar si ya tenemos el valor de hoy
	if ($uf && !empty($uf['fecha']) && $uf['valor'] > 0) {
		$storedDate = substr($uf['fecha'], 0, 10); // 'YYYY-MM-DD'
		if ($storedDate === now()->format('Y-m-d')) {
			return (float) $uf['valor'];
		}
	}

	// Consultar mindicador.cl
	try {
		$response = Http::timeout(5)->get('https://mindicador.cl/api');
		if ($response->successful()) {
			$data = $response->json();
			$ufData = $data['uf'] ?? null;

			if ($ufData && isset($ufData['valor'])) {
				// Guardar en BD con fecha de hoy
				$ufData['fecha'] = now()->format('Y-m-d');
				Configuration::where('code', 'mindicador')->update([
					'data_fields' => json_encode(['uf' => $ufData]),
					'updated_at' => now(),
				]);
				return (float) $ufData['valor'];
			}
		}
	} catch (\Exception $e) {
		// Si falla la API, retornar valor almacenado o 0
	}

	return (float) ($uf['valor'] ?? 0);
}


function normalizePhoneNumber(?string $phone): ?string
{
	if (empty($phone)) {
		return null;
	}

	// Eliminamos espacios, guiones y otros caracteres
	$clean = preg_replace('/\D+/', '', $phone);

	// Si ya viene con el prefijo +56
	if (strpos($phone, '+56') === 0) {
		return $phone;
	}

	// Si empieza con 56 (sin +), lo agregamos
	if (strpos($clean, '56') === 0) {
		return '+' . $clean;
	}

	// Si empieza con 9 (celular chileno estándar), le agregamos +56
	if (strpos($clean, '9') === 0 && strlen($clean) === 9) {
		return '+56' . $clean;
	}

	// Si empieza con 0 o 09 (a veces lo anotan así)
	if (strpos($clean, '09') === 0) {
		return '+56' . substr($clean, 1);
	}

	// Caso genérico: retornamos con +56 al inicio
	return '+56' . $clean;
}

/**
 * Convierte a mayúsculas, elimina tildes y deja solo letras y espacios.
 */
function normalizeOnlyText(string $text): string
{
	// Quitar espacios extra al inicio y final
	$text = trim($text);

	// Reemplazar tildes y caracteres especiales comunes
	$text = strtr($text, [
		'á' => 'a',
		'é' => 'e',
		'í' => 'i',
		'ó' => 'o',
		'ú' => 'u',
		'Á' => 'A',
		'É' => 'E',
		'Í' => 'I',
		'Ó' => 'O',
		'Ú' => 'U',
		'ñ' => 'n',
		'Ñ' => 'N',
	]);

	// Dejar solo letras y espacios (quita cualquier otro carácter)
	$text = preg_replace('/[^A-Za-z\s]/', '', $text);
	$text = trim($text);
	// Convertir a mayúsculas
	return strtoupper($text);
}


/**
 * Ofusca un correo manteniendo parte visible del local y dominio.
 * Ejemplo: correo@gmail.com → coxxxx@xxxxx.cl | coxxxx@gmail.com
 */
function obfuscateEmail(?string $email): string
{
	if (empty($email) || !str_contains($email, '@')) {
		return '';
	}

	[$local, $domain] = explode('@', $email, 2);
	$local = trim($local);
	$domain = trim($domain);

	if (strlen($local) <= 2) {
		return str_repeat('x', strlen($local)) . '@' . $domain;
	}

	// Mostrar las 2 primeras letras y ofuscar el resto hasta 6 caracteres
	$visible = substr($local, 0, 2);
	$maskedLength = min(strlen($local) - 2, 6);
	$masked = str_repeat('x', $maskedLength);

	return strtolower($visible . $masked . '@' . $domain);
}