Cabañas Lomas del Uritorco - Sistema de Reservas

🏞️ Cabañas Lomas del Uritorco

Capilla del Monte - Córdoba

✨ Escapada perfecta en las sierras cordobesas

📅 Consulta tu reserva

Día que llegás a la cabaña
Día que dejás libre la cabaña (checkout mañana)
Mencione la cantidad de adultos
Menores de 12 años - 50% de descuento por menor
Consultar políticas de mascotas (opcional)

<!DOCTYPE html>

<html>

<head>

<meta charset="UTF-8">

<meta name="viewport" content="width=device-width, initial-scale=1.0">

<title>Cabañas Lomas del Uritorco - Sistema de Reservas</title>

<style>

/* CSS Completo para el Sistema de Reservas */

.cabanas-container {

max-width: 600px;

margin: 0 auto;

padding: 20px;

font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;

background: #ffffff;

border-radius: 12px;

box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);

}

.cabanas-header {

text-align: center;

margin-bottom: 30px;

}

.cabanas-title {

font-size: 2rem;

font-weight: bold;

color: #1f2937;

margin: 0 0 10px 0;

}

.cabanas-subtitle {

font-size: 1.1rem;

color: #6b7280;

margin: 0 0 5px 0;

}

.cabanas-info {

font-size: 0.9rem;

color: #9ca3af;

margin: 0;

}

.cabanas-form-container {

background: linear-gradient(135deg, #dbeafe 0%, #e0e7ff 100%);

padding: 30px;

border-radius: 12px;

margin-bottom: 30px;

.cabanas-form-title {

font-size: 1.3rem;

font-weight: 600;

color: #1f2937;

margin: 0 0 25px 0;

display: flex;

align-items: center;

}

.cabanas-form {

display: flex;

flex-direction: column;

gap: 20px;

}

.cabanas-dates-row {

display: grid;

grid-template-columns: 1fr 1fr;

gap: 15px;

}

@media (max-width: 640px) {

.cabanas-dates-row {

grid-template-columns: 1fr;

}

}

.cabanas-field {

display: flex;

flex-direction: column;

gap: 8px;

}

.cabanas-field label {

font-size: 0.9rem;

font-weight: 500;

color: #374151;

}

.cabanas-field input,

.cabanas-field select {

padding: 12px 16px;

border: 2px solid #e5e7eb;

border-radius: 8px;

font-size: 1rem;

font-family: inherit;

transition: all 0.2s ease;

background: #ffffff;

}

.cabanas-field input:focus,

.cabanas-field select:focus {

outline: none;

border-color: #3b82f6;

box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);

}

.cabanas-field small {

font-size: 0.8rem;

color: #6b7280;

margin-top: 4px;

}

.cabanas-submit-btn {

background: linear-gradient(135deg, #3b82f6 0%, #1e40af 100%);

color: white;

border: none;

border-radius: 8px;

padding: 16px 24px;

font-size: 1rem;

font-weight: 600;

cursor: pointer;

transition: all 0.2s ease;

display: flex;

align-items: center;

justify-content: center;

gap: 8px;

margin-top: 10px;

}

.cabanas-submit-btn:hover {

transform: translateY(-2px);

box-shadow: 0 8px 25px rgba(59, 130, 246, 0.3);

}

.cabanas-submit-btn:active {

transform: translateY(0);

}

.cabanas-submit-btn:disabled {

background: #9ca3af;

cursor: not-allowed;

transform: none;

box-shadow: none;

}

.cabanas-result {

background: #ffffff;

border-radius: 12px;

padding: 30px;

border: 1px solid #e5e7eb;

margin-top: 20px;

}

.cabanas-success {

border-color: #10b981;

background: linear-gradient(135deg, #f0fdfa 0%, #ecfdf5 100%);

}

.cabanas-error {

border-color: #ef4444;

background: linear-gradient(135deg, #fef2f2 0%, #fee2e2 100%);

}

.result-header {

display: flex;

align-items: center;

gap: 12px;

margin-bottom: 20px;

}

.result-icon {

font-size: 2rem;

}

.result-header h3 {

margin: 0;

font-size: 1.5rem;

color: #1f2937;

}

.result-summary {

background: #ffffff;

border-radius: 8px;

padding: 20px;

margin-bottom: 20px;

border: 1px solid #e5e7eb;

}

.result-summary div {

display: flex;

justify-content: space-between;

margin-bottom: 8px;

font-size: 0.95rem;

}

.price-breakdown {

background: #f9fafb;

border-radius: 8px;

padding: 15px;

margin: 15px 0;

}

.price-breakdown h4 {

margin: 0 0 10px 0;

font-size: 1rem;

color: #374151;

}

.breakdown-day {

display: flex;

justify-content: space-between;

align-items: center;

padding: 8px 0;

border-bottom: 1px solid #e5e7eb;

font-size: 0.9rem;

}

.breakdown-day:last-child {

border-bottom: none;

}

.breakdown-date {

font-weight: 500;

}

.breakdown-type {

color: #6b7280;

font-size: 0.8rem;

}

.breakdown-price {

font-weight: 600;

color: #1f2937;

}

.total-price {

background: linear-gradient(135deg, #8b5cf6 0%, #7c3aed 50%, #6d28d9 100%);

color: white;

padding: 15px;

border-radius: 8px;

text-align: center;

font-size: 1.2rem;

margin: 20px 0;

box-shadow: 0 4px 15px rgba(139, 92, 246, 0.3);

border: 1px solid rgba(255, 255, 255, 0.2);

}

.action-buttons {

display: flex;

gap: 12px;

margin-top: 20px;

}

.btn-reserve,

.btn-info {

padding: 12px 24px;

border: none;

border-radius: 6px;

font-size: 0.9rem;

font-weight: 600;

cursor: pointer;

transition: all 0.2s ease;

text-decoration: none;

display: inline-block;

text-align: center;

flex: 1;

}

.btn-reserve {

background: linear-gradient(135deg, #10b981 0%, #059669 100%);

color: white;

}

.btn-reserve:hover {

transform: translateY(-1px);

box-shadow: 0 4px 12px rgba(16, 185, 129, 0.3);

}

.btn-info {

background: #ffffff;

color: #3b82f6;

border: 2px solid #3b82f6;

}

.btn-info:hover {

background: #3b82f6;

color: white;

}

.suggestion {

background: #fffbeb;

border: 1px solid #fbbf24;

border-radius: 8px;

padding: 15px;

margin-top: 15px;

}

.suggestion p {

margin: 0;

font-size: 0.9rem;

color: #92400e;

}

.cabanas-info-section {

background: #f9fafb;

border-radius: 12px;

padding: 25px;

}

.cabanas-info-section h3 {

margin: 0 0 20px 0;

color: #1f2937;

}

.cabanas-info-grid {

display: grid;

grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));

gap: 15px;

margin-bottom: 20px;

}

.cabin-card {

background: #ffffff;

border-radius: 8px;

padding: 20px;

border: 1px solid #e5e7eb;

}

.cabin-card h4 {

margin: 0 0 10px 0;

color: #1f2937;

font-size: 1.1rem;

}

.cabin-card p {

margin: 0;

color: #6b7280;

font-size: 0.9rem;

}

.assignment-note {

text-align: center;

font-style: italic;

color: #6b7280;

font-size: 0.9rem;

margin-bottom: 20px;

}

.contact-info {

background: #ffffff;

border-radius: 8px;

padding: 20px;

border: 1px solid #e5e7eb;

}

.contact-info h4 {

margin: 0 0 15px 0;

color: #1f2937;

}

.contact-info p {

margin: 8px 0;

font-size: 0.9rem;

color: #374151;

}

.contact-info a {

color: #3b82f6;

text-decoration: none;

font-weight: 500;

}

.contact-info a:hover {

text-decoration: underline;

}

.result-message {

font-size: 1rem;

color: #374151;

margin-bottom: 15px;

line-height: 1.5;

}

.available-cabins {

background: #f0fdfa;

border-radius: 8px;

padding: 15px;

margin: 15px 0;

border: 1px solid #10b981;

}

.available-cabins h4 {

margin: 0 0 12px 0;

font-size: 1rem;

color: #065f46;

}

.cabins-list {

display: flex;

flex-direction: column;

gap: 8px;

}

.available-cabin {

display: flex;

align-items: center;

gap: 10px;

padding: 8px 12px;

background: #ffffff;

border-radius: 6px;

border: 1px solid #d1fae5;

}

.cabin-emoji {

font-size: 1.2rem;

}

.cabin-name {

font-weight: 500;

color: #065f46;

flex: 1;

}

/* Colores específicos para cada cabaña */

.cabin-lomas-apart {

color: #10B981 !important; /* Verde para Lomas Apart */

}

.cabin-uritorco {

color: #8B5CF6 !important; /* Violeta para Uritorco */

}

.cabin-capacity {

font-size: 0.8rem;

color: #059669;

background: #ecfdf5;

padding: 2px 8px;

border-radius: 4px;

}

.cabin-details {

flex: 1;

display: flex;

flex-direction: column;

gap: 4px;

}

.cabin-calendar-info small {

color: #9ca3af;

font-size: 0.7rem;

}

.advance-payment {

background: #fef3c7;

border: 1px solid #f59e0b;

border-radius: 8px;

padding: 15px;

margin: 15px 0;

text-align: center;

}

.advance-payment h4 {

margin: 0 0 8px 0;

color: #92400e;

}

.advance-payment p {

margin: 0;

font-size: 0.9rem;

color: #92400e;

}

.btn-mercadopago {

background: linear-gradient(135deg, #009ee3 0%, #0084d4 100%) !important;

color: white !important;

}

.btn-mercadopago:hover {

background: linear-gradient(135deg, #0084d4 0%, #006bb3 100%) !important;

transform: translateY(-1px);

box-shadow: 0 4px 12px rgba(0, 158, 227, 0.3);

}

.action-buttons {

display: grid;

grid-template-columns: 1fr;

gap: 12px;

margin-top: 20px;

}

@media (min-width: 640px) {

.action-buttons {

grid-template-columns: 1fr 1fr 1fr;

}

}

@media (max-width: 640px) {

.cabanas-container {

padding: 15px;

}

.cabanas-form-container {

padding: 20px;

}

.action-buttons {

flex-direction: column;

}

}

</style>

</head>

<body>

<div class="cabanas-container">

<div class="cabanas-header">

<h1 class="cabanas-title">🏞️ Cabañas Lomas del Uritorco</h1>

<p class="cabanas-subtitle">Capilla del Monte - Córdoba</p>

<p class="cabanas-info">✨ Escapada perfecta en las sierras cordobesas</p>

</div>

<div class="cabanas-form-container">

<h3 class="cabanas-form-title">📅 Consulta tu reserva</h3>

<form id="cabanas-booking-form" class="cabanas-form">

<div class="cabanas-dates-row">

<div class="cabanas-field">

<label for="check_in">Fecha de llegada</label>

<input type="date" id="check_in" name="check_in" required>

<small>Día que llegás a la cabaña</small>

</div>

<div class="cabanas-field">

<label for="check_out">Fecha de salida</label>

<input type="date" id="check_out" name="check_out" required>

<small>Día que dejás libre la cabaña (checkout mañana)</small>

</div>

</div>

<div class="cabanas-field">

<label for="guests">👤 Cantidad de adultos</label>

<select id="guests" name="guests" required>

<option value="1">1 adulto</option>

<option value="2">2 adultos</option>

<option value="3" selected>3 adultos</option>

<option value="4">4 adultos</option>

<option value="5">5 adultos</option>

<option value="6">6 adultos</option>

</select>

<small>Mencione la cantidad de adultos</small>

</div>

<div class="cabanas-field">

<label for="minors">🧒 ¿Trae menores?</label>

<select id="minors" name="minors">

<option value="no">No</option>

<option value="1">1 menor</option>

<option value="2">2 menores</option>

<option value="3">3 menores</option>

</select>

<small>Menores de 12 años - 50% de descuento por menor</small>

</div>

<div class="cabanas-field">

<label for="pets">🐶 ¿Trae mascotas?</label>

<select id="pets" name="pets">

<option value="no">No</option>

<option value="yes">Sí</option>

</select>

<small>Consultar políticas de mascotas (opcional)</small>

</div>

<button type="submit" id="check-availability-btn" class="cabanas-submit-btn">

<span class="btn-text">🔍 Consultar Disponibilidad</span>

<span class="btn-loading" style="display:none;">⏳</span>

</button>

</form>

</div>

</div>

<!-- ...existing code UI y formulario... -->

<!-- El bloque JS y todas las funciones van al final, dentro de <script> -->

function findSeason(dateStr) {

const date = new Date(dateStr);

for (const season of PRICING_CONFIG.seasons) {

const startDate = new Date(season.start);

const endDate = new Date(season.end);

if (date >= startDate && date <= endDate) {

return season;

}

}

// Si no encuentra temporada, usar temporada normal como fallback

return PRICING_CONFIG.seasons[1];

}

// Función para detectar días "sandwich" entre reservas (DESACTIVADA)

// Esta función se usará en el futuro cuando tengamos datos reales de reservas anteriores

function detectSandwichDates(blockedDates) {

// DESACTIVADO: No aplicar descuentos sandwich hasta tener datos reales

return [];

/*

const sandwichDates = [];

// Ordenar fechas bloqueadas

const sortedBlockedDates = blockedDates.sort();

console.log('🥪 Detectando días sandwich entre reservas:', sortedBlockedDates);

// Buscar huecos de 1-2 días entre reservas

for (let i = 0; i < sortedBlockedDates.length - 1; i++) {

const currentDate = new Date(sortedBlockedDates[i]);

const nextDate = new Date(sortedBlockedDates[i + 1]);

// Calcular días entre esta fecha y la siguiente

const daysBetween = Math.ceil((nextDate - currentDate) / (1000 60 60 * 24)) - 1;

// Si hay 1 o 2 días libres entre reservas, son días sandwich

if (daysBetween > 0 && daysBetween <= 2) {

const sandwichStart = new Date(currentDate);

sandwichStart.setDate(currentDate.getDate() + 1);

console.log(`🥪 Encontrado hueco sandwich: ${daysBetween} días entre ${sortedBlockedDates[i]} y ${sortedBlockedDates[i + 1]}`);

for (let j = 0; j < daysBetween; j++) {

const sandwichDay = new Date(sandwichStart);

sandwichDay.setDate(sandwichStart.getDate() + j);

const sandwichDateStr = sandwichDay.toISOString().split('T')[0];

sandwichDates.push(sandwichDateStr);

console.log(`🥪 Día sandwich agregado: ${sandwichDateStr}`);

}

}

}

console.log('🥪 Total días sandwich detectados:', sandwichDates);

return sandwichDates;

*/

}

// Función para verificar si una fecha está en período sandwich (DESACTIVADA)

function isSandwichDate(dateStr, cabinId, allBlockedDates) {

// DESACTIVADO: No aplicar descuentos sandwich hasta tener datos reales

return false;

// const sandwichDates = detectSandwichDates(allBlockedDates);

// return sandwichDates.includes(dateStr);

}

// Función para verificar si es fin de semana largo (usa feriados reales de Google Calendar - OPTIMIZADA)

async function isLongWeekend(date, realHolidays = null) {

const dateStr = date.toISOString().split('T')[0];

const dayOfWeek = date.getDay(); // 0 = Domingo, 6 = Sábado

// Obtener feriados reales si no se proporcionaron (solo del mes relevante)

let holidays = realHolidays;

if (!holidays) {

// Optimizar: solo buscar feriados del mes de la fecha consultada

const monthStart = new Date(date.getFullYear(), date.getMonth(), 1).toISOString().split('T')[0];

const monthEnd = new Date(date.getFullYear(), date.getMonth() + 1, 0).toISOString().split('T')[0];

holidays = await fetchArgentineHolidays(monthStart, monthEnd);

}

// Si es feriado oficial, aplicar tarifa de fin de semana largo

if (holidays.includes(dateStr)) {

return true;

}

// Verificar feriados puente (lunes después de domingo feriado, viernes antes de domingo feriado)

if (dayOfWeek === 1) { // Lunes

const sunday = new Date(date);

sunday.setDate(date.getDate() - 1);

if (holidays.includes(sunday.toISOString().split('T')[0])) {

return true;

}

}

if (dayOfWeek === 5) { // Viernes

const sunday = new Date(date);

sunday.setDate(date.getDate() + 2);

if (holidays.includes(sunday.toISOString().split('T')[0])) {

return true;

}

}

return false;

}

// Función para generar rango de fechas (CORREGIDO para evitar overbooking)

function getDateRange(startDate, endDate) {

const dates = [];

const currentDate = new Date(startDate);

const lastDate = new Date(endDate);

// IMPORTANTE: El checkout NO debe estar incluido en las fechas ocupadas

// Si checkout es 30/7, significa que liberan la cabaña el 30/7 por la mañana

// Por tanto, las fechas ocupadas son solo hasta el 29/7 (la última noche)

while (currentDate < lastDate) {

dates.push(currentDate.toISOString().split('T')[0]);

currentDate.setDate(currentDate.getDate() + 1);

}

return dates;

}

// Función para formatear fecha en español

function formatDateSpanish(dateStr) {

const date = new Date(dateStr + 'T12:00:00'); // Agregar hora para evitar problemas de zona horaria

const options = {

weekday: 'short',

day: 'numeric',

month: 'short'

};

return date.toLocaleDateString('es-AR', options);

}

// Función para generar URL de MercadoPago con datos de la reserva

function generateMercadoPagoURL(data) {

// URL base de MercadoPago

const baseURL = 'https://link.mercadopago.com.ar/lomasdeluritorco';

// Preparar datos de la reserva para n8n (se pasarán como parámetros)

const reservationData = {

// Datos de la reserva

checkin: data.check_in,

checkout: data.check_out,

guests: data.guests,

nights: data.nights,

// Precios

total_ars: data.total_price_ars,

total_usd: data.total_price_usd,

anticipo_ars: data.advance_payment_ars,

anticipo_usd: data.advance_payment_usd,

// Detalles

season: data.season,

cabins: data.available_cabins.map(c => c.name).join(' o '),

// Timestamp

timestamp: Date.now()

};

// Construir URL con parámetros para n8n (opcional, para tracking)

const urlParams = new URLSearchParams({

// Parámetros básicos para referencia

amount: data.advance_payment_ars,

reference: `RES-${reservationData.timestamp}`,

checkin: data.check_in,

checkout: data.check_out,

guests: data.guests

});

// En una integración completa con n8n, podrías usar:

// return `${baseURL}?${urlParams.toString()}`;

// Por ahora, URL directa (n8n capturará los datos después del pago)

return baseURL;

}

// Event listener principal

document.addEventListener('DOMContentLoaded', function() {

const form = document.getElementById('cabanas-booking-form');

const checkInInput = document.getElementById('check_in');

const checkOutInput = document.getElementById('check_out');

const submitBtn = document.getElementById('check-availability-btn');

const btnText = submitBtn.querySelector('.btn-text');

const btnLoading = submitBtn.querySelector('.btn-loading');

const resultDiv = document.getElementById('availability-result');

// Establecer fecha mínima como hoy

const today = new Date().toISOString().split('T')[0];

checkInInput.min = today;

// Actualizar fecha mínima de check-out cuando cambia check-in

checkInInput.addEventListener('change', function() {

const checkInDate = new Date(this.value);

const nextDay = new Date(checkInDate);

nextDay.setDate(checkInDate.getDate() + 1);

checkOutInput.min = nextDay.toISOString().split('T')[0];

if (checkOutInput.value && checkOutInput.value <= this.value) {

checkOutInput.value = '';

}

});

form.addEventListener('submit', async function(e) {

e.preventDefault();

const checkIn = checkInInput.value;

const checkOut = checkOutInput.value;

const guests = parseInt(document.getElementById('guests').value);

// Debug para verificar fechas capturadas

console.log(`📝 FORMULARIO ENVIADO:`);

console.log(` Check-in capturado: ${checkIn}`);

console.log(` Check-out capturado: ${checkOut}`);

console.log(` Huéspedes: ${guests}`);

// Validaciones

if (!checkIn || !checkOut) {

showError('Por favor selecciona las fechas de llegada y salida');

return;

}

if (checkIn >= checkOut) {

showError('La fecha de salida debe ser posterior a la de llegada');

return;

}

if (checkIn < today) {

showError('No puedes seleccionar fechas pasadas');

return;

}

// Mostrar loading

submitBtn.disabled = true;

btnText.style.display = 'none';

btnLoading.style.display = 'inline';

resultDiv.style.display = 'none';

try {

// Verificar disponibilidad (ahora incluye calendarios externos)

const result = await checkAvailability(checkIn, checkOut, guests);

// Ocultar loading

submitBtn.disabled = false;

btnText.style.display = 'inline';

btnLoading.style.display = 'none';

resultDiv.style.display = 'block';

if (result.available) {

resultDiv.innerHTML = generateSuccessHTML(result);

resultDiv.className = 'cabanas-result cabanas-success';

} else {

resultDiv.innerHTML = generateErrorHTML(result.message, result.suggestions, result.unavailableReasons);

resultDiv.className = 'cabanas-result cabanas-error';

}

} catch (error) {

console.error('Error verificando disponibilidad:', error);

// Ocultar loading

submitBtn.disabled = false;

btnText.style.display = 'inline';

btnLoading.style.display = 'none';

showError('Error al verificar disponibilidad. Por favor intenta nuevamente.');

}

});

async function checkAvailability(checkIn, checkOut, guests) {

// Verificar disponibilidad de todas las cabañas

const availabilityResult = await checkAllCabinsAvailability(checkIn, checkOut, guests);

console.log('Resultado de disponibilidad:', availabilityResult); // Debug

if (availabilityResult.availableCabins.length === 0) {

// Buscar fechas alternativas

const suggestions = await suggestAlternativeDates(checkIn, checkOut, guests);

let message = `Lo sentimos, no hay cabañas disponibles para ${guests} persona${guests > 1 ? 's' : ''} del ${formatDateSpanish(checkIn)} al ${formatDateSpanish(checkOut)}.`;

if (availabilityResult.unavailableReasons.length > 0) {

message += `\n\nMotivos:\n${availabilityResult.unavailableReasons.join('\n')}`;

}

return {

available: false,

message: message,

suggestions: suggestions,

unavailableReasons: availabilityResult.unavailableReasons

};

}

// Calcular pricing (usar la primera cabaña disponible para el cálculo)

const pricing = await calculatePricing(checkIn, checkOut, guests);

return {

available: true,

message: `¡Excelente! Tenemos ${availabilityResult.availableCabins.length} cabaña${availabilityResult.availableCabins.length > 1 ? 's' : ''} disponible${availabilityResult.availableCabins.length > 1 ? 's' : ''} para tu estadía`,

nights: pricing.nights,

total_price_usd: pricing.total_usd,

total_price_ars: pricing.total_ars,

advance_payment_usd: pricing.advance_payment_usd,

advance_payment_ars: pricing.advance_payment_ars,

price_breakdown: pricing.breakdown,

season: pricing.season,

available_cabins: availabilityResult.availableCabins,

check_in: checkIn,

check_out: checkOut,

guests: guests

};

}

async function calculatePricing(checkIn, checkOut, guests) {

const startDate = new Date(checkIn);

const endDate = new Date(checkOut);

const nights = Math.ceil((endDate - startDate) / (1000 60 60 * 24));

console.log(`🎯 CALCULANDO PRECIOS: ${checkIn} a ${checkOut} (${nights} noches, ${guests} huéspedes)`);

// Obtener feriados reales SOLO del período de la estadía (OPTIMIZADO)

const realHolidays = await fetchArgentineHolidays(checkIn, checkOut);

console.log(`📅 Feriados oficiales para el período ${checkIn}-${checkOut}:`, realHolidays);

let total = 0;

const breakdown = [];

let seasonInfo = '';

// Detectar días especiales ANTES del loop (solo para logs, sin descuentos)

console.log(`📅 Calculando precios estándar basados en temporada y fines de semana largos`);

for (let i = 0; i < nights; i++) {

const currentDate = new Date(startDate);

currentDate.setDate(startDate.getDate() + i);

const dateStr = currentDate.toISOString().split('T')[0];

// Buscar temporada

const season = findSeason(dateStr);

if (!season) continue;

// Guardar info de temporada para mostrar

if (seasonInfo === '') {

seasonInfo = season.name;

}

const basePriceUSD = season.prices_usd[guests];

const basePriceARS = convertUSDtoARS(basePriceUSD);

let dayTotalUSD = basePriceUSD;

let surchargeUSD = 0;

let type = 'Día normal';

// Verificar tipo de día

const dayOfWeek = currentDate.getDay(); // 0 = Domingo, 6 = Sábado

const isWeekend = (dayOfWeek === 0 || dayOfWeek === 6);

const isHoliday = realHolidays.includes(dateStr);

const isLongWeekendCheck = await isLongWeekend(currentDate, realHolidays);

console.log(`📅 ${dateStr}: Base $${basePriceUSD}USD | Weekend: ${isWeekend ? 'SÍ' : 'NO'} | Holiday: ${isHoliday ? 'SÍ' : 'NO'} | LongWeekend: ${isLongWeekendCheck ? 'SÍ' : 'NO'}`);

// Aplicar recargos según tipo de día

if (isHoliday || isLongWeekendCheck) {

surchargeUSD = season.long_weekend_surcharge_usd;

dayTotalUSD += surchargeUSD;

type = 'Fin de semana largo';

console.log(` ➕ Recargo fin de semana largo: +$${surchargeUSD}USD`);

} else if (isWeekend) {

surchargeUSD = season.weekend_surcharge_usd;

dayTotalUSD += surchargeUSD;

type = 'Fin de semana';

console.log(` ➕ Recargo fin de semana: +$${surchargeUSD}USD`);

}

console.log(` 🎯 Total día: $${dayTotalUSD}USD (${type})`);

console.log(` 💰 En pesos: $${convertUSDtoARS(dayTotalUSD).toLocaleString()}ARS`);

console.log(` ---`);

breakdown.push({

date: dateStr,

base_price_usd: basePriceUSD,

base_price_ars: basePriceARS,

surcharge_usd: surchargeUSD,

surcharge_ars: convertUSDtoARS(surchargeUSD),

discount_usd: 0, // Sin descuentos por ahora

discount_ars: 0,

total_usd: dayTotalUSD,

total_ars: convertUSDtoARS(dayTotalUSD),

type: type,

season: season.name,

is_special: false // Removido lógica sandwich

});

total += dayTotalUSD;

}

return {

nights: nights,

total_usd: total,

total_ars: convertUSDtoARS(total),

breakdown: breakdown,

season: seasonInfo,

advance_payment_usd: Math.round(total * 0.5), // 50% anticipo

advance_payment_ars: convertUSDtoARS(Math.round(total * 0.5))

};

}

function generateSuccessHTML(data) {

const checkInFormatted = formatDateSpanish(data.check_in);

const checkOutFormatted = formatDateSpanish(data.check_out);

// Debug para verificar fechas

console.log(`🗓️ DEBUG FECHAS:`);

console.log(` Fecha llegada original: ${data.check_in}`);

console.log(` Fecha salida original: ${data.check_out}`);

console.log(` Fecha llegada formateada: ${checkInFormatted}`);

console.log(` Fecha salida formateada: ${checkOutFormatted}`);

// Generar lista de cabañas disponibles

const cabinsList = data.available_cabins.map(cabin =>

`${cabin.emoji} ${cabin.name}`

).join(' o ');

const whatsappMessage = `Hola! Me interesa reservar una cabaña del ${checkInFormatted} al ${checkOutFormatted} para ${data.guests} persona${data.guests > 1 ? 's' : ''}. Cabañas disponibles: ${cabinsList}. El total sería $${data.total_price_ars.toLocaleString('es-AR')} ARS. ¿Está disponible?`;

const whatsappURL = `https://wa.me/5493548436436?text=${encodeURIComponent(whatsappMessage)}`;

const emailSubject = `Consulta Reserva - ${checkInFormatted} al ${checkOutFormatted}`;

const emailBody = `Hola,\n\nMe interesa reservar una cabaña con los siguientes detalles:\n\nFecha llegada: ${checkInFormatted}\nFecha salida: ${checkOutFormatted}\nHuéspedes: ${data.guests}\nCabañas disponibles: ${cabinsList}\nTotal: $${data.total_price_ars.toLocaleString('es-AR')} ARS\n\n¿Está disponible?\n\nGracias!`;

const emailURL = `mailto:[email protected]?subject=${encodeURIComponent(emailSubject)}&body=${encodeURIComponent(emailBody)}`;

// URL de MercadoPago para pago del 50% (necesitarás proporcionar la URL real)

const mercadoPagoURL = generateMercadoPagoURL(data);

// Generar sección de cabañas disponibles con detalles específicos

const availableCabinsHTML = data.available_cabins.length > 0 ? `

<div class="available-cabins">

<h4>🏠 Cabañas disponibles para tu reserva:</h4>

<div class="cabins-list">

${data.available_cabins.map(cabin => `

// ...existing code...

// Solo la versión funcional y corregida debe permanecer aquí

// Si hay duplicados, se eliminan y se mantiene la versión actualizada

// ...existing code...

function generateErrorHTML(message, suggestions = [], unavailableReasons = []) {

let suggestionsHTML = '';

if (suggestions && suggestions.length > 0) {

suggestionsHTML = `

<div class="suggestion">

<h4 style="margin: 0 0 10px 0; color: #92400e;">💡 Fechas alternativas disponibles:</h4>

${suggestions.map(suggestion => `

<div style="background: white; padding: 10px; margin: 8px 0; border-radius: 6px; border: 1px solid #f59e0b;">

<strong>${formatDateSpanish(suggestion.checkIn)} al ${formatDateSpanish(suggestion.checkOut)}</strong><br>

<small>${suggestion.availableCabins} cabaña${suggestion.availableCabins > 1 ? 's' : ''} disponible${suggestion.availableCabins > 1 ? 's' : ''} - $${suggestion.totalARS.toLocaleString('es-AR')} ARS</small>

</div>

`).join('')}

</div>

`;

}

let reasonsHTML = '';

if (unavailableReasons && unavailableReasons.length > 0) {

reasonsHTML = `

<div class="suggestion">

<h4 style="margin: 0 0 10px 0; color: #92400e;">ℹ️ Detalles de disponibilidad:</h4>

${unavailableReasons.map(reason => `

<p style="margin: 4px 0; font-size: 0.9rem;">${reason}</p>

`).join('')}

</div>

`;

}

return `

<div class="result-header">

<span class="result-icon">❌</span>

<h3>No disponible</h3>

</div>

<p class="result-message">${message}</p>

${reasonsHTML}

${suggestionsHTML}

${!suggestionsHTML ? `

<div class="suggestion">

<p><strong>💡 Sugerencia:</strong> Prueba con otras fechas o reduce el número de huéspedes.</p>

</div>

` : ''}

<div class="contact-info">

<h4>📞 ¿Necesitas ayuda?</h4>

<p>Contáctanos para encontrar fechas alternativas:</p>

<p>WhatsApp: <a href="https://wa.me/5493548436436?text=Hola!%20Necesito%20ayuda%20con%20las%20fechas%20de%20reserva" target="_blank">+54 9 354 843-6436</a></p>

<p>Email: <a href="mailto:[email protected]">[email protected]</a></p>

</div>

`;

}

function showError(message) {

resultDiv.style.display = 'block';

resultDiv.innerHTML = generateErrorHTML(message, [], []);

resultDiv.className = 'cabanas-result cabanas-error';

}

});

</body>

<script>

// TODO: Mueve aquí todo el JS y funciones del sistema de reservas

// Ejemplo:

// function parseICalData(...) {...}

// function checkAllCabinsAvailability(...) {...}

// ...etc...

</script>

</html>

<!DOCTYPE html>

<html>

<head>

<meta charset="UTF-8">

<meta name="viewport" content="width=device-width, initial-scale=1.0">

<title>Caba�as Lomas del Uritorco - Sistema de Reservas</title>

<style>

/* CSS Completo para el Sistema de Reservas */

.cabanas-container {

max-width: 600px;

margin: 0 auto;

padding: 20px;

font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;

background: #ffffff;

border-radius: 12px;

box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);

}

.cabanas-header {

text-align: center;

margin-bottom: 30px;

}

.cabanas-title {

font-size: 2rem;

font-weight: bold;

color: #1f2937;

margin: 0 0 10px 0;

}

.cabanas-subtitle {

font-size: 1.1rem;

color: #6b7280;

margin: 0 0 5px 0;

}

.cabanas-info {

font-size: 0.9rem;

color: #9ca3af;

margin: 0;

}

.cabanas-form-container {

background: linear-gradient(135deg, #dbeafe 0%, #e0e7ff 100%);

padding: 30px;

border-radius: 12px;

margin-bottom: 30px;

}

.cabanas-form-title {

font-size: 1.3rem;

font-weight: 600;

color: #1f2937;

margin: 0 0 25px 0;

display: flex;

align-items: center;

}

.cabanas-form {

display: flex;

flex-direction: column;

gap: 20px;

}

.cabanas-dates-row {

display: grid;

grid-template-columns: 1fr 1fr;

gap: 15px;

}

@media (max-width: 640px) {

.cabanas-dates-row {

grid-template-columns: 1fr;

}

}

.cabanas-field {

display: flex;

flex-direction: column;

gap: 8px;

}

.cabanas-field label {

font-size: 0.9rem;

font-weight: 500;

color: #374151;

}

.cabanas-field input,

.cabanas-field select {

padding: 12px 16px;

border: 2px solid #e5e7eb;

border-radius: 8px;

font-size: 1rem;

font-family: inherit;

transition: all 0.2s ease;

background: #ffffff;

}

.cabanas-field input:focus,

.cabanas-field select:focus {

outline: none;

border-color: #3b82f6;

box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);

}

.cabanas-field small {

font-size: 0.8rem;

color: #6b7280;

margin-top: 4px;

}

.cabanas-submit-btn {

background: linear-gradient(135deg, #3b82f6 0%, #1e40af 100%);

color: white;

border: none;

border-radius: 8px;

padding: 16px 24px;

font-size: 1rem;

font-weight: 600;

cursor: pointer;

transition: all 0.2s ease;

display: flex;

align-items: center;

justify-content: center;

gap: 8px;

margin-top: 10px;

}

.cabanas-submit-btn:hover {

transform: translateY(-2px);

box-shadow: 0 8px 25px rgba(59, 130, 246, 0.3);

}

.cabanas-submit-btn:active {

transform: translateY(0);

}

.cabanas-submit-btn:disabled {

background: #9ca3af;

cursor: not-allowed;

transform: none;

box-shadow: none;

}

.cabanas-result {

background: #ffffff;

border-radius: 12px;

padding: 30px;

border: 1px solid #e5e7eb;

margin-top: 20px;

}

.cabanas-success {

border-color: #10b981;

background: linear-gradient(135deg, #f0fdfa 0%, #ecfdf5 100%);

}

.cabanas-error {

border-color: #ef4444;

background: linear-gradient(135deg, #fef2f2 0%, #fee2e2 100%);

}

.result-header {

display: flex;

align-items: center;

gap: 12px;

margin-bottom: 20px;

}

.result-icon {

font-size: 2rem;

}

.result-header h3 {

margin: 0;

font-size: 1.5rem;

color: #1f2937;

}

.result-summary {

background: #ffffff;

border-radius: 8px;

padding: 20px;

margin-bottom: 20px;

border: 1px solid #e5e7eb;

}

.result-summary div {

display: flex;

justify-content: space-between;

margin-bottom: 8px;

font-size: 0.95rem;

}

.price-breakdown {

background: #f9fafb;

border-radius: 8px;

padding: 15px;

margin: 15px 0;

}

.price-breakdown h4 {

margin: 0 0 10px 0;

font-size: 1rem;

color: #374151;

}

.breakdown-day {

display: flex;

justify-content: space-between;

align-items: center;

padding: 8px 0;

border-bottom: 1px solid #e5e7eb;

font-size: 0.9rem;

}

.breakdown-day:last-child {

border-bottom: none;

}

.breakdown-date {

font-weight: 500;

}

.breakdown-type {

color: #6b7280;

font-size: 0.8rem;

}

.breakdown-price {

font-weight: 600;

color: #1f2937;

}

.total-price {

background: linear-gradient(135deg, #8b5cf6 0%, #7c3aed 50%, #6d28d9 100%);

color: white;

padding: 15px;

border-radius: 8px;

text-align: center;

font-size: 1.2rem;

margin: 20px 0;

box-shadow: 0 4px 15px rgba(139, 92, 246, 0.3);

border: 1px solid rgba(255, 255, 255, 0.2);

}

.action-buttons {

display: flex;

gap: 12px;

margin-top: 20px;

}

.btn-reserve,

.btn-info {

padding: 12px 24px;

border: none;

border-radius: 6px;

font-size: 0.9rem;

font-weight: 600;

cursor: pointer;

transition: all 0.2s ease;

text-decoration: none;

display: inline-block;

text-align: center;

flex: 1;

}

.btn-reserve {

background: linear-gradient(135deg, #10b981 0%, #059669 100%);

color: white;

}

.btn-reserve:hover {

transform: translateY(-1px);

box-shadow: 0 4px 12px rgba(16, 185, 129, 0.3);

}

.btn-share-whatsapp:hover {

transform: translateY(-1px);

box-shadow: 0 8px 25px rgba(37, 211, 102, 0.6);

background: linear-gradient(135deg, #1cb55c 0%, #128c43 50%, #0f7c3c 100%);

}

.btn-generate-image:hover {

transform: translateY(-1px);

box-shadow: 0 8px 25px rgba(245, 158, 11, 0.6);

background: linear-gradient(135deg, #d97706 0%, #b45309 50%, #92400e 100%);

}

.btn-info {

background: #ffffff;

color: #3b82f6;

border: 2px solid #3b82f6;

}

.btn-info:hover {

background: #3b82f6;

color: white;

}

.suggestion {

background: #fffbeb;

border: 1px solid #fbbf24;

border-radius: 8px;

padding: 15px;

margin-top: 15px;

}

.suggestion p {

margin: 0;

font-size: 0.9rem;

color: #92400e;

}

.cabanas-info-section {

background: #f9fafb;

border-radius: 12px;

padding: 25px;

}

.cabanas-info-section h3 {

margin: 0 0 20px 0;

color: #1f2937;

}

.cabanas-info-grid {

display: grid;

grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));

gap: 15px;

margin-bottom: 20px;

}

.cabin-card {

background: #ffffff;

border-radius: 8px;

padding: 20px;

border: 1px solid #e5e7eb;

}

.cabin-card h4 {

margin: 0 0 10px 0;

color: #1f2937;

font-size: 1.1rem;

}

.cabin-card p {

margin: 0;

color: #6b7280;

font-size: 0.9rem;

}

.assignment-note {

text-align: center;

font-style: italic;

color: #6b7280;

font-size: 0.9rem;

margin-bottom: 20px;

}

.contact-info {

background: #ffffff;

border-radius: 8px;

padding: 20px;

border: 1px solid #e5e7eb;

}

.contact-info h4 {

margin: 0 0 15px 0;

color: #1f2937;

}

.contact-info p {

margin: 8px 0;

font-size: 0.9rem;

color: #374151;

}

.contact-info a {

color: #3b82f6;

text-decoration: none;

font-weight: 500;

}

.contact-info a:hover {

text-decoration: underline;

}

.result-message {

font-size: 1rem;

color: #374151;

margin-bottom: 15px;

line-height: 1.5;

}

.available-cabins {

background: #f0fdfa;

border-radius: 8px;

padding: 15px;

margin: 15px 0;

border: 1px solid #10b981;

}

.available-cabins h4 {

margin: 0 0 12px 0;

font-size: 1rem;

color: #065f46;

}

.cabins-list {

display: flex;

flex-direction: column;

gap: 8px;

}

.available-cabin {

display: flex;

align-items: center;

gap: 10px;

padding: 8px 12px;

background: #ffffff;

border-radius: 6px;

border: 1px solid #d1fae5;

}

.cabin-emoji {

font-size: 1.2rem;

}

.cabin-name {

font-weight: 500;

color: #065f46;

flex: 1;

}

/* Colores espec�ficos para cada caba�a */

.cabin-lomas-apart {

color: #10B981 !important; /* Verde para Lomas Apart */

}

.cabin-uritorco {

color: #8B5CF6 !important; /* Violeta para Uritorco */

}

.cabin-capacity {

font-size: 0.8rem;

color: #059669;

background: #ecfdf5;

padding: 2px 8px;

border-radius: 4px;

}

.cabin-details {

flex: 1;

display: flex;

flex-direction: column;

gap: 4px;

}

.cabin-calendar-info small {

color: #9ca3af;

font-size: 0.7rem;

}

.advance-payment {

background: #fef3c7;

border: 1px solid #f59e0b;

border-radius: 8px;

padding: 15px;

margin: 15px 0;

text-align: center;

}

.advance-payment h4 {

margin: 0 0 8px 0;

color: #92400e;

}

.advance-payment p {

margin: 0;

font-size: 0.9rem;

color: #92400e;

}

.btn-mercadopago {

background: linear-gradient(135deg, #009ee3 0%, #0084d4 100%) !important;

color: white !important;

}

.btn-mercadopago:hover {

background: linear-gradient(135deg, #0084d4 0%, #006bb3 100%) !important;

transform: translateY(-1px);

box-shadow: 0 4px 12px rgba(0, 158, 227, 0.3);

}

.action-buttons {

display: grid;

grid-template-columns: 1fr;

gap: 12px;

margin-top: 20px;

}

@media (min-width: 640px) {

.action-buttons {

grid-template-columns: 1fr 1fr 1fr;

}

}

@media (max-width: 640px) {

.cabanas-container {

padding: 15px;

}

.cabanas-form-container {

padding: 20px;

}

.action-buttons {

flex-direction: column;

}

}

/* Estilos para secci�n de descuentos */

.cabanas-discount-section {

background: linear-gradient(135deg, #fef3c7 0%, #fde68a 100%);

border: 2px solid #f59e0b;

border-radius: 12px;

padding: 20px;

margin-top: 20px;

}

.cabanas-discount-section h4 {

margin: 0 0 15px 0;

color: #92400e;

font-size: 1.1rem;

font-weight: 600;

}

.discount-input-row {

display: flex;

gap: 10px;

margin-bottom: 10px;

}

.discount-input {

flex: 1;

padding: 12px 16px;

border: 2px solid #f59e0b;

border-radius: 8px;

font-size: 1rem;

background: #ffffff;

color: #92400e;

}

.discount-input:focus {

outline: none;

border-color: #d97706;

box-shadow: 0 0 0 3px rgba(217, 119, 6, 0.1);

}

.discount-apply-btn {

background: linear-gradient(135deg, #f59e0b 0%, #d97706 100%);

color: white;

border: none;

border-radius: 8px;

padding: 12px 20px;

font-size: 1rem;

font-weight: 600;

cursor: pointer;

transition: all 0.2s ease;

white-space: nowrap;

}

.discount-apply-btn:hover {

background: linear-gradient(135deg, #d97706 0%, #b45309 100%);

transform: translateY(-1px);

box-shadow: 0 4px 12px rgba(245, 158, 11, 0.3);

}

.discount-apply-btn:disabled {

background: #9ca3af;

cursor: not-allowed;

transform: none;

box-shadow: none;

}

.discount-result {

padding: 12px 16px;

border-radius: 8px;

font-weight: 600;

margin-top: 10px;

}

.discount-success {

background: #d1fae5;

color: #065f46;

border: 2px solid #10b981;

}

.discount-error {

background: #fee2e2;

color: #991b1b;

border: 2px solid #ef4444;

}

@media (max-width: 640px) {

.discount-input-row {

flex-direction: column;

}

.discount-apply-btn {

padding: 14px 20px;

}

}

</style>

</head>

<body>

<div class="cabanas-container">

<div class="cabanas-header">

<h1 class="cabanas-title">??? Caba�as Lomas del Uritorco</h1>

<p class="cabanas-subtitle">Capilla del Monte - C�rdoba</p>

<p class="cabanas-info">? Escapada perfecta en las sierras cordobesas</p>

</div>

<div class="cabanas-form-container">

<h3 class="cabanas-form-title">?? Consulta tu reserva</h3>

<form id="cabanas-booking-form" class="cabanas-form">

<div class="cabanas-dates-row">

<div class="cabanas-field">

<label for="check_in">Fecha de llegada</label>

<input type="date" id="check_in" name="check_in" required>

<small>D�a que lleg�s a la caba�a</small>

</div>

<div class="cabanas-field">

<label for="check_out">Fecha de salida</label>

<input type="date" id="check_out" name="check_out" required>

<small>D�a que dej�s libre la caba�a</small>

</div>

</div>

<div class="cabanas-field">

<label for="guests">?? Cantidad de hu�spedes</label>

<select id="guests" name="guests" required>

<option value="1">1 adulto</option>

<option value="2">2 adultos</option>

<option value="3" selected>3 adultos</option>

<option value="4">4 adultos</option>

<option value="5">5 adultos</option>

<option value="6">6 adultos</option>

</select>

<small>Mencione la cantidad de adultos</small>

</div>

<div class="cabanas-field">

<label for="minors">?? �Trae menores?</label>

<select id="minors" name="minors">

<option value="no">No</option>

<option value="1">1 menor</option>

<option value="2">2 menores</option>

<option value="3">3 menores</option>

</select>

<small>Menores de 12 a�os - 50% de descuento por menor</small>

</div>

<div class="cabanas-field">

<label for="pets">?? �Trae mascotas?</label>

<select id="pets" name="pets">

<option value="no">No</option>

<option value="yes">S�</option>

</select>

<small>Consultar pol�ticas de mascotas (opcional)</small>

</div>

<button type="submit" id="check-availability-btn" class="cabanas-submit-btn">

<span class="btn-text">🔍 Consultar Disponibilidad</span>

<span class="btn-loading" style="display:none;">⏳</span>

</button>

</form>

</div>

</div>

<!-- Todo el JS debe ir dentro del bloque <script> al final del archivo -->

<h4>??? �Tienes un C�digo de Descuento?</h4>

<div class="discount-input-row">

<input type="text" id="discount_code" name="discount_code" placeholder="Ej: PRIMERAVEZ10, VERANO20" class="discount-input">

<button type="button" id="apply-discount-btn" class="discount-apply-btn">

<span class="btn-text">Aplicar</span>

<span class="btn-loading" style="display: none;">?</span>

</button>

</div>

<div id="discount-result" class="discount-result" style="display: none;"></div>

</div>

</div>

<div id="availability-result" class="cabanas-result" style="display: none;"></div>

</div>

<script>

// Configuraci�n completa STANDALONE - Con calendarios por caba�a

const PRICING_CONFIG = {

// Paridad USD/ARS (configurable desde admin)

usd_to_ars_rate: 1300, // Valor por defecto, se puede modificar desde admin

// C�DIGOS DE DESCUENTO CONFIGURABLES DESDE ADMIN

discount_codes: {

// Descuentos por porcentaje

'PRIMERAVEZ10': {

type: 'percentage',

value: 10,

admin_reason: 'Descuento primera vez',

client_display: 'Descuento especial',

active: true,

min_nights: 1,

max_uses: null,

valid_from: null,

valid_until: null

},

'VERANO15': {

type: 'percentage',

value: 15,

admin_reason: 'Promoci�n temporada verano',

client_display: 'Descuento especial',

active: true,

min_nights: 3,

max_uses: 50,

valid_from: '2025-12-01',

valid_until: '2026-03-31'

},

'FINDESEMANA20': {

type: 'percentage',

value: 20,

admin_reason: 'Descuento fin de semana para ocupar',

client_display: 'Descuento especial',

active: true,

min_nights: 2,

max_uses: null,

valid_from: null,

valid_until: null

},

// Descuentos por monto fijo

'FIJO500': {

type: 'fixed_ars',

value: 500,

admin_reason: 'Descuento fijo cliente frecuente',

client_display: 'Descuento especial',

active: true,

min_nights: 1,

max_uses: null,

valid_from: null,

valid_until: null

},

'FIJO20USD': {

type: 'fixed_usd',

value: 20,

admin_reason: 'Descuento fijo en USD para extranjeros',

client_display: 'Descuento especial',

active: true,

min_nights: 2,

max_uses: null,

valid_from: null,

valid_until: null

},

// Descuentos especiales

'CLIENTEVIP': {

type: 'percentage',

value: 25,

admin_reason: 'Cliente VIP - descuento especial',

client_display: 'Descuento especial',

active: true,

min_nights: 1,

max_uses: null,

valid_from: null,

valid_until: null

},

'ULTIMAHORA': {

type: 'percentage',

value: 30,

admin_reason: 'Descuento �ltima hora - ocupar caba�a',

client_display: 'Descuento especial',

active: false, // Se activa manualmente

min_nights: 1,

max_uses: 5,

valid_from: null,

valid_until: null

},

// C�DIGOS ESPEC�FICOS PARA FINES DE SEMANA LARGOS

'AGOSTO2NOCHES': {

type: 'percentage',

value: 15,

admin_reason: 'Compensaci�n por 2 noches en fin de semana largo agosto (15-19/8)',

client_display: 'Descuento especial',

active: true,

min_nights: 2,

max_nights: 2, // Solo para exactly 2 noches

date_restrictions: {

valid_date_ranges: [

{ start: '2025-08-15', end: '2025-08-19' }

]

},

special_pricing: {

charge_as_minimum_nights: 3, // Cobra como si fueran 3 noches

message: 'Fin de semana largo: se cobra como m�nimo 3 noches o aplic� descuento por 2 noches'

}

},

'FINDELARGO3': {

type: 'percentage',

value: 5,

admin_reason: 'Incentivo para quedarse 3+ noches en fines de semana largos',

client_display: 'Descuento especial',

active: true,

min_nights: 3,

date_restrictions: {

long_weekends_only: true

}

},

'COMPENSACION2NOCHES': {

type: 'fixed_percentage_or_charge_as_minimum',

value: 20,

admin_reason: 'Compensaci�n por estad�a corta en per�odo de alta demanda',

client_display: 'Descuento especial',

active: true,

min_nights: 1,

max_nights: 2,

special_logic: {

strategy: 'choice',

option_1: {

type: 'percentage_discount',

value: 20,

message: 'Descuento del 20% por estad�a corta'

},

option_2: {

type: 'charge_as_minimum',

minimum_nights: 3,

message: 'Se cobra como m�nimo 3 noches'

}

}

}

},

// CALENDARIO DE FERIADOS ARGENTINOS (REAL)

// Utilizando el calendario oficial de Google para Argentina

holidays_calendar: {

name: 'Feriados Argentinos (Google Calendar)',

ical_url: 'https://calendar.google.com/calendar/ical/es.ar%23holiday%40group.v.calendar.google.com/public/basic.ics',

timezone: 'America/Argentina/Cordoba'

}, // SITUACI�N REAL EXACTA (22/7/2025):

//

// ??? CABA�A LOMAS APART (VERDE):

// - Booking.com: "closed - not available" del 21/7 al 2/8 (checkout 2/8 = libera ma�ana 2/8)

// - Noches ocupadas: 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 1/8

// - Estado: NO DISPONIBLE hasta el 2/8

//

// ?? CABA�A URITORCO (VIOLETA):

// - Silvia: 23-28/7 (checkout 28/7 = libera ma�ana 28/7)

// - Noches ocupadas por Silvia: 23, 24, 25, 26, 27/7

// - Flia Delgado: 30/7-4/8 (checkout 4/8 = libera ma�ana 4/8)

// - Noches ocupadas por Flia Delgado: 30, 31/7, 1, 2, 3/8

// - SOLO DISPONIBLE: 28/7 y 29/7 (2 noches libres entre reservas)

// - Estado: DISPONIBLE solo para estancias que no incluyan fechas ocupadas

//

// EJEMPLOS DE RESERVAS:

// ? 28/7-30/7 (2 noches: 28, 29) = DISPONIBLE

// ? 30/7-31/7 (1 noche: 30) = NO DISPONIBLE (Flia Delgado ya est�)

// ? 29/7-31/7 (2 noches: 29, 30) = NO DISPONIBLE (30/7 ocupada)

// ? 4/8-6/8 (2 noches: 4, 5) = DISPONIBLE (despu�s del checkout de Flia Delgado)

//

// RESULTADO: Solo Caba�a Uritorco disponible para fechas espec�ficas (28-29/7)

// CALENDARIOS REALES CONFIGURADOS:

// - Google Calendar: Reservas directas de cada caba�a

// - Booking.com: Reservas sincronizadas autom�ticamente desde Booking.com

// - Manual blocks: Fechas de mantenimiento o bloqueos especiales

// Configuraci�n de caba�as individuales con Google Calendar + Booking.com

cabins: {

'lomas_apart': {

name: 'Caba�a Lomas Apart',

emoji: '???',

color: '#10B981', // Verde para Lomas Apart

max_guests: 3, // M�ximo 3 personas

// Google Calendar principal de Lomas Apart

google_calendar_id: '[email protected]',

google_calendar_url: 'https://calendar.google.com/calendar/ical/mmonsdcsflpe9p1igm08klllh8%40group.calendar.google.com/public/basic.ics',

// Booking.com calendar para Lomas Apart (REAL - sincronizado autom�ticamente)

booking_calendar_id: '[email protected]', // ID real de Booking

booking_calendar_url: 'https://ical.booking.com/v1/export?t=b9307149-21c2-4e9d-a33a-47fa20f6a5b6', // URL real iCal de Booking para Lomas Apart

// Fechas bloqueadas manualmente (mantenimiento, etc.)

manual_blocks: [

// BOOKING.COM REAL: "closed - not available" del 21/7 al 2/8 (checkout 2/8)

'2025-07-21', '2025-07-22', '2025-07-23', '2025-07-24', '2025-07-25',

'2025-07-26', '2025-07-27', '2025-07-28', '2025-07-29', '2025-07-30',

'2025-07-31', '2025-08-01', // Ocupada hasta el 2/8

// Reservas futuras

'2025-08-15', '2025-08-16', '2025-08-17',

'2025-09-15', '2025-09-16',

'2025-09-25', '2025-09-26', '2025-09-27'

]

},

'uritorco': {

name: 'Caba�a Uritorco',

emoji: '??',

color: '#8B5CF6', // Violeta para Uritorco

max_guests: 3,

// Google Calendar principal de Caba�a Uritorco

google_calendar_id: '[email protected]',

google_calendar_url: 'https://calendar.google.com/calendar/ical/em4ht6t4uqlr4edra90bsrqkls%40group.calendar.google.com/public/basic.ics',

// Booking.com calendar para Uritorco (REAL - sincronizado autom�ticamente)

booking_calendar_id: '[email protected]', // ID real de Booking

booking_calendar_url: 'https://ical.booking.com/v1/export?t=1d615de0-eb38-481c-bc35-4a80a09ccd9c', // URL real iCal de Booking para Uritorco

manual_blocks: [

// SILVIA: 23-28/7 (checkout 28/7 = libera ma�ana 28/7)

'2025-07-23', '2025-07-24', '2025-07-25', '2025-07-26', '2025-07-27',

// FLIA DELGADO: 30/7-4/8 (checkout 4/8 = libera ma�ana 4/8)

// ?? CR�TICO: Estas noches est�n ocupadas

'2025-07-30', '2025-07-31', '2025-08-01', '2025-08-02', '2025-08-03',

// Reservas futuras

'2025-08-10', '2025-08-11', '2025-08-12',

'2025-08-20', '2025-08-21', '2025-08-22',

'2025-10-05', '2025-10-06',

'2025-12-28', '2025-12-29', '2025-12-30'

]

}

},

// Temporadas globales seg�n vacaciones argentinas (precios base en USD, se convierten a ARS)

seasons: [

{

name: 'Temporada Alta - Vacaciones de Verano',

end: '2025-02-28',

prices_usd: { 1: 50, 2: 60, 3: 80, 4: 90 }, // Precios base en USD

weekend_surcharge_usd: 20,

long_weekend_surcharge_usd: 30

},

{

name: 'Temporada Normal',

start: '2025-03-01',

end: '2025-06-30',

prices_usd: { 1: 35, 2: 40, 3: 55, 4: 65 },

weekend_surcharge_usd: 10,

long_weekend_surcharge_usd: 15

},

{

name: 'Temporada Alta - Vacaciones de Invierno',

start: '2025-07-01',

end: '2025-07-31',

prices_usd: { 1: 45, 2: 55, 3: 75, 4: 85 },

weekend_surcharge_usd: 15,

long_weekend_surcharge_usd: 25

},

{

name: 'Temporada Normal',

start: '2025-08-01',

end: '2025-12-31',

prices_usd: { 1: 35, 2: 40, 3: 55, 4: 65 },

weekend_surcharge_usd: 10,

long_weekend_surcharge_usd: 15

}

],

// Feriados argentinos (FALLBACK - se actualizan autom�ticamente desde Google Calendar)

holidays: [

'2025-07-09', // D�a de la Independencia (ya pas�)

'2025-08-17', // Paso a la Inmortalidad del General San Mart�n (domingo)

'2025-08-18', // Feriado puente (lunes)

'2025-10-12', // D�a del Respeto a la Diversidad Cultural

'2025-10-13', // Feriado puente (lunes)

'2025-11-20', // D�a de la Soberan�a Nacional

'2025-12-08', // Inmaculada Concepci�n de Mar�a

'2025-12-25', // Navidad

'2026-01-01', // A�o Nuevo

'2026-02-24', '2026-02-25' // Carnaval

],

// Cache para feriados obtenidos del calendario real

holidays_cache: {

data: [],

last_updated: null,

expiry_hours: 24 // Cache por 24 horas

},

// Sistema de c�digos de descuento

discount_codes: {

// C�digos porcentuales

'PRIMERAVEZ10': {

type: 'percentage',

value: 10,

description: 'Primera vez - 10% de descuento',

min_nights: 2,

max_uses: 100,

used_count: 0,

valid_until: '2025-12-31',

active: true

},

'VERANO20': {

type: 'percentage',

value: 20,

description: 'Promoci�n verano - 20% de descuento',

min_nights: 3,

max_uses: 50,

used_count: 0,

valid_until: '2025-02-28',

active: true

},

'INVIERNO15': {

type: 'percentage',

value: 15,

description: 'Promoci�n invierno - 15% de descuento',

min_nights: 2,

max_uses: 75,

used_count: 0,

valid_until: '2025-07-31',

active: true

},

// C�digos de descuento fijo

'DESCUENTO5000': {

type: 'fixed_ars',

value: 5000,

description: 'Descuento fijo $5.000 ARS',

min_nights: 2,

max_uses: 25,

used_count: 0,

valid_until: '2025-12-31',

active: true

},

'ULTIMOMOMENTO': {

type: 'percentage',

value: 25,

description: '�ltimo momento - 25% de descuento',

min_nights: 1,

max_uses: 20,

used_count: 0,

valid_until: '2025-12-31',

active: true,

conditions: {

advance_booking_max_days: 7 // Solo v�lido si se reserva con menos de 7 d�as de anticipaci�n

}

}

}

};

// Funciones para manejo de monedas

function convertUSDtoARS(usdAmount) {

return Math.round(usdAmount * PRICING_CONFIG.usd_to_ars_rate);

}

function formatPrice(usdAmount) {

const arsAmount = convertUSDtoARS(usdAmount);

return {

usd: usdAmount,

ars: arsAmount,

formatted: `$${arsAmount.toLocaleString('es-AR')} ARS`

};

}

// Funci�n para obtener fechas bloqueadas desde Google Calendar + Booking.com por caba�a (INTEGRACI�N REAL)

async function fetchCabinBlockedDates(cabinId) {

const cabin = PRICING_CONFIG.cabins[cabinId];

if (!cabin) return [];

try {

const blockedDates = [...cabin.manual_blocks]; // Empezar con fechas manuales

// Consultar Google Calendar principal (REAL)

if (cabin.google_calendar_url) {

const googleDates = await fetchRealCalendarData(cabin.google_calendar_url, cabin.name, 'Google Calendar');

blockedDates.push(...googleDates);

}

// Consultar Booking.com Calendar (REAL - iCal desde Booking.com)

if (cabin.booking_calendar_url) {

const bookingDates = await fetchRealCalendarData(cabin.booking_calendar_url, cabin.name, 'Booking.com');

blockedDates.push(...bookingDates);

}

// Remover duplicados

const uniqueBlockedDates = [...new Set(blockedDates)];

return uniqueBlockedDates;

} catch (error) {

return cabin.manual_blocks; // Fallback a fechas manuales

}

}

// INTEGRACI�N REAL CON CALENDARIO DE FERIADOS ARGENTINOS (LIMITADA A 6 MESES)

async function fetchArgentineHolidays(startDate = null, endDate = null) {

try {

// LIMITAR B�SQUEDA A 6 MESES M�XIMO

const now = new Date();

const maxDate = new Date(now);

maxDate.setMonth(now.getMonth() + 6);

// Si no se especifican fechas, usar desde hoy hasta 6 meses

if (!startDate) {

startDate = now.toISOString().split('T')[0];

}

if (!endDate) {

endDate = maxDate.toISOString().split('T')[0];

}

// Forzar l�mite de 6 meses m�ximo

const requestedEndDate = new Date(endDate);

if (requestedEndDate > maxDate) {

endDate = maxDate.toISOString().split('T')[0];

}

// Verificar cache primero

const cache = PRICING_CONFIG.holidays_cache;

if (cache.data.length > 0 && cache.last_updated) {

const hoursAgo = (now - new Date(cache.last_updated)) / (1000 60 60);

if (hoursAgo < cache.expiry_hours && cache.period) {

// Verificar si el cache cubre el per�odo solicitado

if (cache.period.startDate <= startDate && cache.period.endDate >= endDate) {

const relevantHolidays = cache.data.filter(holiday =>

holiday >= startDate && holiday <= endDate

);

return relevantHolidays;

}

}

}

// Usar fallback hardcoded en lugar de consultar APIs externas

// Filtrar feriados hardcoded para el per�odo limitado

const relevantHolidays = PRICING_CONFIG.holidays.filter(holiday => {

return holiday >= startDate && holiday <= endDate;

});

// Actualizar cache con per�odo limitado

cache.data = relevantHolidays;

cache.last_updated = now.toISOString();

cache.period = { startDate, endDate };

return relevantHolidays;

} catch (error) {

// Aplicar l�mite de 6 meses al fallback tambi�n

const now = new Date();

const maxDate = new Date(now);

maxDate.setMonth(now.getMonth() + 6);

const maxDateStr = maxDate.toISOString().split('T')[0];

const limitedFallback = PRICING_CONFIG.holidays.filter(holiday =>

holiday >= (startDate || now.toISOString().split('T')[0]) &&

holiday <= (endDate || maxDateStr)

);

return limitedFallback;

}

}

// INTEGRACI�N REAL CON CALENDARIOS - NO SIMULACI�N (CON TIMEOUT)

// Esta funci�n hace fetch REAL a los calendarios iCal

async function fetchRealCalendarData(icalUrl, cabinName, calendarType = 'Google Calendar') {

try {

// CORS Proxy para poder acceder a los calendarios desde el navegador

// En producci�n, esto deber�a manejarse desde el backend

const proxyUrl = `https://api.allorigins.win/raw?url=${encodeURIComponent(icalUrl)}`;

// Agregar timeout de 5 segundos para evitar demoras

const controller = new AbortController();

const timeoutId = setTimeout(() => controller.abort(), 5000); // 5 segundos

const response = await fetch(proxyUrl, {

signal: controller.signal

});

clearTimeout(timeoutId); // Limpiar timeout si la respuesta llega a tiempo

if (!response.ok) {

throw new Error(`HTTP error! status: ${response.status}`);

}

const icalData = await response.text();

// Parser b�sico de iCal para extraer fechas bloqueadas

const blockedDates = parseICalData(icalData);

return blockedDates;

} catch (error) {

// FALLBACK: Si falla la consulta real, usar datos simulados como backup

return await simulateGoogleCalendarFetch(icalUrl, cabinName, calendarType);

}

}

// Parser de datos iCal para extraer fechas de eventos/reservas

function parseICalData(icalData) {

const blockedDates = [];

const lines = icalData.split('\n');

let currentEvent = {};

let inEvent = false;

for (let line of lines) {

line = line.trim();

if (line === 'BEGIN:VEVENT') {

inEvent = true;

currentEvent = {};

} else if (line === 'END:VEVENT' && inEvent) {

// Procesar evento completado

if (currentEvent.dtstart && currentEvent.dtend) {

const startDate = parseICalDate(currentEvent.dtstart);

const endDate = parseICalDate(currentEvent.dtend);

if (startDate && endDate) {

// Generar todas las fechas entre start y end (excluyendo end como checkout)

const dateRange = getDateRange(startDate, endDate);

blockedDates.push(...dateRange);

}

}

inEvent = false;

} else if (inEvent) {

// Extraer propiedades del evento

if (line.startsWith('DTSTART')) {

currentEvent.dtstart = line.split(':')[1];

} else if (line.startsWith('DTEND')) {

currentEvent.dtend = line.split(':')[1];

} else if (line.startsWith('SUMMARY')) {

currentEvent.summary = line.split(':')[1];

}

}

}

// Remover duplicados y ordenar

return [...new Set(blockedDates)].sort();

}

// Parser de fechas iCal (maneja diferentes formatos)

function parseICalDate(icalDate) {

if (!icalDate) return null;

// Formato t�pico: 20250721T000000Z o 20250721

let dateStr = icalDate.replace(/[TZ]/g, '').substring(0, 8);

if (dateStr.length === 8) {

// Convertir YYYYMMDD a YYYY-MM-DD

const year = dateStr.substring(0, 4);

const month = dateStr.substring(4, 6);

const day = dateStr.substring(6, 8);

return `${year}-${month}-${day}`;

}

return null;

}

async function simulateGoogleCalendarFetch(icalUrl, cabinName, calendarType = 'Google Calendar') {

return new Promise((resolve) => {

setTimeout(() => {

// SITUACI�N REAL ACTUAL (22/7/2025):

// - Lomas Apart: ESTAD�A EN CURSO (verde) - bloqueada actualmente

// - Uritorco: DISPONIBLE (violeta) hasta el 30/7 cuando entra Flia Delgado

if (cabinName.includes('Lomas Apart')) {

if (calendarType === 'Google Calendar') {

// LOMAS APART: Reservas directas de Google Calendar

resolve([

// Reservas futuras de Google Calendar (despu�s de la estad�a de Booking.com)

'2025-08-15', '2025-08-16', '2025-08-17', // Reserva directa agosto

'2025-09-15', '2025-09-16', // Septiembre

'2025-12-20', '2025-12-21', '2025-12-22' // Diciembre

]);

} else if (calendarType === 'Booking.com') {

// BOOKING.COM REAL: "closed - not available" del 21/7 al 2/8 (checkout 2/8 = libera ma�ana 2/8)

// Las noches ocupadas son: 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 1 (hasta checkout 2/8)

resolve([

'2025-07-21', '2025-07-22', '2025-07-23', '2025-07-24', '2025-07-25',

'2025-07-26', '2025-07-27', '2025-07-28', '2025-07-29', '2025-07-30',

'2025-07-31', '2025-08-01', // Booking.com: 21/7 al 2/8 (checkout 2/8)

// Otras reservas de Booking.com futuras

'2025-09-25', '2025-09-26', '2025-09-27', // Septiembre Booking

'2025-11-10', '2025-11-11' // Noviembre Booking

]);

}

} else if (cabinName.includes('Uritorco')) {

if (calendarType === 'Google Calendar') {

// CABA�A URITORCO: Reservas directas del Google Calendar

resolve([

// SILVIA: 23-28/7 (checkout 28/7 = libera ma�ana 28/7)

// Noches ocupadas: 23, 24, 25, 26, 27

'2025-07-23', '2025-07-24', '2025-07-25', '2025-07-26', '2025-07-27', // Silvia: 23-28/7

// FLIA DELGADO: 30/7-4/8 (checkout 4/8 = libera ma�ana 4/8)

// ?? CR�TICO: Noches ocupadas son 30, 31/7 y 1, 2, 3/8

// CUALQUIER reserva que incluya estas noches ser� RECHAZADA

'2025-07-30', '2025-07-31', '2025-08-01', '2025-08-02', '2025-08-03', // Flia Delgado: 30/7-4/8

// RESULTADO REAL: Solo 28/7 y 29/7 est�n libres entre reservas

// Una reserva 30/7-31/7 ser� RECHAZADA porque 30/7 est� ocupado por Flia Delgado

// Reservas futuras

'2025-08-10', '2025-08-11', '2025-08-12', // Agosto adicional

'2025-10-05', '2025-10-06', // Octubre

'2025-12-28', '2025-12-29', '2025-12-30' // Diciembre

]);

} else if (calendarType === 'Booking.com') {

// Reservas desde Booking.com sincronizadas para Uritorco

resolve([

'2025-08-20', '2025-08-21', '2025-08-22', // Reserva Booking agosto

'2025-09-05', '2025-09-06', '2025-09-07', // Septiembre Booking

'2025-10-15', '2025-10-16', '2025-10-17', // Octubre Booking

'2025-11-20', '2025-11-21' // Noviembre Booking

]);

}

}

resolve([]);

}, Math.random() * 500 + 200); // Simular latencia del calendario

});

}

// Funci�n para sugerir fechas alternativas cuando no hay disponibilidad

async function suggestAlternativeDates(originalCheckIn, originalCheckOut, totalGuests) {

const suggestions = [];

const nights = Math.ceil((new Date(originalCheckOut) - new Date(originalCheckIn)) / (1000 60 60 * 24));

// Buscar en los pr�ximos 60 d�as

const startSearch = new Date();

const endSearch = new Date();

endSearch.setDate(startSearch.getDate() + 60);

let currentDate = new Date(startSearch);

while (currentDate <= endSearch && suggestions.length < 3) {

const checkInStr = currentDate.toISOString().split('T')[0];

const checkOutDate = new Date(currentDate);

checkOutDate.setDate(currentDate.getDate() + nights);

const checkOutStr = checkOutDate.toISOString().split('T')[0];

// No sugerir la misma fecha que ya prob�

if (checkInStr !== originalCheckIn) {

const availability = await checkAllCabinsAvailability(checkInStr, checkOutStr, totalGuests);

if (availability.availableCabins.length > 0) {

const pricing = await calculatePricing(checkInStr, checkOutStr, totalGuests);

let accommodationType = '';

if (availability.isMultipleCabins) {

accommodationType = ` (${availability.availableCabins.length} caba�as)`;

}

suggestions.push({

checkIn: checkInStr,

checkOut: checkOutStr,

availableCabins: availability.availableCabins.length,

totalARS: pricing.total_ars,

season: pricing.season,

accommodationType: accommodationType

});

}

}

// Avanzar al siguiente d�a

currentDate.setDate(currentDate.getDate() + 1);

}

return suggestions;

}

// Funci�n para verificar disponibilidad de todas las caba�as

async function checkAllCabinsAvailability(checkIn, checkOut, totalGuests) {

const availableCabins = [];

const unavailableReasons = [];

const cabinIds = Object.keys(PRICING_CONFIG.cabins);

// Primero verificar caba�as que pueden alojar todo el grupo

for (const cabinId of cabinIds) {

const cabin = PRICING_CONFIG.cabins[cabinId];

// Obtener fechas bloqueadas para esta caba�a espec�fica

const blockedDates = await fetchCabinBlockedDates(cabinId);

// Verificar si hay conflicto con las fechas solicitadas

const dateRange = getDateRange(checkIn, checkOut);

const conflictingDates = dateRange.filter(date => blockedDates.includes(date));

const hasConflict = conflictingDates.length > 0;

if (hasConflict) {

unavailableReasons.push(`${cabin.emoji} ${cabin.name}: Ocupada en ${conflictingDates.map(d => formatDateSpanish(d)).join(', ')}`);

} else {

// Verificar capacidad

if (totalGuests <= cabin.max_guests) {

availableCabins.push({

id: cabinId,

name: cabin.name,

emoji: cabin.emoji,

max_guests: cabin.max_guests,

blocked_dates: blockedDates,

accommodation_type: 'single_cabin',

guests_capacity: totalGuests

});

} else {

}

}

}

// Si no hay caba�as que puedan alojar todo el grupo, verificar combinaci�n de caba�as

if (availableCabins.length === 0 && totalGuests > 3) {

// Obtener todas las caba�as disponibles (sin restricci�n de capacidad)

const allAvailableCabins = [];

for (const cabinId of cabinIds) {

const cabin = PRICING_CONFIG.cabins[cabinId];

const blockedDates = await fetchCabinBlockedDates(cabinId);

const dateRange = getDateRange(checkIn, checkOut);

const conflictingDates = dateRange.filter(date => blockedDates.includes(date));

if (conflictingDates.length === 0) {

allAvailableCabins.push({

id: cabinId,

name: cabin.name,

emoji: cabin.emoji,

max_guests: cabin.max_guests,

blocked_dates: blockedDates

});

}

}

// Si hay al menos 2 caba�as disponibles, ofrecer alojamiento separado

if (allAvailableCabins.length >= 2) {

const totalCapacity = allAvailableCabins.reduce((sum, cabin) => sum + cabin.max_guests, 0);

if (totalGuests <= totalCapacity) {

// Retornar todas las caba�as disponibles con indicaci�n de alojamiento separado

return {

availableCabins: allAvailableCabins.map(cabin => ({

...cabin,

accommodation_type: 'multiple_cabins',

guests_capacity: totalGuests,

total_cabins_needed: allAvailableCabins.length

})),

unavailableReasons: [],

isMultipleCabins: true,

totalCapacity: totalCapacity

};

}

}

}

return {

availableCabins,

unavailableReasons,

isMultipleCabins: false

};

}

function findSeason(dateStr) {

const date = new Date(dateStr);

for (const season of PRICING_CONFIG.seasons) {

const startDate = new Date(season.start);

const endDate = new Date(season.end);

if (date >= startDate && date <= endDate) {

return season;

}

}

// Si no encuentra temporada, usar temporada normal como fallback

return PRICING_CONFIG.seasons[1];

}

// Funci�n para detectar d�as "sandwich" entre reservas (DESACTIVADA)

// Esta funci�n se usar� en el futuro cuando tengamos datos reales de reservas anteriores

function detectSandwichDates(blockedDates) {

// DESACTIVADO: No aplicar descuentos sandwich hasta tener datos reales

return [];

/*

const sandwichDates = [];

// Ordenar fechas bloqueadas

const sortedBlockedDates = blockedDates.sort();

// Buscar huecos de 1-2 d�as entre reservas

for (let i = 0; i < sortedBlockedDates.length - 1; i++) {

const currentDate = new Date(sortedBlockedDates[i]);

const nextDate = new Date(sortedBlockedDates[i + 1]);

// Calcular d�as entre esta fecha y la siguiente

const daysBetween = Math.ceil((nextDate - currentDate) / (1000 60 60 * 24)) - 1;

// Si hay 1 o 2 d�as libres entre reservas, son d�as sandwich

if (daysBetween > 0 && daysBetween <= 2) {

const sandwichStart = new Date(currentDate);

sandwichStart.setDate(currentDate.getDate() + 1);

for (let j = 0; j < daysBetween; j++) {

const sandwichDay = new Date(sandwichStart);

sandwichDay.setDate(sandwichStart.getDate() + j);

const sandwichDateStr = sandwichDay.toISOString().split('T')[0];

sandwichDates.push(sandwichDateStr);

}

}

}

return sandwichDates;

*/

}

// Funci�n para verificar si una fecha est� en per�odo sandwich (DESACTIVADA)

function isSandwichDate(dateStr, cabinId, allBlockedDates) {

// DESACTIVADO: No aplicar descuentos sandwich hasta tener datos reales

return false;

// const sandwichDates = detectSandwichDates(allBlockedDates);

// return sandwichDates.includes(dateStr);

}

// Funci�n para verificar si es fin de semana largo (usa feriados reales de Google Calendar - OPTIMIZADA)

async function isLongWeekend(date, realHolidays = null) {

const dateStr = date.toISOString().split('T')[0];

const dayOfWeek = date.getDay(); // 0 = Domingo, 6 = S�bado

// Obtener feriados reales si no se proporcionaron (solo del mes relevante)

let holidays = realHolidays;

if (!holidays) {

// Optimizar: solo buscar feriados del mes de la fecha consultada

const monthStart = new Date(date.getFullYear(), date.getMonth(), 1).toISOString().split('T')[0];

const monthEnd = new Date(date.getFullYear(), date.getMonth() + 1, 0).toISOString().split('T')[0];

holidays = await fetchArgentineHolidays(monthStart, monthEnd);

}

// Si es feriado oficial, aplicar tarifa de fin de semana largo

if (holidays.includes(dateStr)) {

return true;

}

// Verificar feriados puente (lunes despu�s de domingo feriado, viernes antes de domingo feriado)

if (dayOfWeek === 1) { // Lunes

const sunday = new Date(date);

sunday.setDate(date.getDate() - 1);

if (holidays.includes(sunday.toISOString().split('T')[0])) {

return true;

}

}

if (dayOfWeek === 5) { // Viernes

const sunday = new Date(date);

sunday.setDate(date.getDate() + 2);

if (holidays.includes(sunday.toISOString().split('T')[0])) {

return true;

}

}

return false;

}

// Funci�n para generar rango de fechas (CORREGIDO para evitar overbooking)

function getDateRange(startDate, endDate) {

const dates = [];

const currentDate = new Date(startDate);

const lastDate = new Date(endDate);

// IMPORTANTE: El checkout NO debe estar incluido en las fechas ocupadas

// Si checkout es 30/7, significa que liberan la caba�a el 30/7 por la ma�ana

// Por tanto, las fechas ocupadas son solo hasta el 29/7 (la �ltima noche)

while (currentDate < lastDate) {

dates.push(currentDate.toISOString().split('T')[0]);

currentDate.setDate(currentDate.getDate() + 1);

}

return dates;

}

// Funci�n para formatear fecha en espa�ol

function formatDateSpanish(dateStr) {

const date = new Date(dateStr + 'T12:00:00'); // Agregar hora para evitar problemas de zona horaria

const options = {

weekday: 'short',

day: 'numeric',

month: 'short'

};

return date.toLocaleDateString('es-AR', options);

}

// Funci�n para generar URL de MercadoPago con datos de la reserva

function generateMercadoPagoURL(data) {

// URL base de MercadoPago

const baseURL = 'https://link.mercadopago.com.ar/lomasdeluritorco';

// Preparar datos de la reserva para n8n (se pasar�n como par�metros)

const reservationData = {

// Datos de la reserva

checkin: data.check_in,

checkout: data.check_out,

totalGuests: data.totalGuests,

adults: data.adults,

minorsCount: data.minorsCount,

minors: data.minors || 'no',

pets: data.pets || 'no',

isMultipleCabins: data.isMultipleCabins || false,

nights: data.nights,

// Precios

total_ars: data.total_price_ars,

total_usd: data.total_price_usd,

anticipo_ars: data.advance_payment_ars,

anticipo_usd: data.advance_payment_usd,

// Detalles

season: data.season,

cabins: data.available_cabins.map(c => c.name).join(data.isMultipleCabins ? ' + ' : ' o '),

// Timestamp

timestamp: Date.now()

};

// Construir URL con par�metros para n8n (opcional, para tracking)

const urlParams = new URLSearchParams({

// Par�metros b�sicos para referencia

amount: data.advance_payment_ars,

reference: `RES-${reservationData.timestamp}`,

checkin: data.check_in,

checkout: data.check_out,

totalGuests: data.totalGuests,

adults: data.adults,

minors: data.minors || 'no',

pets: data.pets || 'no',

isMultipleCabins: data.isMultipleCabins || false

});

// En una integraci�n completa con n8n, podr�as usar:

// return `${baseURL}?${urlParams.toString()}`;

// Por ahora, URL directa (n8n capturar� los datos despu�s del pago)

return baseURL;

}

// Event listener principal

document.addEventListener('DOMContentLoaded', function() {

const form = document.getElementById('cabanas-booking-form');

const checkInInput = document.getElementById('check_in');

const checkOutInput = document.getElementById('check_out');

const submitBtn = document.getElementById('check-availability-btn');

const btnText = submitBtn.querySelector('.btn-text');

const btnLoading = submitBtn.querySelector('.btn-loading');

const resultDiv = document.getElementById('availability-result');

// ===== FUNCIONES GLOBALES PARA COMPARTIR Y GENERAR IMAGEN =====

function shareQuoteOnWhatsApp() {

if (!currentReservationData) {

alert('Primero debes verificar disponibilidad para compartir la cotizaci�n');

return;

}

const data = currentReservationData;

// Formatear fechas

const checkInFormatted = formatDate(data.check_in);

const checkOutFormatted = formatDate(data.check_out);

const nights = calculateNights(data.check_in, data.check_out);

// Informaci�n de hu�spedes

const guestInfo = `${data.adults} adulto${data.adults > 1 ? 's' : ''}` +

(data.minors > 0 ? ` + ${data.minors} menor${data.minors > 1 ? 'es' : ''}` : '');

const totalGuestText = `${data.totalGuests} persona${data.totalGuests > 1 ? 's' : ''} total`;

// Informaci�n de caba�as

const cabinsList = data.available_cabins.map(cabin =>

`${cabin.emoji} ${cabin.name}`

).join(' + ');

let accommodationText = '';

if (data.isMultipleCabins) {

accommodationText = `Se alojar�n en ${data.available_cabins.length} caba�as separadas: ${cabinsList}`;

} else {

accommodationText = `Caba�as disponibles: ${cabinsList}`;

}

// Informaci�n de descuento (si aplica)

let discountInfo = '';

if (data.discount_applied && data.discount_applied.amount > 0) {

discountInfo = `\n?? Descuento especial aplicado: -$${data.discount_applied.amount.toLocaleString('es-AR')} ARS`;

}

// Construir mensaje

const message = `??? COTIZACI�N CABA�AS LOMAS DEL URITORCO

?? Check-in: ${checkInFormatted}

?? Check-out: ${checkOutFormatted}

?? Noches: ${nights}

?? Hu�spedes: ${guestInfo} (${totalGuestText})

?? Alojamiento:

${accommodationText}${discountInfo}

?? TOTAL: $${data.total_price_ars.toLocaleString('es-AR')} ARS

?? Se�a (30%): $${data.advance_payment_ars.toLocaleString('es-AR')} ARS

?? �Confirmamos la reserva? �Esperamos tu respuesta!`;

// URL de WhatsApp

const whatsappURL = `https://wa.me/5493548479302?text=${encodeURIComponent(message)}`;

// Abrir WhatsApp

window.open(whatsappURL, '_blank');

}

function generateQuoteImage() {

if (!currentReservationData) {

alert('Primero debes verificar disponibilidad para generar la imagen');

return;

}

const data = currentReservationData;

// Crear canvas

const canvas = document.createElement('canvas');

const ctx = canvas.getContext('2d');

// Configurar tama�o del canvas (formato �ptimo para WhatsApp)

canvas.width = 800;

canvas.height = 1000;

// Fondo degradado

const gradient = ctx.createLinearGradient(0, 0, 0, canvas.height);

gradient.addColorStop(0, '#1e3a8a');

gradient.addColorStop(0.5, '#3730a3');

gradient.addColorStop(1, '#581c87');

ctx.fillStyle = gradient;

ctx.fillRect(0, 0, canvas.width, canvas.height);

// Configuraci�n de texto

ctx.textAlign = 'center';

ctx.fillStyle = '#ffffff';

// T�tulo principal

ctx.font = 'bold 36px Arial';

ctx.fillText('CABA�AS LOMAS', canvas.width / 2, 80);

ctx.fillText('DEL URITORCO', canvas.width / 2, 120);

// Subt�tulo

ctx.font = '20px Arial';

ctx.fillStyle = '#fbbf24';

ctx.fillText('COTIZACI�N PERSONALIZADA', canvas.width / 2, 160);

// L�nea decorativa

ctx.strokeStyle = '#fbbf24';

ctx.lineWidth = 3;

ctx.beginPath();

ctx.moveTo(canvas.width * 0.2, 180);

ctx.lineTo(canvas.width * 0.8, 180);

ctx.stroke();

// Fechas y noches

ctx.font = 'bold 24px Arial';

ctx.fillStyle = '#ffffff';

ctx.fillText(`?? ${formatDate(data.check_in)} ? ${formatDate(data.check_out)}`, canvas.width / 2, 240);

ctx.fillText(`?? ${data.nights} noche${data.nights > 1 ? 's' : ''}`, canvas.width / 2, 280);

// Hu�spedes

const guestInfo = `?? ${data.adults} adulto${data.adults > 1 ? 's' : ''}` +

(data.minors > 0 ? ` + ${data.minors} menor${data.minors > 1 ? 'es' : ''}` : '');

ctx.fillText(guestInfo, canvas.width / 2, 320);

// Caba�as disponibles

ctx.font = '18px Arial';

ctx.fillStyle = '#d1d5db';

ctx.fillText('?? CABA�AS DISPONIBLES:', canvas.width / 2, 380);

let yPos = 420;

data.available_cabins.forEach(cabin => {

ctx.font = 'bold 20px Arial';

ctx.fillStyle = '#fbbf24';

ctx.fillText(`${cabin.emoji} ${cabin.name}`, canvas.width / 2, yPos);

yPos += 40;

});

// Informaci�n de descuento (si aplica)

if (data.discount_applied && data.discount_applied.amount > 0) {

ctx.font = 'bold 22px Arial';

ctx.fillStyle = '#10b981';

ctx.fillText(`?? Descuento: -$${data.discount_applied.amount.toLocaleString('es-AR')} ARS`, canvas.width / 2, yPos + 40);

yPos += 60;

}

// Precios

ctx.font = 'bold 32px Arial';

ctx.fillStyle = '#fbbf24';

ctx.fillText('?? TOTAL', canvas.width / 2, yPos + 80);

ctx.font = 'bold 40px Arial';

ctx.fillStyle = '#ffffff';

ctx.fillText(`$${data.total_price_ars.toLocaleString('es-AR')} ARS`, canvas.width / 2, yPos + 130);

ctx.font = '24px Arial';

ctx.fillStyle = '#d1d5db';

ctx.fillText(`(USD $${data.total_price_usd.toLocaleString('en-US')})`, canvas.width / 2, yPos + 170);

// Se�a

ctx.font = 'bold 28px Arial';

ctx.fillStyle = '#ef4444';

ctx.fillText('?? SE�A (30%)', canvas.width / 2, yPos + 230);

ctx.font = 'bold 32px Arial';

ctx.fillStyle = '#ffffff';

ctx.fillText(`$${data.advance_payment_ars.toLocaleString('es-AR')} ARS`, canvas.width / 2, yPos + 270);

// Contacto

ctx.font = '18px Arial';

ctx.fillStyle = '#fbbf24';

ctx.fillText('?? WhatsApp: +54 9 3548 479302', canvas.width / 2, canvas.height - 60);

ctx.fillText('?? Capilla del Monte, C�rdoba', canvas.width / 2, canvas.height - 30);

// Convertir a imagen y mostrar

canvas.toBlob(function(blob) {

const url = URL.createObjectURL(blob);

// Crear modal para mostrar la imagen

const modal = document.createElement('div');

modal.style.cssText = `

position: fixed; top: 0; left: 0; width: 100%; height: 100%;

background: rgba(0,0,0,0.9); display: flex; flex-direction: column;

justify-content: center; align-items: center; z-index: 10000;

`;

const img = document.createElement('img');

img.src = url;

img.style.cssText = 'max-width: 90%; max-height: 80%; border-radius: 10px; box-shadow: 0 10px 30px rgba(0,0,0,0.5);';

const closeBtn = document.createElement('button');

closeBtn.textContent = 'Cerrar Vista Previa';

closeBtn.style.cssText = `

margin-top: 20px; padding: 12px 24px; background: #ef4444;

color: white; border: none; border-radius: 8px; cursor: pointer;

font-size: 16px; font-weight: 600;

`;

closeBtn.onclick = () => document.body.removeChild(modal);

modal.appendChild(img);

modal.appendChild(closeBtn);

document.body.appendChild(modal);

});

}

// ? VARIABLE GLOBAL PARA ALMACENAR DATOS DE RESERVA

let currentReservationData = null;

// Establecer fecha m�nima como hoy

const today = new Date().toISOString().split('T')[0];

checkInInput.min = today;

// Actualizar fecha m�nima de check-out cuando cambia check-in

checkInInput.addEventListener('change', function() {

const checkInDate = new Date(this.value);

const nextDay = new Date(checkInDate);

nextDay.setDate(checkInDate.getDate() + 1);

checkOutInput.min = nextDay.toISOString().split('T')[0];

if (checkOutInput.value && checkOutInput.value <= this.value) {

checkOutInput.value = '';

}

});

form.addEventListener('submit', async function(e) {

e.preventDefault();

const checkIn = checkInInput.value;

const checkOut = checkOutInput.value;

const adults = parseInt(document.getElementById('guests').value);

const minorsCount = document.getElementById('minors').value === 'no' ? 0 : parseInt(document.getElementById('minors').value);

const totalGuests = adults + minorsCount; // Total de personas (adultos + menores)

const minors = document.getElementById('minors').value;

const pets = document.getElementById('pets').value;

// Debug para verificar fechas capturadas

// Validaciones

if (!checkIn || !checkOut) {

showError('Por favor selecciona las fechas de llegada y salida');

return;

}

if (checkIn >= checkOut) {

showError('La fecha de salida debe ser posterior a la de llegada');

return;

}

if (checkIn < today) {

showError('No puedes seleccionar fechas pasadas');

return;

}

// Mostrar loading

submitBtn.disabled = true;

btnText.style.display = 'none';

btnLoading.style.display = 'inline';

resultDiv.style.display = 'none';

try {

// Verificar disponibilidad (ahora incluye calendarios externos)

const result = await checkAvailability(checkIn, checkOut, totalGuests, adults, minorsCount, minors, pets);

// Ocultar loading

submitBtn.disabled = false;

btnText.style.display = 'inline';

btnLoading.style.display = 'none';

resultDiv.style.display = 'block';

if (result.available) {

resultDiv.innerHTML = generateSuccessHTML(result);

resultDiv.className = 'cabanas-result cabanas-success';

// ? GUARDAR DATOS PARA WHATSAPP Y DESCUENTOS

currentReservationData = result;

// Mostrar secci�n de descuentos

document.getElementById('discount-section').style.display = 'block';

} else {

resultDiv.innerHTML = generateErrorHTML(result.message, result.suggestions, result.unavailableReasons);

resultDiv.className = 'cabanas-result cabanas-error';

// Ocultar secci�n de descuentos

document.getElementById('discount-section').style.display = 'none';

}

} catch (error) {

// Ocultar loading

submitBtn.disabled = false;

btnText.style.display = 'inline';

btnLoading.style.display = 'none';

showError('Error al verificar disponibilidad. Por favor intenta nuevamente.');

}

});

async function checkAvailability(checkIn, checkOut, totalGuests, adults, minorsCount, minors, pets) {

// Verificar disponibilidad de todas las caba�as

const availabilityResult = await checkAllCabinsAvailability(checkIn, checkOut, totalGuests);

// Debug

if (availabilityResult.availableCabins.length === 0) {

// Buscar fechas alternativas

const suggestions = await suggestAlternativeDates(checkIn, checkOut, totalGuests);

let message = `Lo sentimos, no hay caba�as disponibles para ${totalGuests} persona${totalGuests > 1 ? 's' : ''} del ${formatDateSpanish(checkIn)} al ${formatDateSpanish(checkOut)}.`;

if (availabilityResult.unavailableReasons.length > 0) {

message += `\n\nMotivos:\n${availabilityResult.unavailableReasons.join('\n')}`;

}

return {

available: false,

message: message,

suggestions: suggestions,

unavailableReasons: availabilityResult.unavailableReasons

};

}

// Calcular pricing (usar la primera caba�a disponible para el c�lculo)

const pricing = await calculatePricing(checkIn, checkOut, totalGuests, adults, minorsCount);

// Generar mensaje apropiado seg�n el tipo de alojamiento

let message;

if (availabilityResult.isMultipleCabins) {

message = `�Excelente! Para ${totalGuests} personas, tenemos ${availabilityResult.availableCabins.length} caba�as disponibles. Se alojar�n separados para mayor comodidad.`;

} else {

message = `�Excelente! Tenemos ${availabilityResult.availableCabins.length} caba�a${availabilityResult.availableCabins.length > 1 ? 's' : ''} disponible${availabilityResult.availableCabins.length > 1 ? 's' : ''} para tu estad�a de ${totalGuests} persona${totalGuests > 1 ? 's' : ''}.`;

}

return {

available: true,

message: message,

nights: pricing.nights,

total_price_usd: pricing.total_usd,

total_price_ars: pricing.total_ars,

advance_payment_usd: pricing.advance_payment_usd,

advance_payment_ars: pricing.advance_payment_ars,

price_breakdown: pricing.breakdown,

season: pricing.season,

available_cabins: availabilityResult.availableCabins,

check_in: checkIn,

check_out: checkOut,

totalGuests: totalGuests,

adults: adults,

minorsCount: minorsCount,

minors: minors,

pets: pets,

isMultipleCabins: availabilityResult.isMultipleCabins || false

};

}

// ===== SISTEMA DE ESTANCIAS M�NIMAS DIN�MICAS =====

function checkMinimumStayRequirements(checkIn, checkOut, totalGuests, adults, minors) {

const startDate = new Date(checkIn);

const endDate = new Date(checkOut);

const nights = Math.ceil((endDate - startDate) / (1000 60 60 * 24));

// Verificar si est� en per�odo de estancia m�nima

const minimumStayInfo = getMinimumStayForPeriod(checkIn, checkOut);

if (!minimumStayInfo.hasRequirement) {

return {

isValid: true,

actualNights: nights,

chargeNights: nights,

penalty: null,

suggestions: []

};

}

if (nights >= minimumStayInfo.minimumNights) {

return {

isValid: true,

actualNights: nights,

chargeNights: nights,

penalty: null,

suggestions: []

};

}

// Aplicar penalizaci�n

const penalty = calculateMinimumStayPenalty(minimumStayInfo, nights, checkIn, checkOut);

// Generar sugerencias

const suggestions = generateMinimumStaySuggestions(checkIn, checkOut, minimumStayInfo.minimumNights);

// Estancia corta detectada

// Estancia corta detectada

// Si necesitas log, usa console.log o return

// Si esto es parte de un objeto, debe ir dentro de return {...}

// Ejemplo:

// return {

// required: minimumStayInfo.minimumNights,

// requested: nights,

// penalty: penalty,

// suggestions: suggestions.length

// };

return {

isValid: false,

actualNights: nights,

chargeNights: penalty.chargeAsNights || nights,

minimumRequired: minimumStayInfo.minimumNights,

penalty: penalty,

reason: minimumStayInfo.reason,

suggestions: suggestions

};

}

function getMinimumStayForPeriod(checkIn, checkOut) {

const startDate = new Date(checkIn);

const endDate = new Date(checkOut);

// Verificar fines de semana largos

const longWeekendPeriods = [

{start: '2025-05-01', end: '2025-05-04', name: 'D�a del Trabajador (4 d�as)', minNights: 3},

{start: '2025-07-18', end: '2025-07-21', name: 'Independencia (4 d�as)', minNights: 3},

{start: '2025-08-15', end: '2025-08-18', name: 'San Mart�n (4 d�as)', minNights: 3},

{start: '2025-12-25', end: '2025-12-29', name: 'Navidad (5 d�as)', minNights: 3},

{start: '2025-12-31', end: '2026-01-05', name: 'A�o Nuevo (6 d�as)', minNights: 3}

];

for (const period of longWeekendPeriods) {

const periodStart = new Date(period.start);

const periodEnd = new Date(period.end);

// Verificar si la estad�a overlap con el per�odo

if ((startDate >= periodStart && startDate <= periodEnd) ||

(endDate >= periodStart && endDate <= periodEnd) ||

(startDate <= periodStart && endDate >= periodEnd)) {

return {

hasRequirement: true,

minimumNights: period.minNights,

reason: period.name,

type: 'long_weekend',

period: period

};

}

}

// Verificar per�odos de vacaciones

const vacationPeriods = [

{start: '2025-07-15', end: '2025-07-31', name: 'Vacaciones de Invierno', minNights: 3},

{start: '2025-12-20', end: '2026-01-10', name: 'Vacaciones de Verano', minNights: 3}

];

for (const period of vacationPeriods) {

const periodStart = new Date(period.start);

const periodEnd = new Date(period.end);

if ((startDate >= periodStart && startDate <= periodEnd) ||

(endDate >= periodStart && endDate <= periodEnd)) {

return {

hasRequirement: true,

minimumNights: period.minNights,

reason: period.name,

type: 'vacation',

period: period

};

}

}

// Verificar fines de semana regulares (solo para 1 noche)

const isWeekendStay = isWeekendOnlyStay(startDate, endDate);

if (isWeekendStay) {

return {

hasRequirement: true,

minimumNights: 2,

reason: 'Fin de semana regular',

type: 'weekend',

period: null

};

}

return {

hasRequirement: false,

minimumNights: 1,

reason: 'Sin restricciones',

type: 'normal'

};

}

function isWeekendOnlyStay(startDate, endDate) {

const nights = Math.ceil((endDate - startDate) / (1000 60 60 * 24));

if (nights !== 1) return false; // Solo aplica para 1 noche

const dayOfWeek = startDate.getDay(); // 0 = Domingo, 6 = S�bado

return dayOfWeek === 5 || dayOfWeek === 6; // Viernes o S�bado

}

function calculateMinimumStayPenalty(minimumStayInfo, actualNights, checkIn, checkOut) {

const shortfall = minimumStayInfo.minimumNights - actualNights;

if (minimumStayInfo.type === 'long_weekend') {

return {

type: 'charge_as_minimum_or_discount',

chargeAsNights: minimumStayInfo.minimumNights,

alternativeDiscount: 20,

message: `Para estad�as de ${actualNights} noche${actualNights > 1 ? 's' : ''} en ${minimumStayInfo.reason.toLowerCase()}:

Opci�n 1: Se cobra como ${minimumStayInfo.minimumNights} noches

Opci�n 2: Descuento del 20% sobre el total actual`,

adminReason: `Estancia corta en ${minimumStayInfo.reason}: ${actualNights}/${minimumStayInfo.minimumNights} noches`

};

}

if (minimumStayInfo.type === 'vacation') {

return {

type: 'charge_as_minimum',

chargeAsNights: minimumStayInfo.minimumNights,

message: `Durante ${minimumStayInfo.reason.toLowerCase()} el m�nimo son ${minimumStayInfo.minimumNights} noches. Se cobra como ${minimumStayInfo.minimumNights} noches.`,

adminReason: `Estancia corta en per�odo de vacaciones: ${actualNights}/${minimumStayInfo.minimumNights} noches`

};

}

if (minimumStayInfo.type === 'weekend') {

return {

type: 'percentage_surcharge',

surchargePercent: 25,

message: `Para estad�as de 1 noche en fin de semana se aplica un recargo del 25% por alta demanda`,

adminReason: `Estancia de 1 noche en fin de semana - recargo por demanda`

};

}

return {

type: 'none',

message: 'Sin penalizaci�n',

adminReason: 'Estancia v�lida'

};

}

function generateMinimumStaySuggestions(checkIn, checkOut, minimumNights) {

const suggestions = [];

const startDate = new Date(checkIn);

// Sugerencia 1: Extender estad�a actual

const extendedCheckOut = new Date(startDate);

extendedCheckOut.setDate(startDate.getDate() + minimumNights);

suggestions.push({

type: 'extend_current',

checkIn: checkIn,

checkOut: extendedCheckOut.toISOString().split('T')[0],

nights: minimumNights,

message: `Extender hasta ${formatDateSpanish(extendedCheckOut.toISOString().split('T')[0])} (${minimumNights} noches)`

});

// Sugerencia 2: Mover fechas hacia adelante (1 semana)

const futureStartDate = new Date(startDate);

futureStartDate.setDate(startDate.getDate() + 7);

const futureEndDate = new Date(futureStartDate);

futureEndDate.setDate(futureStartDate.getDate() + minimumNights);

suggestions.push({

type: 'alternative_dates',

checkIn: futureStartDate.toISOString().split('T')[0],

checkOut: futureEndDate.toISOString().split('T')[0],

nights: minimumNights,

message: `${formatDateSpanish(futureStartDate.toISOString().split('T')[0])} a ${formatDateSpanish(futureEndDate.toISOString().split('T')[0])} (sin restricciones)`

});

return suggestions;

}

// ===== FIN SISTEMA DE ESTANCIAS M�NIMAS =====

async function calculatePricing(checkIn, checkOut, guests, adults = null, minors = null) {

const startDate = new Date(checkIn);

const endDate = new Date(checkOut);

const nights = Math.ceil((endDate - startDate) / (1000 60 60 * 24));

// Si no se especifican adultos/menores, asumimos que todos son adultos (retrocompatibilidad)

if (adults === null) {

adults = guests;

minors = 0;

}

// ===== VERIFICAR ESTANCIAS M�NIMAS =====

const minimumStayCheck = checkMinimumStayRequirements(checkIn, checkOut, guests, adults, minors);

// Si hay penalizaci�n por estancia corta, calculamos con las noches penalizadas

const effectiveNights = minimumStayCheck.chargeNights;

const actualNights = nights;

if (!minimumStayCheck.isValid) {

}

// ===== FIN VERIFICACI�N ESTANCIAS M�NIMAS =====

// Obtener feriados reales SOLO del per�odo de la estad�a (OPTIMIZADO)

const realHolidays = await fetchArgentineHolidays(checkIn, checkOut);

let total = 0;

const breakdown = [];

let seasonInfo = '';

// Detectar d�as especiales ANTES del loop (solo para logs, sin descuentos)

// Usar effectiveNights para el c�lculo (puede ser mayor que nights si hay penalizaci�n)

for (let i = 0; i < effectiveNights; i++) {

const currentDate = new Date(startDate);

currentDate.setDate(startDate.getDate() + i);

const dateStr = currentDate.toISOString().split('T')[0];

// Buscar temporada

const season = findSeason(dateStr);

if (!season) continue;

// Guardar info de temporada para mostrar

if (seasonInfo === '') {

seasonInfo = season.name;

}

// Calcular precio base por adultos

const basePriceUSD = season.prices_usd[adults] || season.prices_usd[3];

// Agregar 50% del precio por cada menor

let minorsSurchargeUSD = 0;

if (minors > 0) {

const singleAdultPrice = season.prices_usd[1] || season.prices_usd[3];

minorsSurchargeUSD = (singleAdultPrice 0.5) minors;

}

const basePriceARS = convertUSDtoARS(basePriceUSD + minorsSurchargeUSD);

let dayTotalUSD = basePriceUSD + minorsSurchargeUSD;

let surchargeUSD = 0;

let type = 'D�a normal';

// Verificar tipo de d�a

const dayOfWeek = currentDate.getDay(); // 0 = Domingo, 6 = S�bado

const isWeekend = (dayOfWeek === 0 || dayOfWeek === 6);

const isHoliday = realHolidays.includes(dateStr);

const isLongWeekendCheck = await isLongWeekend(currentDate, realHolidays);

// Aplicar recargos seg�n tipo de d�a

if (isHoliday || isLongWeekendCheck) {

surchargeUSD = season.long_weekend_surcharge_usd;

dayTotalUSD += surchargeUSD;

type = 'Fin de semana largo';

} else if (isWeekend) {

surchargeUSD = season.weekend_surcharge_usd;

dayTotalUSD += surchargeUSD;

type = 'Fin de semana';

}

if (minors > 0) {

}

breakdown.push({

date: dateStr,

base_price_usd: basePriceUSD,

base_price_ars: convertUSDtoARS(basePriceUSD),

minors_surcharge_usd: minorsSurchargeUSD,

minors_surcharge_ars: convertUSDtoARS(minorsSurchargeUSD),

surcharge_usd: surchargeUSD,

surcharge_ars: convertUSDtoARS(surchargeUSD),

discount_usd: 0, // Sin descuentos por ahora

discount_ars: 0,

total_usd: dayTotalUSD,

total_ars: convertUSDtoARS(dayTotalUSD),

type: type,

season: season.name,

is_special: false // Removido l�gica sandwich

});

total += dayTotalUSD;

}

// Aplicar penalizaci�n si es necesario

let finalTotal = total;

let penaltyApplied = null;

if (!minimumStayCheck.isValid && minimumStayCheck.penalty) {

if (minimumStayCheck.penalty.type === 'percentage_surcharge') {

const surcharge = total * (minimumStayCheck.penalty.surchargePercent / 100);

finalTotal = total + surcharge;

penaltyApplied = {

type: 'surcharge',

amount_usd: surcharge,

amount_ars: convertUSDtoARS(surcharge),

percentage: minimumStayCheck.penalty.surchargePercent,

reason: minimumStayCheck.penalty.adminReason,

message: minimumStayCheck.penalty.message

};

} else if (minimumStayCheck.penalty.type === 'charge_as_minimum_or_discount') {

// Ya se calcul� con effectiveNights, no se aplica recargo adicional

penaltyApplied = {

type: 'charged_as_minimum',

effective_nights: effectiveNights,

actual_nights: actualNights,

reason: minimumStayCheck.penalty.adminReason,

message: minimumStayCheck.penalty.message,

alternative_available: true,

alternative_discount: minimumStayCheck.penalty.alternativeDiscount

};

}

}

return {

nights: actualNights, // Noches reales

effective_nights: effectiveNights, // Noches que se cobran

total_usd: finalTotal,

total_ars: convertUSDtoARS(finalTotal),

breakdown: breakdown,

season: seasonInfo,

advance_payment_usd: Math.round(finalTotal * 0.5), // 50% anticipo

advance_payment_ars: convertUSDtoARS(Math.round(finalTotal * 0.5)),

minimum_stay_info: minimumStayCheck,

penalty_applied: penaltyApplied

};

}

function generateSuccessHTML(data) {

const checkInFormatted = formatDateSpanish(data.check_in);

const checkOutFormatted = formatDateSpanish(data.check_out);

// Debug para verificar fechas

// Generar informaci�n de hu�spedes

let guestInfo = `${data.adults} adulto${data.adults > 1 ? 's' : ''}`;

if (data.minorsCount > 0) {

guestInfo += ` + ${data.minorsCount} menor${data.minorsCount > 1 ? 'es' : ''}`;

}

const totalGuestText = `${data.totalGuests} persona${data.totalGuests > 1 ? 's' : ''} total`;

// Generar lista de caba�as disponibles

const cabinsList = data.available_cabins.map(cabin =>

`${cabin.emoji} ${cabin.name}`

).join(' + ');

// Mensaje para comunicaciones externas (si las hubiera)

let accommodationText = '';

if (data.isMultipleCabins) {

accommodationText = ` Se alojar�n en ${data.available_cabins.length} caba�as separadas: ${cabinsList}`;

} else {

accommodationText = ` Caba�as disponibles: ${cabinsList}`;

}

const whatsappMessage = `Hola! Me interesa reservar del ${checkInFormatted} al ${checkOutFormatted} para ${guestInfo} (${totalGuestText}).${accommodationText}. El total ser�a $${data.total_price_ars.toLocaleString('es-AR')} ARS. �Est� disponible?`;

const emailSubject = `Consulta Reserva - ${checkInFormatted} al ${checkOutFormatted}`;

const emailBody = `Hola,\n\nMe interesa reservar con los siguientes detalles:\n\nFecha llegada: ${checkInFormatted}\nFecha salida: ${checkOutFormatted}\nHu�spedes: ${guestInfo} (${totalGuestText})\n${accommodationText}\nTotal: $${data.total_price_ars.toLocaleString('es-AR')} ARS\n\n�Est� disponible?\n\nGracias!`;

const emailURL = `mailto:[email protected]?subject=${encodeURIComponent(emailSubject)}&body=${encodeURIComponent(emailBody)}`;

// URL de MercadoPago para pago del 50% (necesitar�s proporcionar la URL real)

const mercadoPagoURL = generateMercadoPagoURL(data);

// Generar secci�n de caba�as disponibles con detalles espec�ficos

// COMENTADO: No mostrar detalles de caba�as en frontend para evitar confusi�n con "�ltima caba�a"

const availableCabinsHTML = ''; /* data.available_cabins.length > 0 ? `

<div class="available-cabins">

<h4>?? Caba�as disponibles para tu reserva:</h4>

<div class="cabins-list">

${data.available_cabins.map(cabin => `

<div class="available-cabin">

<span class="cabin-emoji">${cabin.emoji}</span>

<div class="cabin-details">

<span class="cabin-name" style="color: ${PRICING_CONFIG.cabins[cabin.id]?.color || '#065f46'};">${cabin.name}</span>

<span class="cabin-capacity">Hasta ${cabin.max_guests} personas</span>

<div class="cabin-calendar-info">

<small>?? Google Calendar + ?? Booking.com sincronizados</small>

</div>

</div>

</div>

`).join('')}

</div>

<p class="assignment-note">Se asignar� autom�ticamente una caba�a disponible seg�n orden de reserva</p>

</div>

` : ''; */

// Generar desglose de precios por d�a

// COMENTADO: No mostrar detalle por d�a para simplificar la experiencia del usuario

let breakdownHTML = ''; /*

if (data.price_breakdown && data.price_breakdown.length > 0) {

breakdownHTML = `

<div class="price-breakdown">

<h4>?? Detalle por d�a</h4>

${data.price_breakdown.map(day => `

<div class="breakdown-day">

<div>

<div class="breakdown-date">${formatDateSpanish(day.date)}</div>

<div class="breakdown-type">${day.type}</div>

</div>

<div class="breakdown-price">

$${day.total_ars.toLocaleString('es-AR')} ARS

</div>

</div>

`).join('')}

</div>

`;

} */

// Generar informaci�n de estancia m�nima si aplica

let minimumStayHTML = '';

if (data.minimum_stay_info && !data.minimum_stay_info.isValid) {

const penaltyInfo = data.penalty_applied;

if (penaltyInfo) {

if (penaltyInfo.type === 'charged_as_minimum') {

minimumStayHTML = `

<div style="background: linear-gradient(135deg, #fef3c7 0%, #fde68a 100%); border: 2px solid #f59e0b; border-radius: 12px; padding: 20px; margin: 20px 0; font-size: 0.95rem;">

<h4 style="margin: 0 0 12px 0; color: #92400e; font-size: 1.1rem;">?? Informaci�n sobre Estancia M�nima</h4>

<div style="background: rgba(255, 255, 255, 0.9); padding: 15px; border-radius: 8px; margin: 12px 0;">

<p style="margin: 8px 0; color: #92400e; line-height: 1.5; font-weight: 600;">

<strong>Per�odo:</strong> ${data.minimum_stay_info.reason}<br>

<strong>Estancia m�nima:</strong> ${data.minimum_stay_info.minimumRequired} noches<br>

<strong>Tu estad�a:</strong> ${data.minimum_stay_info.actualNights} noche${data.minimum_stay_info.actualNights > 1 ? 's' : ''}

</p>

<p style="margin: 8px 0; color: #92400e; font-weight: 600;">

Se cobra como ${penaltyInfo.effective_nights} noches (m�nimo del per�odo)

</p>

${penaltyInfo.alternative_available ? `

<div style="background: #e0f2fe; padding: 12px; border-radius: 6px; margin: 12px 0; border-left: 4px solid #0ea5e9;">

<p style="margin: 4px 0; color: #0c4a6e; font-weight: 600;">?? Alternativa disponible:</p>

<p style="margin: 4px 0; color: #0c4a6e;">Pod�s aplicar el c�digo <strong>AGOSTO2NOCHES</strong> para obtener un descuento del ${penaltyInfo.alternative_discount}% sobre el precio actual (${data.minimum_stay_info.actualNights} noches)</p>

</div>

` : ''}

</div>

</div>

`;

} else if (penaltyInfo.type === 'surcharge') {

minimumStayHTML = `

<div style="background: linear-gradient(135deg, #fef3c7 0%, #fde68a 100%); border: 2px solid #f59e0b; border-radius: 12px; padding: 20px; margin: 20px 0; font-size: 0.95rem;">

<h4 style="margin: 0 0 12px 0; color: #92400e; font-size: 1.1rem;">?? Recargo por Alta Demanda</h4>

<div style="background: rgba(255, 255, 255, 0.9); padding: 15px; border-radius: 8px; margin: 12px 0;">

<p style="margin: 8px 0; color: #92400e; line-height: 1.5;">

<strong>Motivo:</strong> ${penaltyInfo.message}<br>

<strong>Recargo aplicado:</strong> +$${penaltyInfo.amount_ars.toLocaleString('es-AR')} ARS (${penaltyInfo.percentage}%)

</p>

</div>

</div>

`;

}

}

}

// Generar sugerencias de estancia m�nima si hay

let minimumStaySuggestionsHTML = '';

if (data.minimum_stay_info && data.minimum_stay_info.suggestions && data.minimum_stay_info.suggestions.length > 0) {

minimumStaySuggestionsHTML = `

<div style="background: #e0f2fe; border: 2px solid #0ea5e9; border-radius: 12px; padding: 20px; margin: 20px 0; font-size: 0.95rem;">

<h4 style="margin: 0 0 12px 0; color: #0c4a6e; font-size: 1.1rem;">?? Sugerencias para optimizar tu estad�a</h4>

${data.minimum_stay_info.suggestions.map(suggestion => `

<div style="background: rgba(255, 255, 255, 0.9); padding: 15px; border-radius: 8px; margin: 12px 0; border-left: 4px solid #0ea5e9;">

<p style="margin: 4px 0; color: #0c4a6e; font-weight: 600;">

${suggestion.type === 'extend_current' ? '?? Extender estad�a actual:' : '?? Fechas alternativas:'}

</p>

<p style="margin: 4px 0; color: #0c4a6e;">

${suggestion.message}

</p>

</div>

`).join('')}

</div>

`;

}

return `

<div class="result-header">

<span class="result-icon">?</span>

<h3>�Disponible!</h3>

</div>

<p class="result-message">${data.message}</p>

<div class="result-summary">

<div><strong>Llegada:</strong> ${checkInFormatted}</div>

<div><strong>Salida:</strong> ${checkOutFormatted} (10:30hs)</div>

<div><strong>Hu�spedes:</strong> ${guestInfo} (${totalGuestText})</div>

${data.pets === 'yes' ? '<div><strong>Mascotas:</strong> S� (consultar pol�ticas)</div>' : ''}

<div><strong>Noches:</strong> ${data.nights}</div>

<div><strong>Temporada:</strong> ${data.season || 'Normal'}</div>

${data.isMultipleCabins ? `<div><strong>Alojamiento:</strong> ${data.available_cabins.length} caba�as separadas</div>` : ''}

</div>

${availableCabinsHTML}

${breakdownHTML}

<div class="total-price">

<div style="margin-bottom: 10px;">

<strong>Total: $${data.total_price_ars.toLocaleString('es-AR')} ARS</strong>

</div>

<div style="font-size: 0.9rem; color: rgba(255, 255, 255, 0.9); font-weight: 500;">

Equivalente a $${data.total_price_usd} USD (Tipo de cambio: $${PRICING_CONFIG.usd_to_ars_rate})

</div>

</div>

${minimumStayHTML}

${minimumStaySuggestionsHTML}

<div style="background: linear-gradient(135deg, #fbbf24 0%, #f59e0b 50%, #d97706 100%); border: 2px solid #f59e0b; border-radius: 12px; padding: 20px; margin: 20px 0; font-size: 0.95rem; box-shadow: 0 8px 25px rgba(245, 158, 11, 0.3);">

${data.isMultipleCabins ?

`<h4 style="margin: 0 0 12px 0; color: #92400e; font-size: 1.1rem;">?? ��LTIMA DISPONIBILIDAD PARA ${data.totalGuests} PERSONAS!</h4>

<p style="margin: 8px 0; color: #92400e; line-height: 1.5; font-weight: 600;">

Esta es la <strong>�nica combinaci�n de caba�as disponible</strong> para alojar a todo tu grupo en las fechas seleccionadas.

�No dejes pasar esta oportunidad �nica de disfrutar Lomas del Uritorco juntos!

</p>` :

`<h4 style="margin: 0 0 12px 0; color: #92400e; font-size: 1.1rem;">?? ��LTIMA CABA�A DISPONIBLE!</h4>

<p style="margin: 8px 0; color: #92400e; line-height: 1.5; font-weight: 600;">

Esta es la <strong>�ltima caba�a disponible</strong> para las fechas que seleccionaste.

�No dejes pasar esta oportunidad �nica de disfrutar Lomas del Uritorco!

</p>`

}

<div style="background: rgba(255, 255, 255, 0.9); padding: 15px; border-radius: 8px; margin: 12px 0;">

<h5 style="margin: 0 0 8px 0; color: #92400e;">?? BONIFICACI�N ESPECIAL POR RESERVAR AHORA:</h5>

<p style="margin: 4px 0; color: #92400e; font-weight: 600;"><strong>??? Sesi�n de Terapia de Reiki y Coaching para el Bienestar SIN CARGO</strong></p>

<p style="margin: 4px 0; color: #92400e; font-size: 0.95rem;"><strong>VALOR: $35.000</strong></p>

<p style="margin: 8px 0; color: #92400e; font-size: 0.9rem;">

?? <strong>Durante su estad�a</strong> en Lomas del Uritorco<br>

?? <strong>O por VideoConferencia</strong> seg�n disponibilidad

</p>

<p style="margin: 8px 0 4px 0; color: #92400e; font-size: 0.85rem; font-style: italic;">*Bonificaci�n v�lida solo al reservar ahora</p>

</div>

<p style="margin: 12px 0 0 0; color: #92400e; font-weight: 700; text-align: center; font-size: 1.05rem;">

?? �RESERVA AHORA y obt�n tu sesi�n de bienestar GRATIS!

</p>

</div>

<div style="background: #f0f9ff; border: 1px solid #0ea5e9; border-radius: 8px; padding: 15px; margin: 15px 0; font-size: 0.9rem;">

<h4 style="margin: 0 0 12px 0; color: #0c4a6e;">?? Proceso de Reserva Detallado:</h4>

<div style="background: white; padding: 12px; border-radius: 6px; margin: 10px 0;">

<p style="margin: 4px 0; color: #0c4a6e;"><strong>1. Pago Seguro:</strong> Haz clic en "Reservar Ahora" y ser�s dirigido a MercadoPago</p>

<p style="margin: 4px 0; color: #0c4a6e;"><strong>2. Anticipo 50%:</strong> Paga $${data.advance_payment_ars.toLocaleString('es-AR')} ARS de forma segura</p>

<p style="margin: 4px 0; color: #0c4a6e;"><strong>3. Formulario Autom�tico:</strong> Despu�s del pago recibir�s el formulario de check-in</p>

<p style="margin: 4px 0; color: #0c4a6e;"><strong>4. Confirmaci�n Inmediata:</strong> WhatsApp + Email con todos los detalles</p>

<p style="margin: 4px 0; color: #0c4a6e;"><strong>5. Check-in:</strong> El resto se paga al llegar a Lomas del Uritorco</p>

</div>

</div>

<div class="action-buttons">

<a href="${mercadoPagoURL}" target="_blank" class="btn-reserve" style="text-decoration: none; background: linear-gradient(135deg, #8b5cf6 0%, #7c3aed 50%, #6d28d9 100%); color: white; padding: 18px 24px; border-radius: 12px; font-weight: 700; text-align: center; display: block; width: 100%; box-sizing: border-box; font-size: 1.2rem; box-shadow: 0 6px 20px rgba(139, 92, 246, 0.4); border: 2px solid rgba(255, 255, 255, 0.3); transition: all 0.3s ease; margin: 20px 0;">

? RESERVAR AHORA

</a>

<button onclick="shareQuoteOnWhatsApp()" class="btn-share-whatsapp" style="background: linear-gradient(135deg, #25d366 0%, #1cb55c 50%, #128c43 100%); color: white; padding: 18px 24px; border: none; border-radius: 12px; font-weight: 700; text-align: center; display: block; width: 100%; box-sizing: border-box; font-size: 1.2rem; box-shadow: 0 6px 20px rgba(37, 211, 102, 0.4); border: 2px solid rgba(255, 255, 255, 0.3); transition: all 0.3s ease; margin: 10px 0; cursor: pointer;">

?? COMPARTIR POR WHATSAPP

</button>

<button onclick="generateQuoteImage()" class="btn-generate-image" style="background: linear-gradient(135deg, #f59e0b 0%, #d97706 50%, #b45309 100%); color: white; padding: 18px 24px; border: none; border-radius: 12px; font-weight: 700; text-align: center; display: block; width: 100%; box-sizing: border-box; font-size: 1.2rem; box-shadow: 0 6px 20px rgba(245, 158, 11, 0.4); border: 2px solid rgba(255, 255, 255, 0.3); transition: all 0.3s ease; margin: 10px 0; cursor: pointer;">

?? GENERAR IMAGEN COTIZACI�N

</button>

</div>

`;

}

function generateErrorHTML(message, suggestions = [], unavailableReasons = []) {

let suggestionsHTML = '';

if (suggestions && suggestions.length > 0) {

suggestionsHTML = `

<div class="suggestion">

<h4 style="margin: 0 0 10px 0; color: #92400e;">?? Fechas alternativas disponibles:</h4>

${suggestions.map(suggestion => `

<div style="background: white; padding: 10px; margin: 8px 0; border-radius: 6px; border: 1px solid #f59e0b;">

<strong>${formatDateSpanish(suggestion.checkIn)} al ${formatDateSpanish(suggestion.checkOut)}</strong><br>

<small>${suggestion.availableCabins} caba�a${suggestion.availableCabins > 1 ? 's' : ''} disponible${suggestion.availableCabins > 1 ? 's' : ''} - $${suggestion.totalARS.toLocaleString('es-AR')} ARS</small>

</div>

`).join('')}

</div>

`;

}

let reasonsHTML = '';

if (unavailableReasons && unavailableReasons.length > 0) {

reasonsHTML = `

<div class="suggestion">

<h4 style="margin: 0 0 10px 0; color: #92400e;">?? Detalles de disponibilidad:</h4>

${unavailableReasons.map(reason => `

<p style="margin: 4px 0; font-size: 0.9rem;">${reason}</p>

`).join('')}

</div>

`;

}

return `

<div class="result-header">

<span class="result-icon">?</span>

<h3>No disponible</h3>

</div>

<p class="result-message">${message}</p>

${reasonsHTML}

${suggestionsHTML}

${!suggestionsHTML ? `

<div class="suggestion">

<h4 style="margin: 0 0 10px 0; color: #92400e;">?? Fechas Alternativas Disponibles:</h4>

<p style="margin: 8px 0; color: #92400e;">Te sugerimos estas fechas pr�ximas disponibles para ${data.guests} hu�sped${data.guests > 1 ? 'es' : ''} y ${data.nights || 'varias'} noche${(data.nights || 2) > 1 ? 's' : ''}:</p>

<div style="background: white; padding: 12px; border-radius: 6px; margin: 10px 0;">

<p style="margin: 4px 0; color: #0c4a6e;"><strong>?? Pr�ximas fechas:</strong> Consulta disponibilidad desde ma�ana en adelante</p>

<p style="margin: 4px 0; color: #0c4a6e;"><strong>??? Fines de semana:</strong> Mayor disponibilidad en d�as de semana</p>

<p style="margin: 4px 0; color: #0c4a6e;"><strong>?? Tip:</strong> Las fechas m�s alejadas suelen tener mejor disponibilidad</p>

</div>

<p style="margin: 8px 0; color: #92400e;"><strong>�Prueba otras fechas y encuentra tu estad�a perfecta!</strong></p>

</div>

` : ''}

<div class="suggestion" style="background: #f0f9ff; border: 1px solid #0ea5e9; border-radius: 8px; padding: 15px; margin: 15px 0;">

<h4 style="margin: 0 0 10px 0; color: #0c4a6e;">? Prueba con Otro Calendario</h4>

<p style="margin: 8px 0; color: #0c4a6e;">Selecciona fechas diferentes arriba en el calendario para encontrar disponibilidad. Tenemos caba�as disponibles durante todo el a�o.</p>

<div style="background: white; padding: 12px; border-radius: 6px; margin: 10px 0;">

<p style="margin: 4px 0; color: #0c4a6e;"><strong>? Consejo:</strong> Intenta con fechas de lunes a jueves para mayor disponibilidad</p>

<p style="margin: 4px 0; color: #0c4a6e;"><strong>??? Flexibilidad:</strong> Cambiar tu fecha de llegada por 1-2 d�as puede abrir nuevas opciones</p>

</div>

</div>

`;

}

function showError(message) {

resultDiv.style.display = 'block';

resultDiv.innerHTML = generateErrorHTML(message, [], []);

resultDiv.className = 'cabanas-result cabanas-error';

}

// ===== SISTEMA DE DESCUENTOS =====

// Aplicar c�digo de descuento

document.getElementById('apply-discount-btn').addEventListener('click', function() {

const discountCode = document.getElementById('discount_code').value.trim().toUpperCase();

if (!discountCode) {

showDiscountResult('error', 'Por favor ingresa un c�digo de descuento');

return;

}

if (!currentReservationData) {

showDiscountResult('error', 'Primero debes verificar disponibilidad');

return;

}

applyDiscountCode(discountCode);

});

// Funci�n para aplicar descuento

function applyDiscountCode(code) {

const discountBtn = document.getElementById('apply-discount-btn');

const btnText = discountBtn.querySelector('.btn-text');

const btnLoading = discountBtn.querySelector('.btn-loading');

// Mostrar loading

btnText.style.display = 'none';

btnLoading.style.display = 'inline';

discountBtn.disabled = true;

// Buscar c�digo en configuraci�n

const discountConfig = PRICING_CONFIG.discount_codes[code];

if (!discountConfig) {

showDiscountResult('error', 'C�digo de descuento no v�lido');

resetDiscountButton();

return;

}

if (!discountConfig.active) {

showDiscountResult('error', 'Este c�digo no est� disponible actualmente');

resetDiscountButton();

return;

}

// Validar n�mero m�nimo de noches

if (discountConfig.min_nights && currentReservationData.nights < discountConfig.min_nights) {

showDiscountResult('error', `Este c�digo requiere m�nimo ${discountConfig.min_nights} noche${discountConfig.min_nights > 1 ? 's' : ''}`);

resetDiscountButton();

return;

}

// Validar fechas de validez

const today = new Date();

if (discountConfig.valid_from) {

const validFrom = new Date(discountConfig.valid_from);

if (today < validFrom) {

showDiscountResult('error', 'Este c�digo a�n no est� disponible');

resetDiscountButton();

return;

}

}

if (discountConfig.valid_until) {

const validUntil = new Date(discountConfig.valid_until);

if (today > validUntil) {

showDiscountResult('error', 'Este c�digo ha expirado');

resetDiscountButton();

return;

}

}

// Calcular descuento

const discountResult = calculateDiscount(discountConfig, currentReservationData);

if (discountResult.success) {

// Aplicar descuento

applyDiscountToReservation(discountResult, discountConfig);

showDiscountResult('success', `? ${discountConfig.client_display} aplicado: -$${discountResult.discount_ars.toLocaleString()} ARS`);

} else {

showDiscountResult('error', discountResult.message);

}

resetDiscountButton();

}

// Calcular monto del descuento

function calculateDiscount(discountConfig, reservationData) {

let discountUSD = 0;

let discountARS = 0;

switch (discountConfig.type) {

case 'percentage':

discountUSD = (reservationData.total_price_usd * discountConfig.value) / 100;

discountARS = (reservationData.total_price_ars * discountConfig.value) / 100;

break;

case 'fixed_usd':

discountUSD = discountConfig.value;

discountARS = convertUSDtoARS(discountConfig.value);

break;

case 'fixed_ars':

discountARS = discountConfig.value;

discountUSD = discountARS / PRICING_CONFIG.usd_to_ars_rate;

break;

default:

return { success: false, message: 'Tipo de descuento no v�lido' };

}

// Validar que el descuento no sea mayor al total

if (discountUSD >= reservationData.total_price_usd) {

discountUSD = reservationData.total_price_usd * 0.95; // M�ximo 95% de descuento

discountARS = reservationData.total_price_ars * 0.95;

}

return {

success: true,

discount_usd: Math.round(discountUSD * 100) / 100,

discount_ars: Math.round(discountARS),

code: discountConfig

};

}

// Aplicar descuento a la reserva

function applyDiscountToReservation(discountResult, discountConfig) {

// Calcular nuevos totales

const newTotalUSD = currentReservationData.total_price_usd - discountResult.discount_usd;

const newTotalARS = currentReservationData.total_price_ars - discountResult.discount_ars;

const newAdvanceUSD = Math.round((newTotalUSD 0.5) 100) / 100;

const newAdvanceARS = Math.round(newTotalARS * 0.5);

// Actualizar datos de reserva

currentReservationData.original_total_usd = currentReservationData.total_price_usd;

currentReservationData.original_total_ars = currentReservationData.total_price_ars;

currentReservationData.discount_applied = {

code: document.getElementById('discount_code').value.toUpperCase(),

admin_reason: discountConfig.admin_reason,

client_display: discountConfig.client_display,

discount_usd: discountResult.discount_usd,

discount_ars: discountResult.discount_ars

};

currentReservationData.total_price_usd = newTotalUSD;

currentReservationData.total_price_ars = newTotalARS;

currentReservationData.advance_payment_usd = newAdvanceUSD;

currentReservationData.advance_payment_ars = newAdvanceARS;

// Actualizar el resumen visual

updateReservationSummaryWithDiscount();

}

// Actualizar resumen visual con descuento

function updateReservationSummaryWithDiscount() {

const resultDiv = document.getElementById('availability-result');

const totalSection = resultDiv.querySelector('.price-total');

if (totalSection && currentReservationData.discount_applied) {

const discount = currentReservationData.discount_applied;

totalSection.innerHTML = `

<div style="margin-bottom: 8px; padding-bottom: 8px; border-bottom: 1px solid #e0e0e0;">

<div style="display: flex; justify-content: space-between; margin-bottom: 4px;">

<span>Subtotal:</span>

<span>$${currentReservationData.original_total_ars.toLocaleString()} ARS</span>

</div>

<div style="display: flex; justify-content: space-between; color: #059669; font-weight: 600;">

<span>??? ${discount.client_display}:</span>

<span>-$${discount.discount_ars.toLocaleString()} ARS</span>

</div>

</div>

<div style="display: flex; justify-content: space-between; font-size: 1.2rem; font-weight: 700; color: #1f2937;">

<span>?? TOTAL:</span>

<span>$${currentReservationData.total_price_ars.toLocaleString()} ARS</span>

</div>

<div style="display: flex; justify-content: space-between; margin-top: 4px; color: #6b7280; font-size: 0.9rem;">

<span>Equivalente:</span>

<span>USD $${currentReservationData.total_price_usd}</span>

</div>

<div style="display: flex; justify-content: space-between; margin-top: 8px; padding-top: 8px; border-top: 1px solid #e0e0e0; color: #059669; font-weight: 600;">

<span>?? Anticipo (50%):</span>

<span>$${currentReservationData.advance_payment_ars.toLocaleString()} ARS</span>

</div>

`;

}

}

// Mostrar resultado del descuento

function showDiscountResult(type, message) {

const resultDiv = document.getElementById('discount-result');

resultDiv.style.display = 'block';

resultDiv.className = `discount-result discount-${type}`;

resultDiv.textContent = message;

// Auto-ocultar despu�s de 5 segundos si es error

if (type === 'error') {

setTimeout(() => {

resultDiv.style.display = 'none';

}, 5000);

}

}

// Resetear bot�n de descuento

function resetDiscountButton() {

const discountBtn = document.getElementById('apply-discount-btn');

const btnText = discountBtn.querySelector('.btn-text');

const btnLoading = discountBtn.querySelector('.btn-loading');

btnText.style.display = 'inline';

btnLoading.style.display = 'none';

discountBtn.disabled = false;

}

// ===== FIN SISTEMA DE DESCUENTOS =====

});

</script>

</body>

</html>