<?php
/**
 * Shared serialization for booking mutations.
 *
 * Every write path uses the same per-calendar-day advisory lock so interval
 * conflict checks and their following INSERT/UPDATE cannot race each other.
 */
if ( ! defined( 'ABSPATH' ) ) {
    exit;
}

class YATOON_Booking_Lock {
    /**
     * Acquire all calendar-day locks touched by an interval.
     *
     * @param DateTimeInterface|string $start_at Booking start in site time.
     * @param DateTimeInterface|string $end_at   Booking end in site time.
     * @param int                      $timeout  Seconds to wait per lock.
     * @return string[]|false
     */
    public static function acquire( $start_at, $end_at = null, $timeout = 8 ) {
        global $wpdb;

        $timezone = function_exists( 'yatoon_timezone' ) ? yatoon_timezone() : wp_timezone();
        $dates    = array();
        foreach ( array( $start_at, $end_at ) as $value ) {
            if ( empty( $value ) ) {
                continue;
            }
            try {
                if ( $value instanceof DateTimeInterface ) {
                    $date = new DateTime( '@' . $value->getTimestamp() );
                    $date->setTimezone( $timezone );
                } else {
                    $date = new DateTime( (string) $value, $timezone );
                }
                $dates[] = $date->format( 'Y-m-d' );
            } catch ( Exception $exception ) {
                $dates[] = 'unparseable';
            }
        }

        $dates = array_values( array_unique( array_filter( $dates ) ) );
        if ( empty( $dates ) ) {
            return false;
        }
        sort( $dates );

        $locks = array();
        foreach ( $dates as $date ) {
            $lock = 'yatoon_day_' . md5( home_url() . '|' . $date );
            $got  = (int) $wpdb->get_var( $wpdb->prepare( 'SELECT GET_LOCK(%s, %d)', $lock, max( 1, (int) $timeout ) ) ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery
            if ( 1 !== $got ) {
                self::release( $locks );
                return false;
            }
            $locks[] = $lock;
        }

        return $locks;
    }

    /** @param string[]|string $locks Lock names returned by acquire(). */
    public static function release( $locks ) {
        global $wpdb;
        foreach ( (array) $locks as $lock ) {
            if ( $lock ) {
                $wpdb->get_var( $wpdb->prepare( 'SELECT RELEASE_LOCK(%s)', $lock ) ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery
            }
        }
    }
}
