<?php
/**
 * booking_connector.php
 * Place: /rooms/modules/channel_manager/providers/booking_connector.php
 *
 * Booking.com Connectivity API connector wrapper.
 *
 * IMPORTANT:
 * - This file is designed for Booking.com Connectivity API credentials.
 * - Booking.com API access usually requires Connectivity Partner access.
 * - Keep live_mode = 0 until your Booking.com credentials/endpoints are approved.
 *
 * Used by:
 * - channel_push_availability.php
 * - channel_push_rates.php
 * - channel_pull_bookings.php
 *
 * This connector supports:
 * - Building Booking.com style payloads
 * - Sending XML/JSON over cURL
 * - Logging safe responses
 * - Blocking live calls unless live_mode is enabled on the connection
 */

if (!function_exists('bc_decrypt_secret')) {
    function bc_decrypt_secret(?string $value): string
    {
        $value = (string)$value;
        if ($value === '') return '';

        $decoded = base64_decode($value, true);
        return $decoded === false ? $value : $decoded;
    }
}

if (!function_exists('bc_connection_live_enabled')) {
    function bc_connection_live_enabled(array $connection): bool
    {
        /*
         * Safer approach:
         * Add column live_mode later if you want.
         * For now, live mode is only enabled when notes contains:
         * BOOKING_LIVE_ENABLED=1
         */
        return strpos((string)($connection['notes'] ?? ''), 'BOOKING_LIVE_ENABLED=1') !== false;
    }
}

if (!function_exists('bc_base_url')) {
    function bc_base_url(array $connection): string
    {
        $url = trim((string)($connection['api_base_url'] ?? ''));

        if ($url === '') {
            /*
             * Booking.com Connectivity supply XML base often uses supply-xml.booking.com.
             * Confirm exact endpoint with your Connectivity Partner account.
             */
            $url = 'https://supply-xml.booking.com';
        }

        return rtrim($url, '/');
    }
}

if (!function_exists('bc_auth_headers')) {
    function bc_auth_headers(array $connection, string $contentType = 'application/xml'): array
    {
        $headers = [
            'Content-Type: ' . $contentType,
            'Accept: application/xml',
        ];

        $token = bc_decrypt_secret($connection['api_token_enc'] ?? '');
        if ($token !== '') {
            $headers[] = 'Authorization: Bearer ' . $token;
            return $headers;
        }

        $username = trim((string)($connection['api_username'] ?? ''));
        $password = bc_decrypt_secret($connection['api_password_enc'] ?? '');

        if ($username !== '' && $password !== '') {
            $headers[] = 'Authorization: Basic ' . base64_encode($username . ':' . $password);
        }

        return $headers;
    }
}

if (!function_exists('bc_http_request')) {
    function bc_http_request(array $connection, string $method, string $endpoint, string $body = '', string $contentType = 'application/xml'): array
    {
        if (!bc_connection_live_enabled($connection)) {
            return [
                'ok' => false,
                'blocked' => true,
                'status' => 0,
                'message' => 'Booking.com live mode is disabled. Add BOOKING_LIVE_ENABLED=1 to connection notes after Booking.com approves credentials.',
                'endpoint' => $endpoint,
                'request_body' => $body,
                'response_body' => '',
            ];
        }

        $url = bc_base_url($connection) . '/' . ltrim($endpoint, '/');

        $ch = curl_init($url);
        $headers = bc_auth_headers($connection, $contentType);

        curl_setopt_array($ch, [
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_TIMEOUT => 45,
            CURLOPT_CUSTOMREQUEST => strtoupper($method),
            CURLOPT_HTTPHEADER => $headers,
        ]);

        if (strtoupper($method) !== 'GET') {
            curl_setopt($ch, CURLOPT_POSTFIELDS, $body);
        }

        $responseBody = curl_exec($ch);
        $error = curl_error($ch);
        $status = (int)curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_close($ch);

        return [
            'ok' => $error === '' && $status >= 200 && $status < 300,
            'blocked' => false,
            'status' => $status,
            'message' => $error !== '' ? $error : ('HTTP ' . $status),
            'endpoint' => $endpoint,
            'request_body' => $body,
            'response_body' => (string)$responseBody,
        ];
    }
}

if (!function_exists('bc_xml_escape')) {
    function bc_xml_escape($value): string
    {
        return htmlspecialchars((string)$value, ENT_XML1 | ENT_QUOTES, 'UTF-8');
    }
}

if (!function_exists('bc_build_availability_xml')) {
    function bc_build_availability_xml(array $connection, array $availabilityRows): string
    {
        /*
         * Generic Booking.com OTA-style inventory payload.
         * Confirm final endpoint/schema with Booking.com certification.
         */
        $hotelCode = bc_xml_escape($connection['external_property_id'] ?? '');

        $xml = '<?xml version="1.0" encoding="UTF-8"?>' . "\n";
        $xml .= '<OTA_HotelAvailNotifRQ xmlns="http://www.opentravel.org/OTA/2003/05" Version="1.0">' . "\n";
        $xml .= '  <AvailStatusMessages HotelCode="' . $hotelCode . '">' . "\n";

        foreach ($availabilityRows as $row) {
            $date = bc_xml_escape($row['date'] ?? '');
            $roomId = bc_xml_escape($row['external_room_id'] ?? '');
            $available = (int)($row['available'] ?? 0);
            $closed = !empty($row['closed']) || $available <= 0;

            $xml .= '    <AvailStatusMessage BookingLimit="' . max(0, $available) . '">' . "\n";
            $xml .= '      <StatusApplicationControl Start="' . $date . '" End="' . $date . '" InvTypeCode="' . $roomId . '" />' . "\n";
            $xml .= '      <RestrictionStatus Status="' . ($closed ? 'Close' : 'Open') . '" />' . "\n";
            $xml .= '    </AvailStatusMessage>' . "\n";
        }

        $xml .= '  </AvailStatusMessages>' . "\n";
        $xml .= '</OTA_HotelAvailNotifRQ>';

        return $xml;
    }
}

if (!function_exists('bc_build_rates_xml')) {
    function bc_build_rates_xml(array $connection, array $rateRows): string
    {
        /*
         * Generic Booking.com OTA-style rate payload.
         * Confirm final endpoint/schema and pricing model with Booking.com certification.
         */
        $hotelCode = bc_xml_escape($connection['external_property_id'] ?? '');

        $xml = '<?xml version="1.0" encoding="UTF-8"?>' . "\n";
        $xml .= '<OTA_HotelRateAmountNotifRQ xmlns="http://www.opentravel.org/OTA/2003/05" Version="1.0">' . "\n";
        $xml .= '  <RateAmountMessages HotelCode="' . $hotelCode . '">' . "\n";

        foreach ($rateRows as $row) {
            $date = bc_xml_escape($row['date'] ?? '');
            $rateId = bc_xml_escape($row['external_rate_id'] ?? '');
            $amount = number_format((float)($row['amount'] ?? 0), 2, '.', '');
            $currency = bc_xml_escape($row['currency'] ?? 'USD');

            $xml .= '    <RateAmountMessage>' . "\n";
            $xml .= '      <StatusApplicationControl Start="' . $date . '" End="' . $date . '" RatePlanCode="' . $rateId . '" />' . "\n";
            $xml .= '      <Rates>' . "\n";
            $xml .= '        <Rate>' . "\n";
            $xml .= '          <BaseByGuestAmts>' . "\n";
            $xml .= '            <BaseByGuestAmt AmountAfterTax="' . $amount . '" CurrencyCode="' . $currency . '" />' . "\n";
            $xml .= '          </BaseByGuestAmts>' . "\n";
            $xml .= '        </Rate>' . "\n";
            $xml .= '      </Rates>' . "\n";
            $xml .= '    </RateAmountMessage>' . "\n";
        }

        $xml .= '  </RateAmountMessages>' . "\n";
        $xml .= '</OTA_HotelRateAmountNotifRQ>';

        return $xml;
    }
}

if (!function_exists('bc_push_availability')) {
    function bc_push_availability(array $connection, array $availabilityRows): array
    {
        $xml = bc_build_availability_xml($connection, $availabilityRows);

        /*
         * Endpoint varies by Booking.com Connectivity product/version.
         * Common OTA-style endpoint pattern:
         * /hotels/ota/OTA_HotelAvailNotif
         */
        return bc_http_request(
            $connection,
            'POST',
            '/hotels/ota/OTA_HotelAvailNotif',
            $xml,
            'application/xml'
        );
    }
}

if (!function_exists('bc_push_rates')) {
    function bc_push_rates(array $connection, array $rateRows): array
    {
        $xml = bc_build_rates_xml($connection, $rateRows);

        /*
         * Endpoint varies by Booking.com Connectivity product/version.
         * Common OTA-style endpoint pattern:
         * /hotels/ota/OTA_HotelRateAmountNotif
         */
        return bc_http_request(
            $connection,
            'POST',
            '/hotels/ota/OTA_HotelRateAmountNotif',
            $xml,
            'application/xml'
        );
    }
}

if (!function_exists('bc_pull_reservations')) {
    function bc_pull_reservations(array $connection, string $dateFrom, string $dateTo): array
    {
        /*
         * Placeholder wrapper.
         * Booking.com Reservations API details depend on your Connectivity Partner access/version.
         */
        if (!bc_connection_live_enabled($connection)) {
            return [
                'ok' => false,
                'blocked' => true,
                'message' => 'Booking.com live reservation pull is disabled. Add BOOKING_LIVE_ENABLED=1 to connection notes after approval.',
                'bookings' => [],
            ];
        }

        $endpoint = '/hotels/xml/reservations?hotel_id='
            . rawurlencode((string)($connection['external_property_id'] ?? ''))
            . '&date_from=' . rawurlencode($dateFrom)
            . '&date_to=' . rawurlencode($dateTo);

        $response = bc_http_request($connection, 'GET', $endpoint, '', 'application/xml');

        return [
            'ok' => $response['ok'],
            'blocked' => $response['blocked'] ?? false,
            'message' => $response['message'],
            'raw_response' => $response['response_body'] ?? '',
            'bookings' => [],
        ];
    }
}
