<?php
if (!function_exists('q_h')) {
    function q_h($value): string { return htmlspecialchars((string)$value, ENT_QUOTES, 'UTF-8'); }
}
if (!function_exists('q_money')) {
    function q_money($value): string { return number_format((float)$value, 2); }
}
if (!function_exists('q_current_user_id')) {
    function q_current_user_id(): int {
        foreach (['user_id','consultant_id','login_user_id','id'] as $k) {
            if (!empty($_SESSION[$k]) && (int)$_SESSION[$k] > 0) return (int)$_SESSION[$k];
        }
        return 0;
    }
}
if (!function_exists('q_next_quote_no')) {
    function q_next_quote_no(PDO $pdo): string {
        do {
            $no = 'QT-' . date('YmdHis') . '-' . random_int(100, 999);
            $st = $pdo->prepare('SELECT COUNT(*) FROM reservation_quotes WHERE quote_no = :no');
            $st->execute([':no'=>$no]);
        } while ((int)$st->fetchColumn() > 0);
        return $no;
    }
}
if (!function_exists('q_next_order_no')) {
    function q_next_order_no(PDO $pdo): string {
        do {
            $no = 'BK-' . date('YmdHis') . '-' . random_int(100, 999);
            $st = $pdo->prepare('SELECT COUNT(*) FROM orders WHERE order_no = :no');
            $st->execute([':no'=>$no]);
        } while ((int)$st->fetchColumn() > 0);
        return $no;
    }
}
if (!function_exists('q_split_guest_name')) {
    function q_split_guest_name(string $fullName): array {
        $fullName = trim(preg_replace('/\s+/', ' ', $fullName));
        if ($fullName === '') return ['', ''];
        $p = explode(' ', $fullName, 2);
        return [trim($p[0] ?? ''), trim($p[1] ?? '')];
    }
}
if (!function_exists('q_find_or_create_guest')) {
    function q_find_or_create_guest(PDO $pdo, string $guestName, int $existingGuestId = 0): int {
        [$first,$last] = q_split_guest_name($guestName);
        if ($first === '' && $last === '') throw new RuntimeException('Guest name is required.');
        if ($existingGuestId > 0) return $existingGuestId;
        $st = $pdo->prepare('SELECT id FROM guests WHERE firstname = :f AND lastname = :l LIMIT 1');
        $st->execute([':f'=>$first, ':l'=>$last]);
        $id = (int)($st->fetchColumn() ?: 0);
        if ($id > 0) return $id;
        $ins = $pdo->prepare('INSERT INTO guests (firstname, lastname) VALUES (:f,:l)');
        $ins->execute([':f'=>$first, ':l'=>$last]);
        return (int)$pdo->lastInsertId();
    }
}
if (!function_exists('q_fetch_room_types_by_property')) {
    function q_fetch_room_types_by_property(PDO $pdo, int $propertyId): array {
        if ($propertyId <= 0) return [];
        $st = $pdo->prepare('SELECT id, title, COALESCE(higher_occupancy,0) AS higher_occupancy FROM room_types WHERE property_id = :p ORDER BY title ASC');
        $st->execute([':p'=>$propertyId]);
        return $st->fetchAll(PDO::FETCH_ASSOC) ?: [];
    }
}
if (!function_exists('q_fetch_rate_plan')) {
    function q_fetch_rate_plan(PDO $pdo, int $roomTypeId, ?int $agentId, string $date): ?array {
        if ($agentId && $agentId > 0) {
            $st = $pdo->prepare('SELECT * FROM room_type_rate_plans WHERE room_type_id = :rt AND status = 1 AND valid_from <= :d1 AND valid_to >= :d2 AND agent_id = :a ORDER BY id DESC LIMIT 1');
            $st->execute([':rt'=>$roomTypeId, ':d1'=>$date, ':d2'=>$date, ':a'=>$agentId]);
            $r = $st->fetch(PDO::FETCH_ASSOC); if ($r) return $r;
        }
        $st = $pdo->prepare('SELECT * FROM room_type_rate_plans WHERE room_type_id = :rt AND status = 1 AND valid_from <= :d1 AND valid_to >= :d2 AND agent_id IS NULL ORDER BY id DESC LIMIT 1');
        $st->execute([':rt'=>$roomTypeId, ':d1'=>$date, ':d2'=>$date]);
        $r = $st->fetch(PDO::FETCH_ASSOC);
        return $r ?: null;
    }
}
if (!function_exists('q_allocate_guests_to_rooms')) {
    function q_allocate_guests_to_rooms(int $adults, int $children, int $rooms): array {
        $rooms = max(1,$rooms); $adults=max(0,$adults); $children=max(0,$children); $out=[];
        for($i=0;$i<$rooms;$i++) $out[]=['adults'=>0,'children'=>0];
        for($i=0;$i<$rooms && $adults>0;$i++){ $out[$i]['adults']++; $adults--; }
        for($i=0;$i<$rooms && $adults>0;$i++){ while($out[$i]['adults']<2 && $adults>0){$out[$i]['adults']++;$adults--;} }
        $i=0; while($adults>0){$out[$i%$rooms]['adults']++;$adults--;$i++;}
        $i=0; while($children>0){ if($out[$i%$rooms]['adults']>0){$out[$i%$rooms]['children']++;$children--;} $i++; }
        return $out;
    }
}
if (!function_exists('q_calc_room_night')) {
    function q_calc_room_night(int $adults, int $children, float $sharing, float $single, float $supp, float $childPct = 50): array {
        $adults=max(0,$adults); $children=max(0,$children); $childPct=max(0,$childPct);
        if ($adults <= 0) return ['adult_total'=>0,'child_total'=>0,'room_total'=>0,'applied_rate_type'=>'empty_room','rate_label'=>'Empty'];
        if ($adults === 1) { $adult = $single > 0 ? $single : ($sharing + $supp); $type = $single > 0 ? 'single_rate' : 'sharing_plus_single_supplement'; $label = $single > 0 ? 'Single' : 'Sharing + Supplement'; }
        else { $adult = $adults * $sharing; $type='sharing'; $label='Sharing'; }
        $childRate = $sharing * ($childPct/100); $child = $children*$childRate; $total=$adult+$child;
        return ['adult_total'=>round($adult,2),'child_total'=>round($child,2),'room_total'=>round($total,2),'applied_rate_type'=>$type,'rate_label'=>$label];
    }
}
if (!function_exists('q_normalize_room_rows')) {
    function q_normalize_room_rows(array $rows): array {
        $clean=[];
        foreach($rows as $r){
            $rt=(int)($r['room_type_id']??0); $qty=max(1,(int)($r['qty_rooms']??1)); $ad=max(0,(int)($r['adults']??0)); $ch=max(0,(int)($r['children']??0));
            if($rt<=0 && $ad<=0 && $ch<=0) continue;
            $clean[]=['room_type_id'=>$rt,'qty_rooms'=>$qty,'adults'=>$ad,'children'=>$ch];
        }
        if(!$clean) $clean[]=['room_type_id'=>0,'qty_rooms'=>1,'adults'=>1,'children'=>0];
        return array_values($clean);
    }
}
if (!function_exists('q_summarize_room_rows')) {
    function q_summarize_room_rows(array $rows): array {
        $rooms=0;$ad=0;$kids=0; foreach($rows as $r){$rooms+=max(1,(int)($r['qty_rooms']??1));$ad+=(int)($r['adults']??0);$kids+=(int)($r['children']??0);} return ['totalrooms'=>$rooms,'adults'=>$ad,'kids'=>$kids,'total_pax'=>$ad+$kids];
    }
}
if (!function_exists('q_expand_room_rows')) {
    function q_expand_room_rows(array $rows): array {
        $slots=[]; foreach($rows as $r){$alloc=q_allocate_guests_to_rooms((int)$r['adults'],(int)$r['children'],max(1,(int)$r['qty_rooms'])); foreach($alloc as $a){$slots[]=['room_type_id'=>(int)$r['room_type_id'],'adults'=>(int)$a['adults'],'children'=>(int)$a['children']];}} return $slots;
    }
}
if (!function_exists('q_build_price_rows')) {
    function q_build_price_rows(PDO $pdo, array $roomRows, ?int $agentId, string $checkIn, string $checkOut): array {
        $in=strtotime($checkIn); $out=strtotime($checkOut); if(!$in || !$out || $out <= $in) return [];
        $slots=q_expand_room_rows($roomRows); $rows=[];
        for($cur=$in; $cur<$out; $cur=strtotime('+1 day',$cur)){
            $date=date('Y-m-d',$cur); $nightTotal=0; $roomBreakdown=[]; $missing=false;
            foreach($slots as $i=>$slot){
                $plan=q_fetch_rate_plan($pdo,(int)$slot['room_type_id'],$agentId,$date);
                if(!$plan){$missing=true; $roomBreakdown[]=['room_no'=>$i+1,'room_type_id'=>(int)$slot['room_type_id'],'adults'=>(int)$slot['adults'],'children'=>(int)$slot['children'],'total'=>0,'rate_plan_id'=>0,'rate_plan_title'=>'No rate plan']; continue;}
                $calc=q_calc_room_night((int)$slot['adults'],(int)$slot['children'],(float)$plan['sharing_rate'],(float)$plan['single_rate'],(float)$plan['single_supplement'],(float)$plan['child_rate_percent']);
                $nightTotal += (float)$calc['room_total'];
                $roomBreakdown[]=['room_no'=>$i+1,'room_type_id'=>(int)$slot['room_type_id'],'adults'=>(int)$slot['adults'],'children'=>(int)$slot['children'],'total'=>(float)$calc['room_total'],'adult_total'=>(float)$calc['adult_total'],'child_total'=>(float)$calc['child_total'],'applied_rate_type'=>$calc['applied_rate_type'],'rate_plan_id'=>(int)$plan['id'],'rate_plan_title'=>(string)$plan['title'],'rate_plan_currency'=>(string)($plan['currency']??''),'rate_plan_season_label'=>(string)($plan['season_label']??''),'rate_plan_season_color'=>(string)($plan['season_color']??'secondary'),'sharing_rate'=>(float)$plan['sharing_rate'],'single_rate'=>(float)$plan['single_rate'],'single_supplement'=>(float)$plan['single_supplement'],'child_rate_percent'=>(float)$plan['child_rate_percent']];
            }
            $rows[]=['date'=>$date,'night_total'=>round($nightTotal,2),'missing_rate'=>$missing,'room_breakdown'=>$roomBreakdown];
        }
        return $rows;
    }
}
if (!function_exists('q_discount_amount')) {
    function q_discount_amount(float $subtotal, string $type, float $value): float {
        if ($type === 'percent') return round($subtotal * max(0,$value) / 100, 2);
        if ($type === 'amount') return round(min($subtotal, max(0,$value)), 2);
        return 0.0;
    }
}
if (!function_exists('q_save_quote_prices')) {
    function q_save_quote_prices(PDO $pdo, int $quoteId, int $propertyId, array $roomRows, array $priceRows): void {
        $pdo->prepare('DELETE FROM reservation_quote_prices WHERE quote_id = :id')->execute([':id'=>$quoteId]);
        $roomIdMap=[];
        $st=$pdo->prepare('SELECT id, row_no FROM reservation_quote_rooms WHERE quote_id=:id ORDER BY row_no ASC,id ASC');
        $st->execute([':id'=>$quoteId]); foreach($st->fetchAll(PDO::FETCH_ASSOC) as $r){$roomIdMap[(int)$r['row_no']] = (int)$r['id'];}
        $ins=$pdo->prepare('INSERT INTO reservation_quote_prices (quote_id, quote_room_id, property_id, stay_date, room_no, room_type_id, adults_count, kids_count, rate_plan_id, rate_plan_title, rate_plan_currency, rate_plan_season_label, rate_plan_season_color, sharing_rate, single_rate, single_supplement, child_rate_percent, adult_total, child_total, total, applied_rate_type) VALUES (:quote_id,:quote_room_id,:property_id,:stay_date,:room_no,:room_type_id,:adults_count,:kids_count,:rate_plan_id,:rate_plan_title,:rate_plan_currency,:rate_plan_season_label,:rate_plan_season_color,:sharing_rate,:single_rate,:single_supplement,:child_rate_percent,:adult_total,:child_total,:total,:applied_rate_type)');
        foreach($priceRows as $night){ foreach(($night['room_breakdown']??[]) as $bd){$ins->execute([':quote_id'=>$quoteId,':quote_room_id'=>$roomIdMap[(int)($bd['room_no']??0)]??null,':property_id'=>$propertyId,':stay_date'=>$night['date'],':room_no'=>(int)($bd['room_no']??1),':room_type_id'=>(int)($bd['room_type_id']??0),':adults_count'=>(int)($bd['adults']??0),':kids_count'=>(int)($bd['children']??0),':rate_plan_id'=>(int)($bd['rate_plan_id']??0),':rate_plan_title'=>(string)($bd['rate_plan_title']??''),':rate_plan_currency'=>(string)($bd['rate_plan_currency']??''),':rate_plan_season_label'=>(string)($bd['rate_plan_season_label']??''),':rate_plan_season_color'=>(string)($bd['rate_plan_season_color']??'secondary'),':sharing_rate'=>(float)($bd['sharing_rate']??0),':single_rate'=>(float)($bd['single_rate']??0),':single_supplement'=>(float)($bd['single_supplement']??0),':child_rate_percent'=>(float)($bd['child_rate_percent']??50),':adult_total'=>(float)($bd['adult_total']??0),':child_total'=>(float)($bd['child_total']??0),':total'=>(float)($bd['total']??0),':applied_rate_type'=>(string)($bd['applied_rate_type']??'')]);}}
    }
}
if (!function_exists('q_count_available_rooms_by_type')) {
    function q_count_available_rooms_by_type(PDO $pdo, int $propertyId, int $roomTypeId, string $checkIn, string $checkOut): array {
        if($propertyId<=0||$roomTypeId<=0||$checkIn===''||$checkOut==='') return ['total_rooms'=>0,'occupied_rooms'=>0,'available_rooms'=>0];
        $t=$pdo->prepare('SELECT COUNT(*) FROM rooms WHERE property_id=:p AND room_type_id=:rt'); $t->execute([':p'=>$propertyId,':rt'=>$roomTypeId]); $total=(int)$t->fetchColumn();
        $o=$pdo->prepare('SELECT COALESCE(SUM(x.rooms_used),0) FROM (SELECT COUNT(rmg.id) rooms_used FROM order_rooming rmg INNER JOIN orders o ON o.id=rmg.order_id WHERE o.property_id=:p1 AND rmg.room_type_id=:rt1 AND COALESCE(o.status,1)<>0 AND o.check_in<:co1 AND o.check_out>:ci1 UNION ALL SELECT COALESCE(SUM(o.totalrooms),0) rooms_used FROM orders o WHERE o.property_id=:p2 AND o.room_type_id=:rt2 AND COALESCE(o.status,1)<>0 AND o.check_in<:co2 AND o.check_out>:ci2 AND NOT EXISTS (SELECT 1 FROM order_rooming r2 WHERE r2.order_id=o.id)) x');
        $o->execute([':p1'=>$propertyId,':rt1'=>$roomTypeId,':co1'=>$checkOut,':ci1'=>$checkIn,':p2'=>$propertyId,':rt2'=>$roomTypeId,':co2'=>$checkOut,':ci2'=>$checkIn]); $occ=(int)$o->fetchColumn();
        return ['total_rooms'=>$total,'occupied_rooms'=>$occ,'available_rooms'=>max(0,$total-$occ)];
    }
}
?>
