<?php
/**
 * Helper functions for Eventim US Event Listings plugin.
 *
 * @package EventimUSEventListings
 */

defined( 'ABSPATH' ) || exit;

// See Tickets custom header code
/**
 * Add custom header code for shortcodes.
 *
 * @return void
 */
function evusel_custom_header_code() {

	global $post, $evuselConfig;

	$style = '';

	$style .= '.seetickets-buy-btn,
                .seetickets-load-more-btn {
                    background-color: ' . esc_attr( $evuselConfig->get( 'color_theme.button_background_color' ) ) . ' !important;
                    color: ' . esc_attr( $evuselConfig->get( 'color_theme.button_text_color' ) ) . " !important;
                }\n
                .seetickets-buy-btn:hover,
                .seetickets-load-more-btn:hover {
                    color: " . esc_attr( $evuselConfig->get( 'color_theme.button_text_hover_color' ) ) . " !important;
                }\n";

	if ( ! empty( $post ) ) {

		// Social icons background color
		if (
			has_shortcode( $post->post_content, $evuselConfig::SHORTCODE_CALENDAR_VIEW ) ||
			has_shortcode( $post->post_content, $evuselConfig::SHORTCODE_LIST_VIEW ) ||
			has_shortcode( $post->post_content, $evuselConfig::SHORTCODE_CATEGORY_FILTER ) ||
			has_shortcode( $post->post_content, $evuselConfig::SHORTCODE_TAG_FILTER ) ||
			has_shortcode( $post->post_content, $evuselConfig::SHORTCODE_EVENT_SEARCH ) ||
			has_shortcode( $post->post_content, $evuselConfig::SHORTCODE_JUST_ANNOUNCED_EVENTS ) ||
			$post->post_type === 'seetickets-event'
		) {
			$style .= ".social-circle {
                background-color: " . esc_attr( $evuselConfig->get( 'social_icon_background_color' ) ) . ";
            }\n";
			$style .= ".social-circle svg, .social-icon svg {
                fill: " . esc_attr( $evuselConfig->get( 'social_icon_color' ) ) . ";
            }\n";
		}

		// Calendar shortcode
		if (
			has_shortcode( $post->post_content, $evuselConfig::SHORTCODE_CALENDAR_VIEW )
		) {
			$style .= ".seetickets-calendar-event-container {
                background-color: " . esc_attr( $evuselConfig->get( 'color_theme.background_color' ) ) . ";
            }\n
            .seetickets-calendar-event-container > * {
                color: " . esc_attr( $evuselConfig->get( 'color_theme.text_color' ) ) . ";
            }\n";

			// Add Material Design styles dynamically
			$materialStyle = $evuselConfig->get( 'shortcodes.calendar_view.material_list_style' );
			if ( $materialStyle === 'outlined' ) {
				$style .= ".seetickets-calendar-event-container.mdc-card--outlined {
                    box-shadow: none !important;
                    border-width: 1px !important;
                    border-style: solid !important;
                    border-color: #e0e0e0 !important;
                    border-radius: 0 !important;
                }\n";
			} else {
				$style .= ".seetickets-calendar-event-container.mdc-card--elevated {
                    box-shadow: 0px 2px 1px -1px rgba(0,0,0,0.2), 0px 1px 1px 0px rgba(0,0,0,0.14), 0px 1px 3px 0px rgba(0,0,0,0.12) !important;
                    border: none !important;
                    border-radius: 0 !important;
                }\n";
			}

			wp_enqueue_script( 'see-calendar-js' );
		}

		// List view shortcode
		if (
			has_shortcode( $post->post_content, $evuselConfig::SHORTCODE_LIST_VIEW )
		) {
			$style .= ".seetickets-list-event-container {
                background-color: " . esc_attr( $evuselConfig->get( 'color_theme.background_color' ) ) . ";
            }\n
            .seetickets-list-event-container > * {
                color: " . esc_attr( $evuselConfig->get( 'color_theme.text_color' ) ) . ";
            }\n";

			// Add Material Design styles dynamically
			$materialStyle = $evuselConfig->get( 'shortcodes.list_view.material_list_style' );
			if ( $materialStyle === 'outlined' ) {
				$style .= ".seetickets-list-event-container.mdc-card--outlined {
                    box-shadow: none !important;
                    border-width: 1px !important;
                    border-style: solid !important;
                    border-color: #e0e0e0 !important;
                    border-radius: 0 !important;
                }\n";
			} else {
				$style .= ".seetickets-list-event-container.mdc-card--elevated {
                    box-shadow: 0px 2px 1px -1px rgba(0,0,0,0.2), 0px 1px 1px 0px rgba(0,0,0,0.14), 0px 1px 3px 0px rgba(0,0,0,0.12) !important;
                    border: none !important;
                    border-radius: 0 !important;
                }\n";
			}

			wp_enqueue_script( 'see-list-js' );
		}

		if (
			has_shortcode( $post->post_content, $evuselConfig::SHORTCODE_EVENT_SEARCH ) ||
			has_shortcode( $post->post_content, $evuselConfig::SHORTCODE_CATEGORY_FILTER ) ||
			has_shortcode( $post->post_content, $evuselConfig::SHORTCODE_TAG_FILTER ) ||
			has_shortcode( $post->post_content, $evuselConfig::SHORTCODE_PAST_EVENTS ) ||
			has_shortcode( $post->post_content, $evuselConfig::SHORTCODE_JUST_ANNOUNCED_EVENTS ) ||
			is_single() && $post->post_type === 'seetickets-event'
		) {
			wp_enqueue_script( 'see-list-js' );
		}

		// Slider shortcode
		if ( has_shortcode( $post->post_content, $evuselConfig::SHORTCODE_SLIDER ) ) {
			wp_enqueue_script( 'swiper-bundle-js' );
			wp_enqueue_script( 'evusel-swiper-init' );
			wp_enqueue_style( 'swiper-bundle-css' );
		}

		// Various
		if (
			has_shortcode( $post->post_content, $evuselConfig::SHORTCODE_CALENDAR_VIEW ) ||
			has_shortcode( $post->post_content, $evuselConfig::SHORTCODE_LIST_VIEW ) ||
			has_shortcode( $post->post_content, $evuselConfig::SHORTCODE_EVENT_SEARCH ) ||
			has_shortcode( $post->post_content, $evuselConfig::SHORTCODE_JUST_ANNOUNCED_EVENTS ) ||
			has_shortcode( $post->post_content, $evuselConfig::SHORTCODE_CATEGORY_FILTER ) ||
			has_shortcode( $post->post_content, $evuselConfig::SHORTCODE_PAST_EVENTS ) ||
			has_shortcode( $post->post_content, $evuselConfig::SHORTCODE_TAG_FILTER ) ||
			is_single() && $post->post_type === 'seetickets-event'
		) {

			wp_enqueue_style( 'material-components-web-css' );
			// wp_enqueue_script('material-components-web-js');

			$style .= ".seetickets-list-event-container {
                background-color: " . esc_attr( $evuselConfig->get( 'color_theme.background_color' ) ) . ";
            }\n
            .seetickets-list-event-container * {
                color: " . esc_attr( $evuselConfig->get( 'color_theme.text_color' ) ) . ";
            }\n";
		}

		// All shortcodes
		if (
			has_shortcode( $post->post_content, $evuselConfig::SHORTCODE_CALENDAR_VIEW ) ||
			has_shortcode( $post->post_content, $evuselConfig::SHORTCODE_LIST_VIEW ) ||
			has_shortcode( $post->post_content, $evuselConfig::SHORTCODE_CATEGORY_FILTER ) ||
			has_shortcode( $post->post_content, $evuselConfig::SHORTCODE_TAG_FILTER ) ||
			has_shortcode( $post->post_content, $evuselConfig::SHORTCODE_SLIDER ) ||
			has_shortcode( $post->post_content, $evuselConfig::SHORTCODE_EVENT_SEARCH ) ||
			has_shortcode( $post->post_content, $evuselConfig::SHORTCODE_JUST_ANNOUNCED_EVENTS ) ||
			has_shortcode( $post->post_content, $evuselConfig::SHORTCODE_PAST_EVENTS )
		) {
			wp_enqueue_style( 'google-fonts', 'https://fonts.googleapis.com/css2?family=Roboto:wght@400;700&display=swap', array(), '2.0' );

		}
	}

	if ( $evuselConfig->get( 'debug.force_script_load' ) === 'on' ) {
		wp_enqueue_script( 'see-calendar-js' );
		wp_enqueue_script( 'see-list-js' );
		wp_enqueue_script( 'swiper-bundle-js' );
		wp_enqueue_script( 'evusel-swiper-init' );
		wp_enqueue_style( 'swiper-bundle-css' );
	}

	if ( ! empty( $style ) ) {
		wp_add_inline_style( 'evusel-custom-styles', $style );
	}
}

add_action( 'wp_ajax_evusel_get_default_event_image', 'evusel_get_default_event_image' );

/**
 * Get default event image via AJAX.
 *
 * @return void
 */
function evusel_get_default_event_image() {
	if ( ! current_user_can( 'upload_files' ) ) {
		wp_send_json_error( array( 'message' => 'Unauthorized' ) );
		exit;
	}

	$nonce = isset( $_GET['nonce'] ) ? sanitize_text_field( wp_unslash( $_GET['nonce'] ) ) : '';
	if ( ! wp_verify_nonce( $nonce, 'evusel-admin-nonce' ) ) {
		wp_send_json_error( array( 'message' => 'Invalid or missing nonce' ) );
		exit;
	}

	$defaultImageId = isset( $_GET['default_image_id'] ) ? sanitize_text_field( wp_unslash( $_GET['default_image_id'] ) ) : '';
	if ( empty( $defaultImageId ) ) {
		wp_send_json_error( array( 'message' => 'Missing image ID' ) );
		exit;
	}
	$image = wp_get_attachment_image_url( $defaultImageId, 'large', false );

	if ( ! $image ) {
		wp_send_json_error( array( 'message' => 'Invalid image ID' ) );
		exit;
	}

	wp_send_json_success( array( 'image' => $image ) );
	exit;
}

/**
 * Get event image URL.
 *
 * @param int $id Event post ID.
 * @return string Image URL.
 * @since 0.10.0
 */
function evusel_get_event_image( $id ) {
	global $evuselConfig;
	$importedMedia = get_post_meta( $id, 'media_imported', true ) !== 'no';

	$useExternalMedia = $evuselConfig->get( 'import.import_to_wp_media_lib' ) !== 'on';

	if ( wp_get_attachment_image_url( get_post_thumbnail_id( $id ), 'large' ) ) {
		return wp_get_attachment_image_url( get_post_thumbnail_id( $id ), 'large' );
	}

	if ( get_post_meta( $id, 'image_sm', true ) ) {
		return get_post_meta( $id, 'image_sm', true );
	}

	return $evuselConfig->get_default_event_image_url();
}

/**
 * Get door time label.
 *
 * @return string Door time label.
 * @since 0.10.0
 */
function evusel_get_doortime_labe() {
	global $evuselConfig;
	$doorTimeLabelIsHidden = $evuselConfig->get( 'custom_labels.door_time.is_hidden' ) === 'on';
	return $doorTimeLabelIsHidden ? '' : $evuselConfig->get( 'custom_labels.door_time.label' ) . ' ';
}

/**
 * Get show time label.
 *
 * @return string Show time label.
 * @since 0.10.0
 */
function evusel_get_showtime_label() {
	global $evuselConfig;
	$showTimeLabelIsHidden = $evuselConfig->get( 'custom_labels.show_time.is_hidden' ) === 'on';
	return $showTimeLabelIsHidden ? '' : $evuselConfig->get( 'custom_labels.show_time.label' ) . ' ';
}

/**
 * Get door time or show time for event.
 *
 * @param string $type    Type: 'door' or 'show'.
 * @param int    $eventId Event post ID.
 * @return string Formatted time string.
 */
function evusel_get_doortime_showtime( string $type, $eventId ) {
	$doorTime = ! empty( get_post_meta( $eventId, 'door_time', true ) ) ? evusel_get_event_display_date_time( $eventId, 'door' ) : '';
	$doorTime = ! empty( $doorTime ) ? $doorTime->format( 'g:iA' ) : $doorTime;
	$showTime = ! empty( get_post_meta( $eventId, 'event_time', true ) ) ? evusel_get_event_display_date_time( $eventId ) : '';
	$showTime = ! empty( $showTime ) ? $showTime->format( 'g:iA' ) : $showTime;

	// Apply filters for doortime and showtime classes based on type
	$doortimeClasses = 'see-doortime ';
	$showtimeClasses = 'see-showtime ';

	if ( $type === 'list' ) {
		$doortimeClasses = apply_filters( 'evusel_list_doortime_classes', $doortimeClasses, $eventId );
		$showtimeClasses = apply_filters( 'evusel_list_showtime_classes', $showtimeClasses, $eventId );
	} elseif ( $type === 'grid' ) {
		$doortimeClasses = apply_filters( 'evusel_grid_doortime_classes', $doortimeClasses, $eventId );
		$showtimeClasses = apply_filters( 'evusel_grid_showtime_classes', $showtimeClasses, $eventId );
	} elseif ( $type === 'calendar' ) {
		$doortimeClasses = apply_filters( 'evusel_calendar_doortime_classes', $doortimeClasses, $eventId );
		$showtimeClasses = apply_filters( 'evusel_calendar_showtime_classes', $showtimeClasses, $eventId );
	} elseif ( $type === 'slider' ) {
		$doortimeClasses = apply_filters( 'evusel_slider_doortime_classes', $doortimeClasses, $eventId );
		$showtimeClasses = apply_filters( 'evusel_slider_showtime_classes', $showtimeClasses, $eventId );
	}
	$divider      = apply_filters( 'seetickets-doortime-showtime-divider', ' / ' );
	$dividerClass = apply_filters( 'seetickets-doortime-showtime-divider-classes', 'see-times-divider ' );

	$doorTimeShowTime = '';

	$eventTime = get_post_meta( $eventId, 'event_time_r', true );

	if ( $eventTime == '' ) {
		$doorTimeShowTime .= ! empty( $doorTime ) ? evusel_get_doortime_labe() . "<span id=\"see-{$type}-doortime-{$eventId}\" class=\"$doortimeClasses\">$doorTime</span>" : '';
	} else {
		$doorTimeShowTime .= ! empty( $doorTime ) ? evusel_get_doortime_labe() . "<span id=\"see-{$type}-doortime-{$eventId}\" class=\"$doortimeClasses\">$doorTime</span>" : '';
		$doorTimeShowTime .= ! empty( $doorTime ) && ! empty( $showTime ) ? "<span class=\"$dividerClass\">$divider</span>" : '';
		$doorTimeShowTime .= ! empty( $showTime ) ? evusel_get_showtime_label() . "<span id=\"see-{$type}-showtime-{$eventId}\" class=\"$showtimeClasses\">$showTime</span>" : '';
	}

	return $doorTimeShowTime;
}

/**
 * Get supporting talent label.
 *
 * @return string Supporting talent label.
 * @since 0.10.0
 */
function evusel_get_supporting_talent_label() {
	global $evuselConfig;
	$isHidden = $evuselConfig->get( 'custom_labels.supporting_talent.is_hidden' ) === 'on';
	return $isHidden ? '' : $evuselConfig->get( 'custom_labels.supporting_talent.label' ) . ' ';
}

// Display certan times in the events' timezone
add_filter( 'acf/prepare_field', 'evusel_custom_acf_prepare_field' );
/**
 * Custom ACF prepare field filter.
 *
 * @param array $field ACF field array.
 * @return array Modified field array.
 */
function evusel_custom_acf_prepare_field( $field ) {
	$fieldsToModify = array( 'event_time', 'door_time', 'end_time', 'sort_date', 'publish_date' );
	$fieldName      = $field['_name'];

	if (
		is_admin()
		&& ! defined( 'EVUSEL_RUNNING_CRON' )
		&& in_array( $fieldName, $fieldsToModify )
		&& ! empty( $field['value'] )
	) {

		global $post;

		switch ( $fieldName ) {

			case 'publish_date':
				$publishDateTime = evusel_get_event_display_publish_date_time( $post->ID );
				if ( ! empty( $publishDateTime ) ) {
					$field['value'] = $publishDateTime->format( 'Y/m/d H:i:s' );
				}
				break;

			case 'event_time':
			case 'door_time':
			case 'end_time':
				$fieldName     = str_replace( '_time', '', $fieldName );
				$eventDateTime = evusel_get_event_display_date_time( $post->ID, $fieldName );
				if ( ! empty( $eventDateTime ) ) {
					$field['value'] = $eventDateTime->format( 'g:i A' );
				}
				break;
		}
	}

	return $field;
}

// Save certain event times in UTC.
add_filter( 'acf/update_value', 'evusel_custom_acf_update_value', 10, 3 );
/**
 * Custom ACF update value filter.
 *
 * @param mixed  $value   Field value.
 * @param int    $post_id Post ID.
 * @param array  $field   ACF field array.
 * @return mixed Modified value.
 */
function evusel_custom_acf_update_value( $value, $post_id, $field ) {
	$fieldsToModify = array( 'event_time', 'door_time', 'end_time', 'sort_date', 'publish_date' );
	$fieldName      = $field['_name'];

	if (
		is_admin()
		&& ! defined( 'EVUSEL_RUNNING_CRON' )
		&& in_array( $fieldName, $fieldsToModify )
		&& ! empty( $value )
	) {

		global $post;
		$utcDateTimeZone = new DateTimeZone( 'UTC' );
        // phpcs:ignore WordPress.Security.NonceVerification.Missing
		$advancedCustomField_bdbe = isset( $_POST['acf']['field_63696f6ebdb4e'] ) ? sanitize_text_field( wp_unslash( $_POST['acf']['field_63696f6ebdb4e'] ) ) : '';
		$eventTimeZoneString      = $advancedCustomField_bdbe ?: 'UTC';
		$eventTimeZone            = new DateTimeZone( evusel_get_valid_timezone( $eventTimeZoneString ) );

		switch ( $fieldName ) {

			case 'publish_date':
				$publishDateTime = DateTime::createFromFormat( 'Y-m-d H:i:s', $value, $eventTimeZone );
				$publishDateTime->setTimezone( $utcDateTimeZone );

				$value = $publishDateTime->format( 'Y-m-d H:i:s' );

				break;

			case 'door_time':
			case 'event_time':
			case 'end_time':
				// Convert the time-picker value (g:i A) to hours and minutes.
				$now  = DateTime::createFromFormat( 'Ymd g:i A', gmdate( 'Ymd' ) . ' ' . $value );
				$hour = $now ? (int) $now->format( 'H' ) : 0;
				$min  = $now ? (int) $now->format( 'i' ) : 0;

				// Use the sort_date from POST as the date portion — always reliable at save time.
                // phpcs:ignore WordPress.Security.NonceVerification.Missing
				$acfDateField = isset( $_POST['acf']['field_6049443d3ed27'] ) ? sanitize_text_field( wp_unslash( $_POST['acf']['field_6049443d3ed27'] ) ) : '';

				if ( ! empty( $acfDateField ) ) {
					$dateFromPost = DateTime::createFromFormat( 'Ymd', $acfDateField, $eventTimeZone );
					if ( $dateFromPost ) {
						$dateFromPost->setTime( $hour, $min );
						$dateFromPost->setTimezone( $utcDateTimeZone );
						$value = $dateFromPost->format( 'Y-m-d H:i:s' );
					}
				} else {
					// Fallback: read existing stored UTC datetime and replace only the time portion.
					$metaKey       = $fieldName;
					$existingMeta  = get_post_meta( $post->ID, $metaKey, true );
					$eventDateTime = ! empty( $existingMeta ) ? DateTime::createFromFormat( 'Y-m-d H:i:s', $existingMeta, $utcDateTimeZone ) : false;
					if ( $eventDateTime ) {
						$eventDateTime->setTimezone( $eventTimeZone );
						$eventDateTime->setTime( $hour, $min );
						$eventDateTime->setTimezone( $utcDateTimeZone );
						$value = $eventDateTime->format( 'Y-m-d H:i:s' );
					}
				}

				break;
		}
	}

	return $value;
}

/**
 * Delete obsolete events for given client IDs.
 *
 * @param array $clientIds Array of client IDs.
 * @return void
 */
function evusel_delete_obsolete_events( array $clientIds ) {
	global $wpdb;

	$postIdsToDelete = $wpdb->prepare(
		"
        SELECT post_id from {$wpdb->prefix}postmeta
        JOIN {$wpdb->prefix}posts on ID = post_id
        WHERE meta_key = 'client_id'
        AND meta_value in (%d);
    ",
		$clientIds   = implode( ',', $clientIds )
	);

	foreach ( $postIdsToDelete as $postId ) {
		$attachment_id = get_post_thumbnail_id( $postId->post_id );
		wp_delete_attachment( $attachment_id, true );
		wp_delete_post( $postId->post_id );
	}
}

/**
 * Delete obsolete terms for given client IDs.
 *
 * @param array $clientIds Array of client IDs.
 * @return void
 */
function evusel_delete_obsolete_terms( array $clientIds ) {
	$terms = get_terms(
		array(
			'meta_key'     => 'client_id',
			'meta_value'   => $clientIds,
			'meta_compare' => 'IN',
		)
	);

	foreach ( $terms as $term ) {
		wp_delete_term( $term->term_id, 'seetickets-event-tag' );
	}
}

/**
 * Import media for event.
 *
 * @param array $seeEvent Event data array.
 * @param int   $id       Event post ID.
 * @return int|false Attachment ID on success, false on failure.
 */
function evusel_import_media( array $seeEvent, $id ) {
	$imageUrl = $seeEvent['image-sm'];

	if ( ! empty( $imageUrl ) ) {

		$parsedImageUrl = wp_parse_url( $imageUrl );
		$imageHost      = $parsedImageUrl['host'];
		$imageHostArr   = explode( '.', $imageHost );

		$file['name'] = str_replace( '/', '', $parsedImageUrl['path'] );
		if ( in_array( 'cloudfront', $imageHostArr ) ) {
			$file['name'] .= '.jpg';
		}

		if ( ! function_exists( 'download_url' ) ) {
			require_once ABSPATH . 'wp-admin/includes/file.php';
		}
		$file['tmp_name'] = download_url( $imageUrl );

		if ( is_wp_error( $file['tmp_name'] ) ) {
			wp_delete_file( $file['tmp_name'] );
		} else {
			if ( ! function_exists( 'media_handle_sideload' ) ) {
				require_once ABSPATH . 'wp-admin/includes/image.php';
				require_once ABSPATH . 'wp-admin/includes/media.php';
			}
			$attachmentId = @media_handle_sideload( $file, $id );
			if ( is_wp_error( $attachmentId ) ) {
				wp_delete_file( $file['tmp_name'] );
			} else {
				set_post_thumbnail( $id, $attachmentId );
				return $attachmentId;
			}
		}
	}

	return null;
}

/**
 * Create anchorized event title link.
 *
 * @param string  $title      Title text.
 * @param WP_Post $event      Event post object.
 * @param string  $classes    Additional CSS classes.
 * @param bool    $getOriginal Whether to get original link.
 * @return string Anchorized HTML string.
 * @since 0.22.3
 */
function evusel_anchorize( $title, $event, $classes = '', $getOriginal = false ) {
	global $evuselConfig;

	$ticketLink = evusel_get_ticket_link( $event, $getOriginal );

	$classes = ! empty( $classes ) ? "class=\"$classes\"" : '';
	$target  = $evuselConfig->get( 'open_event_page_to_new_tab' ) === 'on' ? 'target="_blank"' : '';

	if ( empty( $ticketLink ) ) {
		return $title;
	}

	return '<a href=' . $ticketLink . " $target $classes>$title</a>";
}

/**
 * Output error notification.
 *
 * @param Throwable|Exception $e        Exception or throwable object.
 * @param array               $seeEvent Event data array.
 * @return void
 * @since 0.24.2b
 */
function evusel_output_error_notification( $e, $seeEvent ) {
	$errorText    = 'An error has occured while running import: ' . esc_html( $e->getMessage() ) . '<br>';
	$errorText   .= 'Thrown in <code>' . esc_html( $e->getFile() ) . '</code> on line ' . esc_html( $e->getLine() ) . ' while importing event <b>' . esc_html( $seeEvent['title'] ) . '</b>.';
	$allowed_html = array(
		'div'  => array( 'class' => array() ),
		'p'    => array(),
		'br'   => array(),
		'code' => array(),
		'b'    => array(),
	);
	echo wp_kses( "<div class=\"notice error is-dismissible\" ><p>Received answer from Event Feed(s), error: {$errorText}</p></div>", $allowed_html );
}

/**
 * Get current date/time for event in event's timezone.
 *
 * @param WP_Post $event Event post object.
 * @return DateTime Current date/time object.
 * @since 0.24.3b
 */
function evusel_GetNowDateTimeForEvent( $event ) {
	$eventTimeZoneString = get_post_meta( $event->ID, 'ianaTimezone', true );
	if ( ! empty( $eventTimeZoneString ) ) {
		$validTimezone     = evusel_get_valid_timezone( $eventTimeZoneString );
		$eventDateTimeZone = new DateTimeZone( $validTimezone );
		return ( new DateTime() )->setTimezone( $eventDateTimeZone );
	} else {
		return new DateTime();
	}
}

/**
 * Get ACF fields.
 *
 * @return array ACF fields array.
 * @since 0.25.0b
 */
function evusel_get_acf_fields() {
	return acf_get_fields( 'group_604943e142cc5' );
}

/**
 * Get update excludable fields.
 *
 * @return array Excludable fields array.
 * @since 0.25.0b
 */
function evusel_get_update_excludable_fields() {
	$fields = array_merge(
		evusel_get_acf_fields(),
		array(
			array(
				'key'   => 'image_01010101', // Can be anything, just make it unique
				'label' => 'Featured Image',
				'name'  => 'image',
			),
		)
	);

	return ( array_filter(
		$fields,
		function ( $field ) {
			return strpos( $field['name'], 'custom_field' ) === false;
		}
	) );
}

/**
 * Get custom labels.
 *
 * @return array Custom labels array.
 * @since 0.25.0b
 */
function evusel_get_custom_labels() {
	return array(
		array(
			'key'         => 'supporting_talent',
			'label'       => 'SUPPORTING TALENT',
			'placeholder' => 'with',
		),
		array(
			'key'         => 'door_time',
			'label'       => 'DOOR TIME',
			'placeholder' => 'Door',
		),
		array(
			'key'         => 'event_time',
			'label'       => 'EVENT TIME',
			'placeholder' => 'Event Time',
		),
		array(
			'key'         => 'show_time',
			'label'       => 'SHOW TIME',
			'placeholder' => 'Show',
		),
	);
}

/**
 * Converts all supported thumbnails to webp.
 *
 * @return void
 * @since 0.25.0b
 */
function evusel_convert_to_webp() {
	if ( ! current_user_can( 'manage_options' ) ) {
		wp_die( esc_html__( 'Unauthorized access', 'eventim-us-event-listings' ) );
	}

	$nonce = isset( $_POST['nonce'] ) ? sanitize_text_field( wp_unslash( $_POST['nonce'] ) ) : '';

	if ( ! wp_verify_nonce( $nonce, 'evusel-admin-nonce' ) ) {
		wp_die( esc_html__( 'Security check failed', 'eventim-us-event-listings' ) );
	}

	if ( ! extension_loaded( 'gd' ) ) {
		wp_die( 'Error: The required PHP extension "GD" was not found.' );
	}

	if ( ! function_exists( 'imagecreatefromjpeg' ) ) {
		wp_die( 'Error: jpeg support is not enabled for GD.' );
	}

	if ( ! function_exists( 'imagewebp' ) || gd_info()['WebP Support'] == false ) {
		wp_die( 'Error: webp support is not enabled for GD.' );
	}

	global $wpdb, $evuselConfig;

	$quality = $evuselConfig->get( 'webp_compression_quality' );

	$cache_key = 'evusel_webp_post_ids_' . md5( 'media_imported' . 'true' );
	$postIds   = wp_cache_get( $cache_key, 'seetickets' );
	if ( false === $postIds ) {
        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery -- Necessary for batch processing, cached above
		$postIds = $wpdb->get_col(
			$wpdb->prepare(
				"SELECT post_id FROM $wpdb->postmeta WHERE meta_key = %s AND meta_value = %s",
				'media_imported',
				'yes'
			)
		);
		wp_cache_set( $cache_key, $postIds, 'seetickets', HOUR_IN_SECONDS );
	}

	foreach ( $postIds as $postId ) {

		$thumbnailId = get_post_thumbnail_id( $postId );

		if ( ! empty( $thumbnailId ) ) {

			$attachment = get_post( $thumbnailId );
			$mimeType   = $attachment->post_mime_type;

			if ( ! empty( $attachment ) ) {

				switch ( $mimeType ) {

					case 'image/png':
						$createrMethod = 'imagecreatefrompng';
						break;
					case 'image/jpeg':
					case 'image/jpg':
						$createrMethod = 'imagecreatefromjpeg';
						break;
					case 'image/webp':
						$createrMethod = 'imagecreatefromwebp';
						break;
					default:
                        // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_trigger_error -- Valid error logging
						trigger_error( 'See Tickets webp conversion: not supported source image format.', E_USER_NOTICE );
						continue 2;
						break;
				}

				$uploadsDir   = wp_get_upload_dir()['basedir'];
				$filenameBase = $attachment->post_name;

				$attachmentMetadata = wp_get_attachment_metadata( $attachment->ID );

				$file      = $attachmentMetadata['file'];
				$fileArray = explode( '/', $file );
				$folder    = "$fileArray[0]/$fileArray[1]";

				// Main File
				$originalFilePath = "$uploadsDir/$file";
				$originalFileSize = filesize( $originalFilePath );
				if ( file_exists( $originalFilePath ) && is_file( $originalFilePath ) ) {
					$originalImage = call_user_func( $createrMethod, $originalFilePath );
					$filenameBase  = $folder . '/' . pathinfo( $file, PATHINFO_FILENAME );
					$newFilePath   = "$uploadsDir/{$filenameBase}.webp";

					imagewebp( $originalImage, $newFilePath . '_', $quality );
					$newFilesize = filesize( $newFilePath . '_' );

					wp_delete_file( $originalFilePath );
					WP_Filesystem::move( $newFilePath . '_', $newFilePath );

					$attachmentMetadata['file']     = "{$filenameBase}.webp";
					$attachmentMetadata['filesize'] = $newFilesize;

					wp_update_post(
						array(
							'ID'             => $attachment->ID,
							'post_mime_type' => 'image/webp',
						// 'guid'              => explode('.', $attachment->guid)[0] . '.webp',
						)
					);
					update_post_meta( $attachment->ID, '_wp_attached_file', "{$filenameBase}.webp" );
				}

				// Original File
				$originalFile     = $attachmentMetadata['original_image'] ?? '';
				$originalFilePath = "$uploadsDir/$folder/$originalFile";
				if ( file_exists( $originalFilePath ) && is_file( $originalFilePath ) ) {
					$originalImage = call_user_func( $createrMethod, $originalFilePath );
					$filenameBase  = $folder . '/' . pathinfo( $originalFile, PATHINFO_FILENAME );
					$newFilePath   = "$uploadsDir/$folder/$filenameBase.webp";
					imagewebp( $originalImage, $newFilePath, $quality );
					if ( $mimeType !== 'image/webp' ) {
						wp_delete_file( $originalFilePath );
					}

					$originalImagefilenameBase            = pathinfo( $attachmentMetadata['original_image'], PATHINFO_FILENAME );
					$attachmentMetadata['original_image'] = $originalImagefilenameBase . '.webp';
					$attachedFileMetaField                = get_post_meta( $attachment->ID, '_wp_attached_file', true );

					$attachmentMetaFieldFilenameBase = pathinfo( $attachedFileMetaField, PATHINFO_FILENAME );
					update_post_meta( $attachment->ID, '_wp_attached_file', $attachmentMetaFieldFilenameBase . '.webp' );
				}

				// Various sizes
				foreach ( $attachmentMetadata['sizes'] as $index => $fileInfo ) {

					$sizedFilePath = "$uploadsDir/$folder/{$fileInfo['file']}";
					if ( file_exists( $sizedFilePath ) && is_file( $sizedFilePath ) ) {
						$file          = $fileInfo['file'];
						$filenameBase  = pathinfo( $file, PATHINFO_FILENAME );
						$newFilePath   = "$uploadsDir/$folder/$filenameBase.webp";
						$originalImage = call_user_func( $createrMethod, $sizedFilePath );

						imagewebp( $originalImage, $newFilePath . '_', $quality );
						$newFilesize = filesize( $newFilePath . '_' );
						wp_delete_file( $sizedFilePath );
						WP_Filesystem::move( $newFilePath . '_', $newFilePath );

						$attachmentMetadata['sizes'][ $index ]['file']      = "$filenameBase.webp";
						$attachmentMetadata['sizes'][ $index ]['filesize']  = $newFilesize;
						$attachmentMetadata['sizes'][ $index ]['mime-type'] = 'image/webp';
					}
				}

				wp_update_attachment_metadata( $attachment->ID, $attachmentMetadata );
			}
		}
	}

	wp_die( 'Conversion process finished.' );
}
add_action( 'wp_ajax_evusel_convert_to_webp', 'evusel_convert_to_webp' );

/**
 * Get shortcode from content.
 *
 * @param string $content  Content to search.
 * @param string $shortcode Shortcode name.
 * @return string Shortcode string or empty.
 * @since 0.28.0b
 */
function evusel_GetShortcodeFromContent( $content, $shortcode ) {
	preg_match_all( '/' . get_shortcode_regex() . '/', $content, $matches, PREG_SET_ORDER );
	foreach ( $matches as $match ) {
		if (
			$shortcode === $match[2] ||
			! empty( $match[5] ) && has_shortcode( $match[5], $shortcode )
		) {
			return $match[0];
			break;
		}
	}

	return '';
}

/**
 * Get card labels HTML.
 *
 * @param WP_Post $event              Event post object.
 * @param string  $eventTitle         Event title.
 * @param string  $doortimeShowtimeType Door/show time type.
 * @param bool    $getOriginalLink    Whether to get original link.
 * @param DateTime|null $eventDateTime Event date/time object.
 * @return string Card labels HTML.
 * @since 0.28.0b
 */
function evusel_getCardLabelsHtml( $event, $eventTitle, $doortimeShowtimeType = 'list', $getOriginalLink = false, $eventDateTime = null ) {
	global $evuselConfig;

	$cardLabelsOrder = $evuselConfig->get( 'event_card_elements_order' );

	$doortimeShowtime = evusel_get_doortime_showtime( $doortimeShowtimeType, $event->ID );

	$eventTime = get_post_meta( $event->ID, 'event_time_r', true );

	$doorState = '<p class="fs-12 ' . esc_attr( $evuselConfig->get( 'event_data_classes.doortime_showtime' ) ) . '">' . wp_kses_post( $doortimeShowtime ) . ' </p>';

	$cardLabels = array(
		'header'            => '<p class="fs-12 ' . esc_attr( $evuselConfig->get( 'event_data_classes.header' ) ) . '">' . evusel_get_event_header_text( $event->ID ) . '</p>',
		'title'             => '<p class="fs-18 bold mb-12 ' . esc_attr( $evuselConfig->get( 'event_data_classes.title' ) ) . '">' . evusel_anchorize( $eventTitle, $event, '', $getOriginalLink ) . '</p>',
		'event_date'        => '<p class="fs-18 bold mt-1r ' . esc_attr( $evuselConfig->get( 'event_data_classes.date' ) ) . '">' . ( ! empty( $eventDateTime ) ? esc_html( $eventDateTime->format( 'D M j' ) ) : '' ) . '</p>',
		'headliners'        => '<p class="fs-12 ' . esc_attr( $evuselConfig->get( 'event_data_classes.headliners' ) ) . '">' . evusel_get_headliners( $event ) . '</p>',
		'supporting_talent' => '<p class="fs-12 ' . esc_attr( $evuselConfig->get( 'event_data_classes.supporting_talent' ) ) . '">' . evusel_get_supporting_talent_text( $event->ID ) . '</p>',
		'subtitle'          => '<p class="fs-12 ' . esc_attr( $evuselConfig->get( 'event_data_classes.subtitle' ) ) . '">' . evusel_subtitle_text( $event->ID ) . '</p>',
		'doortime_showtime' => $doorState,
		'venue'             => '<p class="fs-12 ' . esc_attr( $evuselConfig->get( 'event_data_classes.venue' ) ) . '">' . evusel_get_venue_text( $event->ID ) . '</p>',
		'ages_price'        => '<p class="fs-12">' . evusel_get_ages_price_text( $event->ID ) . '</p>',
		'genre'             => '<p class="fs-12 ' . esc_attr( $evuselConfig->get( 'event_data_classes.genre' ) ) . '">' . evusel_get_genre_text( $event->ID ) . '</p>',
	);

	// Adding Custom Fields
	for ( $i = 1; $i <= 3; $i++ ) {
		$showCustomField = ! empty( get_field( "show_custom_field_{$i}", $event->ID ) );
		if ( $showCustomField ) {

			$fieldLabel = get_field( "custom_field_{$i}_label", $event->ID );

			$cardLabels[ "custom_field_$i" ] = sprintf(
				'<p class="fs-12 %s">%s%s %s</p>',
				$evuselConfig->get( "event_data_classes.custom_field_{$i}" ),
				$fieldLabel,
				! empty( $fieldLabel ) ? ':' : '',
				get_field( "custom_field_{$i}", $event->ID )
			);
		}
	}

	$cardLabelsHtml = '';
	foreach ( $cardLabelsOrder as $cardLabel ) {
		if ( isset( $cardLabels[ $cardLabel ] ) ) {
			$cardLabelsHtml .= $cardLabels[ $cardLabel ];
		}
	}

	return $cardLabelsHtml;
}

/**
 * Generate event card HTML
 *
 * @param int    $eventId Event post ID
 * @param string $templateType 'list' or 'grid'
 * @param int    $columnAmount Number of columns for grid view
 * @param array  $dimensionsPerColumn Image dimensions per column
 * @param int    $nthItem Item index for alternating views
 * @return string Event card HTML
 */
function evusel_get_event_card( $eventId, $templateType = 'list', $columnAmount = 3, $dimensionsPerColumn = array(), $nthItem = 0 ) {
	global $evuselConfig;

	$event = get_post( $eventId );
	if ( ! $event ) {
		return '';
	}

	$listType      = $templateType === 'grid' ? 'g' : 'l';
	$eventDateTime = evusel_get_event_display_date_time( $eventId );
	$eventImage    = evusel_get_event_image( $eventId );

	$alternate = '';
	if ( $listType === 'l' && $evuselConfig->get( 'shortcodes.list_view.use_alternating_view' ) === 'on' ) {
		$alternate = ( $nthItem % 2 !== 0 ) ? '_alternate' : '';
	}

	$ticketLink       = evusel_get_ticket_link( $event );
	$ticketButtonLink = evusel_get_ticket_link( $event, true );
	$eventTitle       = get_post_meta( $eventId, 'event_title', true );
	$btnText          = evusel_get_ticket_status_button_text( $event );
	$ariaLabel        = evusel_get_ticket_button_aria_label( false, $eventTitle, $eventDateTime );
	$classes          = evusel_get_ticket_status_button_classes( $event );

	$materialStyle = $evuselConfig->get( 'shortcodes.list_view.material_list_style' );
	if ( $materialStyle !== 'outlined' && $materialStyle !== 'elevated' ) {
		$materialStyle = 'outlined';
	}

	$containerClasses  = 'seetickets-list-event-container mdc-card ';
	$containerClasses .= $listType === 'g' ? 'grid-item ' : '';
	$containerClasses .= $materialStyle === 'outlined' ? 'mdc-card--outlined ' : 'mdc-card--elevated ';

	$gridImageContainerStyle  = $listType === 'l' ?
		'width: 275px; height: 275px; ' :
		'width: ' . ( isset( $dimensionsPerColumn[ $columnAmount ]['imageWidth'] ) ? $dimensionsPerColumn[ $columnAmount ]['imageWidth'] : 290 ) . 'px; height: ' . ( isset( $dimensionsPerColumn[ $columnAmount ]['imageWidth'] ) ? $dimensionsPerColumn[ $columnAmount ]['imageWidth'] : 290 ) . 'px; ';
	$gridImageContainerStyle .= $evuselConfig->get( 'event_image_style' ) === 'square' ?
		'object-fit: cover; ' :
		'object-fit: contain; ';

	$socialClasses         = $evuselConfig->get( 'social_icon_circles' ) === 'on' ? 'social-circle ' : 'social-icon ';
	$openEventPageToNewTab = $evuselConfig->get( 'open_event_page_to_new_tab' );

	$seeticketEventButtonTarget = '';
	if ( $openEventPageToNewTab == 'on' ) {
		$seeticketEventButtonTarget = "target='_blank'";
	}

	$seetiketEventButton = "<a href='" . esc_url( $ticketButtonLink ) . "' " . esc_attr( $seeticketEventButtonTarget ) . " aria-label='" . esc_attr( $ariaLabel ) . "' class='seetickets-buy-btn fs-16 " . esc_attr( $classes ) . "'>" . esc_html( $btnText ) . '</a>';

	$templateFile = EVUSEL_PLUGIN_DIR . "includes/templates/event_{$templateType}_element{$alternate}.html";
	if ( ! file_exists( $templateFile ) ) {
		return '';
	}

	$listElementTemplate = str_replace(
		array(
			'{%image%}',
			'{%cardLabels%}',
			'{%buttonClasses%}',
			'{%eventButtonText%}',
			'{%eventContainerClass%}',
			'{%eventContainerStyle%}',
			'{%pluginDirUrl%}',
			'{%facebookShareText%}',
			'{%twitterSharetext%}',
			'{%ticketLink%}',
			'{%socialClasses%}',
			'{%ariaLabel%}',
			'{%facebookIcon%}',
			'{%twitterIcon%}',
			'{%openEventPageToNewTab%}',
		),
		array(
			evusel_anchorize( '<img src="' . esc_url( $eventImage ) . '" style="' . esc_attr( $gridImageContainerStyle ) . '" class="seetickets-list-view-event-image">', $event ),
			evusel_getCardLabelsHtml( $event, $eventTitle, $templateType, false, $eventDateTime ),
			esc_attr( $classes ),
			esc_html( $btnText ),
			esc_attr( $containerClasses ),
			$listType === 'l' ? '' : 'style="width: ' . ( isset( $dimensionsPerColumn[ $columnAmount ]['gridWidth'] ) ? esc_attr( $dimensionsPerColumn[ $columnAmount ]['gridWidth'] ) : 330 ) . 'px;"',
			esc_url( EVUSEL_PLUGIN_DIR_URL ),
			esc_url( urldecode( $ticketLink ) ),
			esc_url( urldecode( $ticketLink ) ),
			esc_url( $ticketButtonLink ),
			esc_attr( $socialClasses ),
			esc_attr( $ariaLabel ),
			'<span class="d-inline-block ' . esc_attr( $socialClasses ) . '">' . file_get_contents( EVUSEL_PLUGIN_DIR . '/images/facebook-icon.svg' ) . '</span>',
			'<span class="d-inline-block ' . esc_attr( $socialClasses ) . '">' . file_get_contents( EVUSEL_PLUGIN_DIR . '/images/x-twitter-brands-solid.svg' ) . '</span>',
			$seetiketEventButton,
		),
		file_get_contents( $templateFile )
	);

	return $listElementTemplate;
}

/**
 * Get all timezones from database.
 *
 * @return array Timezones array.
 * @since 0.23.0b
 */
function evusel_get_all_timezones() {
	global $wpdb;
	$cache_key = 'evusel_all_timezones';
	$results   = wp_cache_get( $cache_key, 'seetickets' );
	if ( false === $results ) {
        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery -- Necessary for timezone list, cached above
		$results = $wpdb->get_col(
			$wpdb->prepare(
				"SELECT DISTINCT meta_value FROM $wpdb->postmeta WHERE meta_key = %s",
				'ianaTimezone'
			)
		);
		wp_cache_set( $cache_key, $results, 'seetickets', DAY_IN_SECONDS );
	}
	$results = array_filter(
		$results,
		function ( $timeZoneString ) {
			return in_array( $timeZoneString, EVUSEL_TIMEZONE_IDENTIFIERS_LIST );
		}
	);
	return $results;
}

/**
 * Filter posts join for event date passed.
 *
 * @param string   $join  SQL join clause.
 * @param WP_Query $query WP_Query object.
 * @return string Modified join clause.
 */
function evusel_posts_join_for_list_eventdatepassed( $join, $query ) {
	global $wpdb;
	if ( isset( $query->query_vars['doing_list_view_eventdatepassed'] ) ) {
		$join .= " LEFT JOIN $wpdb->postmeta AS smtj1 ON ( $wpdb->posts.ID = smtj1.post_id AND smtj1.meta_key = 'ianaTimezone' )";
		$join .= " LEFT JOIN $wpdb->postmeta AS smtj2 ON ( $wpdb->posts.ID = smtj2.post_id AND smtj2.meta_key = 'sort_date' )";
	}

	return $join;
}
add_filter( 'posts_join', 'evusel_posts_join_for_list_eventdatepassed', 11, 2 );

/**
 * Filter posts where for event date passed.
 *
 * @param string   $where SQL where clause.
 * @param WP_Query $query WP_Query object.
 * @return string Modified where clause.
 * @since 0.33.0b
 */
function evusel_posts_where_for_list_eventdatepassed( $where, $query ) {
	if ( isset( $query->query['doing_list_view_eventdatepassed'] ) ) {

		$timezones = evusel_get_all_timezones();
		$today     = ( new DateTime() )->modify( '-6 hours' )->format( 'Ymd' );

		$conditions = array();

		if ( ! empty( $timezones ) ) {
			foreach ( $timezones as $eventTimeZone ) {
				$validTimezone       = evusel_get_valid_timezone( $eventTimeZone );
				$eventPassedDateTime = ( new DateTime() );
				$eventPassedDateTime
					->setTimezone( new DateTimeZone( $validTimezone ) )
					->modify( '-6 hours' );

				$conditions[] = sprintf(
					"( smtj1.meta_value = '%s' AND CAST(smtj2.meta_value AS DATE) >= '%s' )",
					esc_sql( $eventTimeZone ),
					esc_sql( $eventPassedDateTime->format( 'Ymd' ) )
				);
			}
		}

		$conditions[] = sprintf(
			"( smtj1.meta_value IS NULL AND ( smtj2.meta_value IS NULL OR CAST(smtj2.meta_value AS DATE) >= '%s' ) )",
			esc_sql( $today )
		);

		$conditions[] = sprintf(
			"( smtj2.meta_value IS NULL OR CAST(smtj2.meta_value AS DATE) >= '%s' )",
			esc_sql( $today )
		);

		if ( ! empty( $conditions ) ) {
			$where .= ' AND ( ' . implode( ' OR ', $conditions ) . ' )';
		} else {
			$where .= sprintf(
				" AND ( smtj2.meta_value IS NULL OR CAST(smtj2.meta_value AS DATE) >= '%s' )",
				esc_sql( $today )
			);
		}
	}

	return $where;
}
add_filter( 'posts_where', 'evusel_posts_where_for_list_eventdatepassed', 11, 2 );

/**
 * Add passed meta criteria to query args.
 *
 * @param array $args Query arguments array (passed by reference).
 * @return void
 * @since 0.30.0b
 */
function evusel_add_passed_meta_criteria( &$args ) {
	global $evuselConfig;
	$passedIf = $evuselConfig->get( 'shortcodes.past_events.past_event_if' );
	$utcNow   = new DateTime();

	switch ( $passedIf ) {

		case 'eventdatepassed':
			$args['doing_list_view_eventdatepassed'] = true;
			break;

		case 'eventtimepassed':
			$today                = $utcNow->format( 'Ymd' );
			$args['meta_query'][] = array(
				'relation' => 'OR',
				array(
					'key'     => 'event_time',
					'value'   => $utcNow->format( 'Y-m-d H:i:s' ),
					'compare' => '>=',
					'type'    => 'datetime',
				),
				array(
					'relation' => 'AND',
					array(
						'key'     => 'event_time',
						'compare' => 'NOT EXISTS',
					),
					array(
						'relation' => 'OR',
						array(
							'key'     => 'sort_date',
							'value'   => $today,
							'compare' => '>=',
							'type'    => 'DATE',
						),
						array(
							'key'     => 'sort_date',
							'compare' => 'NOT EXISTS',
						),
					),
				),
			);
			break;

		case 'offsaletimepassed':
			$args['meta_query'][] = array(
				'relation' => 'OR',
				array(
					'key'     => 'offsale',
					'compare' => 'NOT EXISTS',
				),
				array(
					'key'     => 'offsale',
					'value'   => $utcNow->format( 'Y-m-d H:i:s' ),
					'compare' => '>=',
					'type'    => 'datetime',
				),
			);
			break;
	}
}

/**
 * Check if event is in the past.
 *
 * @param WP_Post|int $event Event post object or ID.
 * @return bool True if event is in the past, false otherwise.
 */
function evuselIsInPast( $event ) {
	global $evuselConfig;

	$dispevliIsInPast = false;
	$now              = evusel_GetNowDateTimeForEvent( $event );

	$passedIf   = $evuselConfig->get( 'shortcodes.past_events.past_event_if' );
	$isMultiDay = (bool) get_post_meta( $event->ID, 'is_multiday', true );

	if ( $isMultiDay ) {

		/*
			We want multi day events to still be active during the last day,
			so we set the time to the last minute of the day.
		*/

		if ( gettype( get_post_meta( $event->ID, 'multiDayEventDates', true ) ) == 'array' ) {
			$lastEventDate = max( get_post_meta( $event->ID, 'multiDayEventDates', true ) );
		}

		if ( isset( $lastEventDate ) ) {
			$lastEventDateTime = new DateTime( $lastEventDate );
			$lastEventDateTime->setTimezone( $now->getTimezone() );
			$lastEventDateTime->setTime( 23, 59 );

			if ( $lastEventDateTime < $now ) {
				$dispevliIsInPast = true;
			}
		}

		return $dispevliIsInPast;
	}

	switch ( $passedIf ) {

		case 'eventdatepassed':
			$eventDateTime = evusel_get_event_display_date_time( $event->ID );
			return ! empty( $eventDateTime ) && strtotime( $now->modify( '-6 hours' )->format( 'Y-m-d H:i' ) ) > strtotime( $eventDateTime->format( 'Y-m-d H:i' ) );
			break;

		case 'eventtimepassed':
			$eventDateTime = evusel_get_event_display_date_time( $event->ID );
			return ! empty( $eventDateTime ) && strtotime( $now->format( 'Y-m-d H:i:s' ) ) > strtotime( $eventDateTime->format( 'Y-m-d H:i:s' ) );
			break;

		case 'offsaletimepassed':
			$offsaleDateTime = evusel_see_get_event_offsale_display_date_time( $event->ID );
			return ! empty( $offsaleDateTime ) && strtotime( $now->format( 'Y-m-d H:i:s' ) ) > strtotime( $offsaleDateTime->format( 'Y-m-d H:i:s' ) );
			break;
	}

	return $dispevliIsInPast;
}

/**
 * Safely log debug information
 *
 * @param string $message Debug message
 * @param mixed  $data    Data to debug
 * @return void
 */
/**
 * Debug logging function - disabled in production
 * To enable, add define('EVUSEL_DEBUG', true) to wp-config.php
 *
 * @param string $message The message to log
 * @param mixed  $data Optional data to log
 * @return void
 */
function evusel_debug_log( $message, $data = null ) {
	if ( ! defined( 'EVUSEL_DEBUG' ) || ! EVUSEL_DEBUG ) {
		return;
	}

	if ( ! defined( 'WP_DEBUG' ) || ! WP_DEBUG || ! defined( 'WP_DEBUG_LOG' ) || ! WP_DEBUG_LOG ) {
		return;
	}

	$evusel_debugData = $data !== null ? wp_json_encode( $data, JSON_PRETTY_PRINT ) : '';
	$log_message      = sprintf( '[See Tickets Debug] %s %s', $message, $evusel_debugData );

    // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log -- Debug function only runs when EVUSEL_DEBUG is true
	error_log( $log_message );
}
/**
 * Get valid timezone identifier.
 *
 * @param string $timezone Timezone string.
 * @return string Valid timezone identifier.
 */
function evusel_get_valid_timezone( $timezone ) {
	$aliases = array(
		'US/Arizona' => 'America/Phoenix',
		'US/Hawaii'  => 'Pacific/Honolulu',
	);
	if ( in_array( $timezone, timezone_identifiers_list() ) ) {
		return $timezone;
	}
	if ( isset( $aliases[ $timezone ] ) ) {
		return $aliases[ $timezone ];
	}
	return 'UTC';
}

/**
 * Filter multi-day events to show only parent events.
 * This function filters the results after query execution to avoid SQL conflicts.
 *
 * @param array $events Array of WP_Post objects.
 * @return array Filtered array of events.
 */
function evusel_filter_multi_day_events_results( $events ) {
	if ( empty( $events ) ) {
		return $events;
	}

	$filtered_events   = array();
	$multi_day_parents = array();

	// First pass: identify multi-day parent events
	foreach ( $events as $event ) {
		$is_multi_day = get_post_meta( $event->ID, 'is_multiday', true );
		if ( $is_multi_day && $event->post_parent == 0 ) {
			$multi_day_parents[] = $event->ID;
		}
	}

	// Second pass: filter events
	foreach ( $events as $event ) {
		$is_multi_day = get_post_meta( $event->ID, 'is_multiday', true );

		// Include non-multi-day events
		if ( ! $is_multi_day ) {
			$filtered_events[] = $event;
			continue;
		}

		// For multi-day events, only include parent events
		if ( $event->post_parent == 0 ) {
			$filtered_events[] = $event;
		}
	}

	return $filtered_events;
}

/**
 * Filter to ensure multi-day events only show parent events in list view.
 * This is a simplified version that only applies to very basic queries.
 *
 * @param string $where SQL where clause.
 * @return string
 */
function evusel_filter_multi_day_events_list_view( $where ) {
	global $wpdb;

	// Only apply to very simple queries to avoid conflicts
	if ( strpos( $where, "wp_posts.post_type = 'seetickets-event'" ) === false ) {
		return $where;
	}

	// Skip if already applied
	if ( strpos( $where, 'is_multiday' ) !== false ) {
		return $where;
	}

	// Skip any complex queries
	if ( strpos( $where, 'smtj' ) !== false ||
		strpos( $where, 'meta_query' ) !== false ||
		strpos( $where, 'doing_list_view_eventdatepassed' ) !== false ||
		strpos( $where, 'ianaTimezone' ) !== false ||
		strpos( $where, 'INNER JOIN' ) !== false ) {
		return $where;
	}

	// Only apply to the simplest queries
	$where .= " AND {$wpdb->posts}.post_parent = 0";

	return $where;
}
