<?php

if (!defined('ABSPATH')) {
    exit;
}

function chbk_get_trip_capacity($trip_id)
{
    $trip_id = (int) $trip_id;
    if ($trip_id < 1) {
        return 0;
    }
    $boat_id = (int) get_post_meta($trip_id, 'chbk_boat_id', true);
    if (!$boat_id) {
        return 0;
    }

    $capacity = (int) get_post_meta($boat_id, 'chbk_max_capacity', true);
    $capacity = (int) apply_filters('chbk_trip_capacity', $capacity, $trip_id, $boat_id);
    return max(0, $capacity);
}

function chbk_get_trip_min_capacity($trip_id)
{
    $boat_id = (int) get_post_meta($trip_id, 'chbk_boat_id', true);
    if (!$boat_id) {
        return 1;
    }

    $min = (int) get_post_meta($boat_id, 'chbk_min_capacity', true);
    return max(1, $min);
}

function chbk_get_trip_timezone($trip_id)
{
    $trip_override = (string) get_post_meta($trip_id, 'chbk_trip_timezone_override', true);
    if ($trip_override !== '') {
        return $trip_override;
    }
    $captain_id = (int) get_post_meta($trip_id, 'chbk_captain_id', true);
    $captain_tz = $captain_id ? (string) get_post_meta($captain_id, 'chbk_timezone', true) : '';
    return $captain_tz ?: chbk_get_default_site_timezone();
}

function chbk_get_trip_duration_minutes($trip_id)
{
    $duration = (int) get_post_meta($trip_id, 'chbk_schedule_duration_minutes', true);
    return max(1, $duration ?: 240);
}

function chbk_get_trip_start_time($trip_id)
{
    $start_time = (string) get_post_meta($trip_id, 'chbk_schedule_start_time', true);
    if ($start_time !== '') {
        return $start_time;
    }

    // Backward-compat for pre-instance scheduling model.
    $legacy_start = (string) get_post_meta($trip_id, 'chbk_schedule_window_start', true);
    return $legacy_start !== '' ? $legacy_start : '08:00';
}

function chbk_get_trip_weekdays($trip_id)
{
    $raw = (string) get_post_meta($trip_id, 'chbk_schedule_weekdays', true);
    if ($raw === '') {
        return [0, 1, 2, 3, 4, 5, 6];
    }

    if (function_exists('chbk_parse_weekdays_csv')) {
        return chbk_parse_weekdays_csv($raw);
    }

    $parts = preg_split('/[^0-6]+/', $raw);
    $days = [];
    foreach ($parts as $part) {
        if ($part === '' || $part === null) {
            continue;
        }
        $day = (int) $part;
        if ($day >= 0 && $day <= 6) {
            $days[] = $day;
        }
    }

    $days = array_values(array_unique($days));
    sort($days);
    return $days;
}

function chbk_get_trip_blackout_dates($trip_id)
{
    $raw = (string) get_post_meta($trip_id, 'chbk_blackout_dates', true);
    if ($raw === '') {
        return [];
    }

    $parts = preg_split('/[\r\n,]+/', $raw);
    $dates = [];

    foreach ($parts as $part) {
        $date = trim((string) $part);
        if (preg_match('/^\d{4}-\d{2}-\d{2}$/', $date)) {
            $dates[] = $date;
        }
    }

    return array_values(array_unique($dates));
}

function chbk_get_entity_blackout_rules($entity_id, $supports_time)
{
    $entity_id = (int) $entity_id;
    if (!$entity_id) {
        return [];
    }

    $rules = chbk_parse_json_meta(get_post_meta($entity_id, 'chbk_blackout_rules', true));
    $normalized = [];
    foreach ($rules as $rule) {
        if (!is_array($rule)) {
            continue;
        }
        $start_date = isset($rule['start_date']) ? (string) $rule['start_date'] : '';
        $end_date = isset($rule['end_date']) ? (string) $rule['end_date'] : '';
        if (!preg_match('/^\d{4}-\d{2}-\d{2}$/', $start_date) || !preg_match('/^\d{4}-\d{2}-\d{2}$/', $end_date)) {
            continue;
        }
        $row = [
            'start_date' => $start_date,
            'end_date' => $end_date,
            'start_time' => '00:00',
            'end_time' => '23:59',
        ];
        if ($supports_time) {
            $start_time = isset($rule['start_time']) ? (string) $rule['start_time'] : '00:00';
            $end_time = isset($rule['end_time']) ? (string) $rule['end_time'] : '23:59';
            $row['start_time'] = preg_match('/^\d{2}:\d{2}$/', $start_time) ? $start_time : '00:00';
            $row['end_time'] = preg_match('/^\d{2}:\d{2}$/', $end_time) ? $end_time : '23:59';
        }
        $normalized[] = $row;
    }

    if (!empty($normalized)) {
        return $normalized;
    }

    // Legacy fallback from simple blackout date list.
    $legacy = chbk_get_trip_blackout_dates($entity_id);
    foreach ($legacy as $date) {
        $normalized[] = [
            'start_date' => $date,
            'end_date' => $date,
            'start_time' => '00:00',
            'end_time' => '23:59',
        ];
    }

    // Legacy fallback for captain unavailable spans.
    if ($supports_time && get_post_type($entity_id) === 'chbk_captain') {
        $legacy_spans = chbk_parse_json_meta(get_post_meta($entity_id, 'chbk_unavailable_spans', true));
        foreach ($legacy_spans as $span) {
            if (!is_array($span)) {
                continue;
            }
            $start_date = isset($span['start_date']) ? (string) $span['start_date'] : '';
            $end_date = isset($span['end_date']) ? (string) $span['end_date'] : '';
            if (!preg_match('/^\d{4}-\d{2}-\d{2}$/', $start_date) || !preg_match('/^\d{4}-\d{2}-\d{2}$/', $end_date)) {
                continue;
            }
            $start_time = isset($span['start_time']) ? (string) $span['start_time'] : '00:00';
            $end_time = isset($span['end_time']) ? (string) $span['end_time'] : '23:59';
            $normalized[] = [
                'start_date' => $start_date,
                'end_date' => $end_date,
                'start_time' => preg_match('/^\d{2}:\d{2}$/', $start_time) ? $start_time : '00:00',
                'end_time' => preg_match('/^\d{2}:\d{2}$/', $end_time) ? $end_time : '23:59',
            ];
        }
    }

    return $normalized;
}

function chbk_is_trip_active($trip_id)
{
    return get_post_meta($trip_id, 'chbk_active_status', true) !== 'inactive';
}

function chbk_is_captain_active($captain_id)
{
    $captain_id = (int) $captain_id;
    if ($captain_id < 1) {
        return true;
    }

    return get_post_meta($captain_id, 'chbk_status', true) !== 'inactive';
}

function chbk_is_boat_active($boat_id)
{
    $boat_id = (int) $boat_id;
    if ($boat_id < 1) {
        return true;
    }

    return get_post_meta($boat_id, 'chbk_status', true) !== 'inactive';
}

function chbk_is_trip_bookable($trip_id)
{
    $trip_id = (int) $trip_id;
    if ($trip_id < 1 || !chbk_is_trip_active($trip_id)) {
        return false;
    }

    $captain_id = (int) get_post_meta($trip_id, 'chbk_captain_id', true);
    if (!chbk_is_captain_active($captain_id)) {
        return false;
    }

    $boat_id = (int) get_post_meta($trip_id, 'chbk_boat_id', true);
    if (!chbk_is_boat_active($boat_id)) {
        return false;
    }

    return true;
}

function chbk_get_bookings_for_slot($trip_id, $trip_datetime_utc)
{
    $bookings = get_posts([
        'post_type' => 'chbk_booking',
        'post_status' => ['publish', 'pending', 'draft'],
        'numberposts' => -1,
    ]);

    $active = [];
    foreach ($bookings as $booking) {
        $booking_trip_id = (int) get_post_meta($booking->ID, 'chbk_trip_id', true);
        if ((int) $trip_id !== $booking_trip_id) {
            continue;
        }

        $booking_datetime_utc = (string) get_post_meta($booking->ID, 'chbk_trip_datetime_utc', true);
        if ((string) $trip_datetime_utc !== $booking_datetime_utc) {
            continue;
        }

        $status = (string) get_post_meta($booking->ID, 'chbk_booking_status', true);
        if (in_array($status, ['cancelled', 'refunded'], true)) {
            continue;
        }
        $active[] = $booking;
    }

    return $active;
}

function chbk_get_booked_party_size($trip_id, $trip_datetime_utc)
{
    $bookings = chbk_get_bookings_for_slot($trip_id, $trip_datetime_utc);
    $total = 0;

    foreach ($bookings as $booking) {
        $total += (int) get_post_meta($booking->ID, 'chbk_party_size', true);
    }

    return $total;
}

function chbk_trip_datetime_to_local($trip_id, $trip_datetime_utc)
{
    $tz = chbk_get_trip_timezone($trip_id);

    try {
        $dt = new DateTime($trip_datetime_utc, new DateTimeZone('UTC'));
        $dt->setTimezone(new DateTimeZone($tz));
        return $dt;
    } catch (Exception $e) {
        return null;
    }
}

function chbk_minutes_from_time($hhmm)
{
    $parts = explode(':', (string) $hhmm);
    if (count($parts) < 2) {
        return 0;
    }

    $h = (int) $parts[0];
    $m = (int) $parts[1];
    return ($h * 60) + $m;
}

function chbk_is_entity_blacked_out_for_slot($entity_id, $supports_time, $slot_start_utc, $slot_end_utc, $timezone)
{
    $entity_id = (int) $entity_id;
    if (!$entity_id) {
        return false;
    }

    try {
        $slot_start = new DateTime($slot_start_utc, new DateTimeZone('UTC'));
        $slot_end = new DateTime($slot_end_utc, new DateTimeZone('UTC'));
    } catch (Exception $e) {
        return false;
    }

    $rules = chbk_get_entity_blackout_rules($entity_id, $supports_time);
    foreach ($rules as $rule) {
        $start_date = isset($rule['start_date']) ? (string) $rule['start_date'] : '';
        $end_date = isset($rule['end_date']) ? (string) $rule['end_date'] : '';
        $start_time = $supports_time && isset($rule['start_time']) ? (string) $rule['start_time'] : '00:00';
        $end_time = $supports_time && isset($rule['end_time']) ? (string) $rule['end_time'] : '23:59';

        if ($start_date === '' || $end_date === '') {
            continue;
        }

        $span_start_utc = chbk_to_utc_datetime($start_date . ' ' . $start_time, $timezone);
        $span_end_utc = chbk_to_utc_datetime($end_date . ' ' . $end_time, $timezone);

        if ($span_start_utc === '' || $span_end_utc === '') {
            continue;
        }

        try {
            $span_start = new DateTime($span_start_utc, new DateTimeZone('UTC'));
            $span_end = new DateTime($span_end_utc, new DateTimeZone('UTC'));
        } catch (Exception $e) {
            continue;
        }

        if ($slot_start < $span_end && $slot_end > $span_start) {
            return true;
        }
    }

    return false;
}

function chbk_validate_availability_window($trip_id, $utc_datetime)
{
    $local_dt = chbk_trip_datetime_to_local($trip_id, $utc_datetime);
    if (!$local_dt) {
        return false;
    }

    $weekday = (int) $local_dt->format('w');
    $date = $local_dt->format('Y-m-d');

    $weekdays = chbk_get_trip_weekdays($trip_id);
    if (!in_array($weekday, $weekdays, true)) {
        return false;
    }

    $season_start = (string) get_post_meta($trip_id, 'chbk_season_start_date', true);
    $season_end = (string) get_post_meta($trip_id, 'chbk_season_end_date', true);
    if ($season_start !== '' && $date < $season_start) {
        return false;
    }
    if ($season_end !== '' && $date > $season_end) {
        return false;
    }

    $start_time = chbk_get_trip_start_time($trip_id);
    $duration = chbk_get_trip_duration_minutes($trip_id);
    $slot_minutes = ((int) $local_dt->format('G') * 60) + (int) $local_dt->format('i');
    $start_minutes = chbk_minutes_from_time($start_time);
    return $slot_minutes === $start_minutes;
}

function chbk_is_trip_available($trip_id, $party_size, $trip_datetime_utc)
{
    if (!chbk_is_trip_bookable($trip_id)) {
        return false;
    }

    if (!chbk_validate_availability_window($trip_id, $trip_datetime_utc)) {
        return false;
    }

    $duration = chbk_get_trip_duration_minutes($trip_id);
    try {
        $slot_start = new DateTime($trip_datetime_utc, new DateTimeZone('UTC'));
    } catch (Exception $e) {
        return false;
    }
    $slot_end = clone $slot_start;
    $slot_end->modify('+' . $duration . ' minutes');
    $slot_end_utc = $slot_end->format('Y-m-d H:i:s');

    $timezone = chbk_get_trip_timezone($trip_id);
    $captain_id = (int) get_post_meta($trip_id, 'chbk_captain_id', true);
    $boat_id = (int) get_post_meta($trip_id, 'chbk_boat_id', true);

    if (chbk_is_entity_blacked_out_for_slot($trip_id, false, $trip_datetime_utc, $slot_end_utc, $timezone)) {
        return false;
    }

    if (chbk_is_entity_blacked_out_for_slot($captain_id, true, $trip_datetime_utc, $slot_end_utc, $timezone)) {
        return false;
    }

    if (chbk_is_entity_blacked_out_for_slot($boat_id, true, $trip_datetime_utc, $slot_end_utc, $timezone)) {
        return false;
    }

    $min = chbk_get_trip_min_capacity($trip_id);
    if ($party_size < $min) {
        return false;
    }

    $single_booking = get_post_meta($trip_id, 'chbk_single_booking_per_slot', true);
    $single_booking = ($single_booking === '') ? 1 : (int) $single_booking;
    if ($single_booking === 1) {
        $existing = chbk_get_bookings_for_slot($trip_id, $trip_datetime_utc);
        return count($existing) === 0;
    }

    $capacity = chbk_get_trip_capacity($trip_id);
    if ($capacity < 1) {
        return false;
    }
    if ($party_size > $capacity) {
        return false;
    }

    $boat_id = (int) get_post_meta($trip_id, 'chbk_boat_id', true);
    $overbooking = (int) get_post_meta($boat_id, 'chbk_allows_overbooking', true);
    if ($overbooking === 1) {
        return true;
    }

    $booked = chbk_get_booked_party_size($trip_id, $trip_datetime_utc);
    return ($booked + $party_size) <= $capacity;
}

function chbk_get_trip_available_slots($trip_id, $party_size = 1, $days_ahead = 90)
{
    $days_ahead = max(1, min(365, (int) $days_ahead));
    $start_time = chbk_get_trip_start_time($trip_id);
    $start_minutes = chbk_minutes_from_time($start_time);

    $tz = chbk_get_trip_timezone($trip_id);
    $weekdays = chbk_get_trip_weekdays($trip_id);
    $blackouts = chbk_get_trip_blackout_dates($trip_id);

    $season_start = (string) get_post_meta($trip_id, 'chbk_season_start_date', true);
    $season_end = (string) get_post_meta($trip_id, 'chbk_season_end_date', true);

    try {
        $now = new DateTime('now', new DateTimeZone($tz));
    } catch (Exception $e) {
        return [];
    }

    $slots = [];

    for ($offset = 0; $offset <= $days_ahead; $offset++) {
        $day = clone $now;
        $day->setTime(0, 0, 0);
        if ($offset > 0) {
            $day->modify('+' . $offset . ' days');
        }

        $weekday = (int) $day->format('w');
        if (!in_array($weekday, $weekdays, true)) {
            continue;
        }

        $date = $day->format('Y-m-d');
        if (($season_start !== '' && $date < $season_start) || ($season_end !== '' && $date > $season_end)) {
            continue;
        }
        if (in_array($date, $blackouts, true)) {
            continue;
        }

        $slot_dt = clone $day;
        $slot_dt->setTime((int) floor($start_minutes / 60), $start_minutes % 60, 0);

        if ($slot_dt < $now) {
            continue;
        }

        $local = $slot_dt->format('Y-m-d H:i');
        $utc = chbk_to_utc_datetime($local, $tz);
        if ($utc === '') {
            continue;
        }

        if (!chbk_is_trip_available($trip_id, $party_size, $utc)) {
            continue;
        }

        $slots[] = [
            'local' => $local,
            'utc' => $utc,
            'label' => $slot_dt->format('D, M j, Y g:i A'),
        ];
    }

    return $slots;
}

function chbk_trip_date_ranges_overlap($a_start, $a_end, $b_start, $b_end)
{
    $a_start = $a_start !== '' ? $a_start : '0000-01-01';
    $a_end = $a_end !== '' ? $a_end : '9999-12-31';
    $b_start = $b_start !== '' ? $b_start : '0000-01-01';
    $b_end = $b_end !== '' ? $b_end : '9999-12-31';

    return !($a_end < $b_start || $b_end < $a_start);
}

function chbk_do_trips_overlap($trip_id_a, $trip_id_b)
{
    $a_weekdays = chbk_get_trip_weekdays($trip_id_a);
    $b_weekdays = chbk_get_trip_weekdays($trip_id_b);
    if (empty(array_intersect($a_weekdays, $b_weekdays))) {
        return false;
    }

    $a_start = chbk_minutes_from_time(chbk_get_trip_start_time($trip_id_a));
    $a_end = $a_start + chbk_get_trip_duration_minutes($trip_id_a);
    $b_start = chbk_minutes_from_time(chbk_get_trip_start_time($trip_id_b));
    $b_end = $b_start + chbk_get_trip_duration_minutes($trip_id_b);

    $time_overlap = ($a_start < $b_end) && ($b_start < $a_end);
    if (!$time_overlap) {
        return false;
    }

    $a_season_start = (string) get_post_meta($trip_id_a, 'chbk_season_start_date', true);
    $a_season_end = (string) get_post_meta($trip_id_a, 'chbk_season_end_date', true);
    $b_season_start = (string) get_post_meta($trip_id_b, 'chbk_season_start_date', true);
    $b_season_end = (string) get_post_meta($trip_id_b, 'chbk_season_end_date', true);

    return chbk_trip_date_ranges_overlap($a_season_start, $a_season_end, $b_season_start, $b_season_end);
}

function chbk_find_overlapping_trips($trip_id)
{
    $trip_id = (int) $trip_id;
    if (!$trip_id || get_post_type($trip_id) !== 'chbk_trip') {
        return [];
    }

    $captain_id = (int) get_post_meta($trip_id, 'chbk_captain_id', true);
    $boat_id = (int) get_post_meta($trip_id, 'chbk_boat_id', true);
    if (!$captain_id || !$boat_id) {
        return [];
    }

    $candidates = get_posts([
        'post_type' => 'chbk_trip',
        'post_status' => ['publish', 'draft', 'pending', 'future', 'private'],
        'numberposts' => -1,
    ]);

    $overlaps = [];
    foreach ($candidates as $candidate) {
        if ((int) $candidate->ID === $trip_id) {
            continue;
        }
        if ((int) get_post_meta($candidate->ID, 'chbk_captain_id', true) !== $captain_id) {
            continue;
        }
        if ((int) get_post_meta($candidate->ID, 'chbk_boat_id', true) !== $boat_id) {
            continue;
        }
        if (chbk_do_trips_overlap($trip_id, $candidate->ID)) {
            $overlaps[] = $candidate;
        }
    }

    return $overlaps;
}

function chbk_calculate_price($trip_id, $party_size, $trip_datetime_utc)
{
    $base_price = (float) get_post_meta($trip_id, 'chbk_base_price', true);
    $seat_price = (float) get_post_meta($trip_id, 'chbk_seat_price', true);
    $trip_type = (string) get_post_meta($trip_id, 'chbk_trip_type', true);
    if ($trip_type === 'private' || $trip_type === '') {
        $trip_type = 'per-group';
    }
    $pricing_rules = chbk_parse_json_meta(get_post_meta($trip_id, 'chbk_pricing_rules', true));
    if ($seat_price <= 0 && isset($pricing_rules['per_person'])) {
        $seat_price = (float) $pricing_rules['per_person'];
    }
    $included_enabled = (int) get_post_meta($trip_id, 'chbk_included_seats_enabled', true) === 1;
    if ($trip_type !== 'hybrid') {
        $included_enabled = false;
    }
    $included_count = (int) get_post_meta($trip_id, 'chbk_included_seats_count', true);
    $included_count = $included_enabled ? max(0, $included_count) : 0;
    $billable_seats = max(0, (int) $party_size - $included_count);

    $total = $base_price;

    if ($trip_type === 'per-seat') {
        $total = $seat_price * $billable_seats;
    }

    if ($trip_type === 'hybrid') {
        $total = $base_price + ($seat_price * $billable_seats);
    }

    if (!empty($pricing_rules['tiered']) && is_array($pricing_rules['tiered'])) {
        foreach ($pricing_rules['tiered'] as $tier) {
            $min = isset($tier['min']) ? (int) $tier['min'] : 0;
            $max = isset($tier['max']) ? (int) $tier['max'] : PHP_INT_MAX;
            if ($party_size >= $min && $party_size <= $max) {
                if (isset($tier['price'])) {
                    $total = (float) $tier['price'];
                }
                if (isset($tier['multiplier'])) {
                    $total *= (float) $tier['multiplier'];
                }
            }
        }
    }

    if (!empty($pricing_rules['seasonal']) && is_array($pricing_rules['seasonal'])) {
        try {
            $dt = new DateTime($trip_datetime_utc, new DateTimeZone('UTC'));
            $month = (int) $dt->format('n');
            foreach ($pricing_rules['seasonal'] as $rule) {
                if (!isset($rule['months']) || !is_array($rule['months'])) {
                    continue;
                }
                if (in_array($month, array_map('intval', $rule['months']), true) && isset($rule['multiplier'])) {
                    $total *= (float) $rule['multiplier'];
                }
            }
        } catch (Exception $e) {
        }
    }

    if (!empty($pricing_rules['add_ons']) && is_array($pricing_rules['add_ons'])) {
        foreach ($pricing_rules['add_ons'] as $add_on) {
            if (isset($add_on['default']) && !empty($add_on['default']) && isset($add_on['price'])) {
                $total += (float) $add_on['price'];
            }
        }
    }

    return (float) apply_filters('chbk_calculate_price_total', max(0, $total), $trip_id, $party_size, $trip_datetime_utc, $pricing_rules);
}

function chbk_calculate_deposit_due($trip_id, $total)
{
    $deposit_rules = chbk_parse_json_meta(get_post_meta($trip_id, 'chbk_deposit_rules', true));
    if (empty($deposit_rules) || empty($deposit_rules['enabled'])) {
        return 0.0;
    }

    if (!empty($deposit_rules['type']) && $deposit_rules['type'] === 'percentage') {
        $percent = isset($deposit_rules['value']) ? (float) $deposit_rules['value'] : 0;
        return round(($total * $percent) / 100, 2);
    }

    return isset($deposit_rules['value']) ? (float) $deposit_rules['value'] : 0.0;
}

function chbk_create_booking($args)
{
    $trip_id = isset($args['trip_id']) ? absint($args['trip_id']) : 0;
    $party_size = isset($args['party_size']) ? absint($args['party_size']) : 1;
    $customer_name = isset($args['customer_name']) ? sanitize_text_field($args['customer_name']) : '';
    $customer_email = isset($args['customer_email']) ? sanitize_email($args['customer_email']) : '';
    $trip_datetime_local = isset($args['trip_datetime']) ? sanitize_text_field($args['trip_datetime']) : '';
    if (strpos($trip_datetime_local, 'T') !== false) {
        $trip_datetime_local = str_replace('T', ' ', $trip_datetime_local);
    }
    if (preg_match('/^\d{4}-\d{2}-\d{2}$/', $trip_datetime_local)) {
        $trip_datetime_local .= ' 00:00';
    }

    if (!$trip_id || !$customer_email || !$trip_datetime_local) {
        return new WP_Error('invalid_booking', 'Missing booking fields.');
    }

    $captain_id = (int) get_post_meta($trip_id, 'chbk_captain_id', true);
    $boat_id = (int) get_post_meta($trip_id, 'chbk_boat_id', true);
    $trip_timezone = chbk_get_trip_timezone($trip_id);
    $trip_datetime_utc = chbk_to_utc_datetime($trip_datetime_local, $trip_timezone);

    if (!chbk_is_trip_available($trip_id, $party_size, $trip_datetime_utc)) {
        return new WP_Error('not_available', 'Trip is not available for that time and party size.');
    }

    $total = chbk_calculate_price($trip_id, $party_size, $trip_datetime_utc);
    $deposit_due = chbk_calculate_deposit_due($trip_id, $total);

    $booking_id = wp_insert_post([
        'post_type' => 'chbk_booking',
        'post_status' => 'publish',
        'post_title' => sprintf('Booking - %s - %s', get_the_title($trip_id), wp_date('Y-m-d H:i')),
    ]);

    if (is_wp_error($booking_id)) {
        return $booking_id;
    }

    $values = [
        'trip_id' => $trip_id,
        'boat_id' => $boat_id,
        'captain_id' => $captain_id,
        'user_id' => get_current_user_id(),
        'party_size' => $party_size,
        'booking_status' => 'pending',
        'payment_status' => 'pending_payment',
        'order_id' => 0,
        'checkin_status' => 'not_checked_in',
        'trip_datetime_utc' => $trip_datetime_utc,
        'total_price' => $total,
        'deposit_due' => $deposit_due,
        'customer_name' => $customer_name,
        'customer_email' => $customer_email,
    ];

    foreach ($values as $key => $value) {
        update_post_meta($booking_id, 'chbk_' . $key, $value);
    }

    do_action('chbk_booking_created', $booking_id, $values);

    return $booking_id;
}

function chbk_update_booking_status($booking_id, $booking_status, $payment_status = '')
{
    update_post_meta($booking_id, 'chbk_booking_status', sanitize_text_field($booking_status));

    if ($payment_status !== '') {
        update_post_meta($booking_id, 'chbk_payment_status', sanitize_text_field($payment_status));
    }

    do_action('chbk_booking_status_changed', $booking_id, $booking_status, $payment_status);
}
