<?php
/**
 * Add to Calendar element.
 *
 * @package BricksExtras
 */

if ( ! defined( 'ABSPATH' ) ) {
	exit; // Exit if accessed directly.
}

/**
 * Add to Calendar element class.
 */
class X_Add_To_Calendar extends \Bricks\Element {

	/**
	 * Element category.
	 *
	 * @var string
	 */
	public $category = 'extras';

	/**
	 * Element name.
	 *
	 * @var string
	 */
	public $name = 'xaddtocalendar';

	/**
	 * Element icon.
	 *
	 * @var string
	 */
	public $icon = 'ti-calendar';

	/**
	 * CSS selector.
	 *
	 * @var string
	 */
	public $css_selector = '';

	/**
	 * Required scripts.
	 *
	 * @var array
	 */
	public $scripts = array( 'xAddToCalendar' );

	/**
	 * Enqueue element scripts.
	 */
	public function enqueue_scripts() {
		wp_enqueue_script( 'x-popper', BRICKSEXTRAS_URL . 'components/assets/js/' . \BricksExtras\Helpers::maybe_minify_scripts( 'popper' ) . '.js', array( 'bricks-scripts' ), \BricksExtras\Plugin::VERSION, true );
		wp_enqueue_script(
			'x-add-to-calendar',
			BRICKSEXTRAS_URL . 'components/assets/js/' . \BricksExtras\Helpers::maybe_minify_scripts( 'addtocalendar' ) . '.js',
			array( 'x-popper' ),
			\BricksExtras\Plugin::VERSION,
			true
		);

		if ( ! \BricksExtras\Helpers::element_css_added( $this->name ) ) {
			wp_enqueue_style( 'x-add-to-calendar', BRICKSEXTRAS_URL . 'components/assets/css/addtocalendar.css', array(), \BricksExtras\Plugin::VERSION );
		}
	}

	/**
	 * Get element label.
	 */
	public function get_label() {
		return esc_html__( 'Add to Calendar', 'bricksextras' );
	}

	/**
	 * Get element keywords.
	 */
	public function get_keywords() {
		return array( 'add to calendar', 'calendar', 'event', 'ics', 'google calendar' );
	}

	/**
	 * Set control groups.
	 */
	public function set_control_groups() {

		$this->control_groups['eventData'] = array(
			'title'    => esc_html__( 'Data', 'bricksextras' ),
			'tab'      => 'content',
			'required' => array( 'dataMode', '=', 'dynamicdata' ),
		);

		$this->control_groups['tooltip'] = array(
			'title' => esc_html__( 'Tooltip', 'bricks' ),
			'tab'   => 'content',
		);

		$this->control_groups['animation'] = array(
			'title' => esc_html__( 'Animation', 'bricks' ),
			'tab'   => 'content',
		);

		$this->control_groups['button'] = array(
			'title' => esc_html__( 'Button', 'bricks' ),
			'tab'   => 'content',
		);

		$this->control_groups['links'] = array(
			'title' => esc_html__( 'Links', 'bricks' ),
			'tab'   => 'content',
		);
	}

	/**
	 * Set element controls.
	 */
	public function set_controls() {

		$this->controls['dataMode'] = array(
			'tab'         => 'content',
			'label'       => esc_html__( 'Data Mode', 'bricksextras' ),
			'type'        => 'select',
			'options'     => array(
				'auto'        => esc_html__( 'Auto', 'bricks' ) . ' (' . esc_html__( 'Popup', 'bricks' ) . ')',
				'dynamicdata' => esc_html__( 'Dynamic data', 'bricksextras' ),
			),
			'placeholder' => esc_html__( 'Auto', 'bricksextras' ),
			'inline'      => true,
		);

		$this->controls['eventTitle'] = array(
			'tab'      => 'content',
			'group'    => 'eventData',
			'label'    => esc_html__( 'Title', 'bricks' ),
			'type'     => 'text',
			'required' => array( 'dataMode', '=', 'dynamicdata' ),
		);

		$this->controls['eventStart'] = array(
			'tab'      => 'content',
			'group'    => 'eventData',
			'label'    => esc_html__( 'Start Date/Time', 'bricksextras' ),
			'type'     => 'text',
			'required' => array( 'dataMode', '=', 'dynamicdata' ),
		);

		$this->controls['eventEnd'] = array(
			'tab'      => 'content',
			'group'    => 'eventData',
			'label'    => esc_html__( 'End Date/Time', 'bricksextras' ),
			'type'     => 'text',
			'required' => array( 'dataMode', '=', 'dynamicdata' ),
		);

		$this->controls['eventDescription'] = array(
			'tab'      => 'content',
			'group'    => 'eventData',
			'label'    => esc_html__( 'Description', 'bricks' ),
			'type'     => 'textarea',
			'required' => array( 'dataMode', '=', 'dynamicdata' ),
		);

		$this->controls['eventLocation'] = array(
			'tab'      => 'content',
			'group'    => 'eventData',
			'label'    => esc_html__( 'Location', 'bricks' ),
			'type'     => 'text',
			'required' => array( 'dataMode', '=', 'dynamicdata' ),
		);

		$this->controls['eventAllDay'] = array(
			'tab'      => 'content',
			'group'    => 'eventData',
			'label'    => esc_html__( 'All Day Event', 'bricksextras' ),
			'type'     => 'text',
			'info'     => esc_html__( 'True', 'bricks' ) . '/' . esc_html__( 'False', 'bricks' ),
			'required' => array( 'dataMode', '=', 'dynamicdata' ),
		);

		$this->controls['calendarServices'] = array(
			'tab'           => 'content',
			'label'         => esc_html__( 'Service', 'bricks' ),
			'type'          => 'repeater',
			'selector'      => 'a.x-add-to-calendar_link',
			'titleProperty' => 'label',
			'default'       => array(
				array(
					'service' => 'google',
					'label'   => 'Google Calendar',
				),
				array(
					'service' => 'outlook',
					'label'   => 'Outlook Calendar',
				),
				array(
					'service' => 'yahoo',
					'label'   => 'Yahoo Calendar',
				),
				array(
					'service' => 'ics',
					'label'   => 'Download ICS',
				),
			),
			'fields'        => array(
				'service'    => array(
					'label'   => esc_html__( 'Service', 'bricks' ),
					'type'    => 'select',
					'options' => array(
						'google'  => 'Google',
						'outlook' => 'Outlook',
						'yahoo'   => 'Yahoo',
						'ics'     => 'ICS',
					),
					'default' => 'google',
				),
				'label'      => array(
					'label'   => esc_html__( 'Label', 'bricks' ),
					'type'    => 'text',
					'default' => 'Google',
				),
				'icon'       => array(
					'label' => esc_html__( 'Icon', 'bricks' ),
					'type'  => 'icon',
					'css'   => array(
						array(
							'selector' => '.x-add-to-calendar_service-icon > *',
						),
					),
				),
				'iconColor'  => array(
					'label' => esc_html__( 'Icon color', 'bricks' ),
					'type'  => 'color',
					'css'   => array(
						array(
							'property' => 'color',
							'selector' => '.x-add-to-calendar_service-icon',
						),
					),
				),
				'background' => array(
					'label' => esc_html__( 'Background', 'bricks' ),
					'type'  => 'color',
					'css'   => array(
						array(
							'property' => 'background-color',
							'selector' => '',
						),
					),
				),
				'textColor'  => array(
					'label' => esc_html__( 'Text color', 'bricks' ),
					'type'  => 'color',
					'css'   => array(
						array(
							'property' => 'color',
							'selector' => '',
						),
					),
				),
			),
		);

		$this->controls['serviceDisplay'] = array(
			'tab'         => 'content',
			'label'       => esc_html__( 'Display', 'bricks' ),
			'type'        => 'select',
			'inline'      => true,
			'options'     => array(
				'both'  => esc_html__( 'Both', 'bricks' ),
				'icon'  => esc_html__( 'Icon', 'bricks' ),
				'label' => esc_html__( 'Label', 'bricks' ),
			),
			'placeholder' => esc_html__( 'Both', 'bricks' ),
		);

		$this->controls['linkTarget'] = array(
			'tab'     => 'content',
			'label'   => esc_html__( 'Open in new tab', 'bricks' ),
			'type'    => 'checkbox',
			'default' => true,
		);

		$this->controls['tooltipTrigger'] = array(
			'tab'     => 'content',
			'group'   => 'tooltip',
			'label'   => esc_html__( 'Trigger', 'bricks' ),
			'type'    => 'select',
			'options' => array(
				'click'      => esc_html__( 'Click', 'bricks' ),
				'mouseenter' => esc_html__( 'Hover', 'bricks' ),
			),
			'default' => 'click',
			'inline'  => true,
		);

		$this->controls['tooltipPlacement'] = array(
			'tab'     => 'content',
			'group'   => 'tooltip',
			'label'   => esc_html__( 'Placement', 'bricksextras' ),
			'type'    => 'select',
			'options' => array(
				'top'    => esc_html__( 'Top', 'bricks' ),
				'bottom' => esc_html__( 'Bottom', 'bricks' ),
				'left'   => esc_html__( 'Left', 'bricks' ),
				'right'  => esc_html__( 'Right', 'bricks' ),
			),
			'default' => 'bottom',
			'inline'  => true,
		);

		$this->controls['delay'] = array(
			'tab'         => 'content',
			'group'       => 'tooltip',
			'label'       => esc_html__( 'Delay', 'bricks' ) . ' (ms)',
			'type'        => 'number',
			'placeholder' => '0',
			'units'       => false,
			'small'       => true,
		);

		$this->controls['offsetSkidding'] = array(
			'tab'         => 'content',
			'group'       => 'tooltip',
			'label'       => esc_html__( 'Offset skidding', 'bricksextras' ) . '  (px)',
			'info'        => esc_html__( 'Distance along the side of the button', 'bricksextras' ),
			'type'        => 'number',
			'placeholder' => '0',
			'units'       => false,
			'small'       => true,
		);

		$this->controls['offsetDistance'] = array(
			'tab'         => 'content',
			'group'       => 'tooltip',
			'label'       => esc_html__( 'Offset distance', 'bricksextras' ) . '  (px)',
			'info'        => esc_html__( 'Distance away from the button', 'bricksextras' ),
			'type'        => 'number',
			'placeholder' => '10',
			'units'       => false,
			'small'       => true,
		);

		$this->controls['moveTransition'] = array(
			'tab'         => 'content',
			'group'       => 'tooltip',
			'label'       => esc_html__( 'Transition', 'bricks' ) . ' (ms)',
			'type'        => 'number',
			'placeholder' => '50',
			'units'       => false,
			'small'       => true,
		);

		$this->controls['appendBody'] = array(
			'tab'   => 'content',
			'group' => 'tooltip',
			'label' => esc_html__( 'Append to body', 'bricksextras' ),
			'type'  => 'checkbox',
		);

		$this->controls['tooltipAnimationSep'] = array(
			'tab'   => 'content',
			'group' => 'animation',
			'type'  => 'separator',
			'label' => esc_html__( 'Tooltip', 'bricks' ),
		);

		$this->controls['tooltipTransitionIn'] = array(
			'tab'         => 'content',
			'group'       => 'animation',
			'label'       => esc_html__( 'Transition', 'bricks' ) . ' (' . esc_html__( 'Show', 'bricks' ) . ')',
			'css'         => array(
				array(
					'property' => '--x-popover-transitionin',
					'selector' => '',
				),
			),
			'type'        => 'number',
			'placeholder' => '300',
			'inline'      => true,
			'unit'        => 'ms',
		);

		$this->controls['tooltipTransitionOut'] = array(
			'tab'         => 'content',
			'group'       => 'animation',
			'label'       => esc_html__( 'Transition', 'bricks' ) . ' (' . esc_html__( 'Hide', 'bricks' ) . ')',
			'css'         => array(
				array(
					'property' => '--x-popover-transitionout',
					'selector' => '',
				),
			),
			'type'        => 'number',
			'placeholder' => '300',
			'unit'        => 'ms',
			'inline'      => true,
		);

		$this->controls['tooltipTranslateX'] = array(
			'tab'         => 'content',
			'group'       => 'animation',
			'label'       => esc_html__( 'Translate X', 'bricks' ),
			'css'         => array(
				array(
					'property' => '--x-popover-translatex',
					'selector' => '',
				),
			),
			'type'        => 'number',
			'placeholder' => '0',
			'inline'      => true,
			'units'       => array(
				'px' => array(
					'min'  => 1,
					'max'  => 1000,
					'step' => 1,
				),
			),
		);

		$this->controls['tooltipTranslateY'] = array(
			'tab'         => 'content',
			'group'       => 'animation',
			'label'       => esc_html__( 'Translate Y', 'bricks' ),
			'css'         => array(
				array(
					'property' => '--x-popover-translatey',
					'selector' => '',
				),
			),
			'type'        => 'number',
			'placeholder' => '10',
			'inline'      => true,
			'units'       => array(
				'px' => array(
					'min'  => 1,
					'max'  => 1000,
					'step' => 1,
				),
			),
		);

		$this->controls['tooltipScale'] = array(
			'tab'         => 'content',
			'group'       => 'animation',
			'label'       => esc_html__( 'Scale', 'bricks' ),
			'css'         => array(
				array(
					'property' => '--x-popover-scale',
					'selector' => '',
				),
			),
			'type'        => 'number',
			'placeholder' => '0.95',
			'inline'      => true,
		);

		$this->controls['buttonText'] = array(
			'tab'     => 'content',
			'label'   => esc_html__( 'Text', 'bricks' ),
			'type'    => 'text',
			'group'   => 'button',
			'default' => esc_html__( 'Add to my calendar', 'bricksextras' ),
			'inline'  => true,
		);

		$this->controls['buttonIcon'] = array(
			'tab'   => 'content',
			'label' => esc_html__( 'Icon', 'bricks' ),
			'type'  => 'icon',
			'group' => 'button',
			'css'   => array(
				array(
					'selector' => '.x-add-to-calendar_icon > *',
				),
			),
		);

		$this->controls['buttonDisplay'] = array(
			'tab'         => 'content',
			'label'       => esc_html__( 'Display', 'bricks' ),
			'type'        => 'select',
			'group'       => 'button',
			'inline'      => true,
			'options'     => array(
				'both'  => esc_html__( 'Both', 'bricks' ),
				'icon'  => esc_html__( 'Icon', 'bricks' ),
				'label' => esc_html__( 'Label', 'bricks' ),
			),
			'placeholder' => esc_html__( 'Both', 'bricks' ),
		);

		// Button styles.
		$this->controls['buttonTypography'] = array(
			'tab'   => 'content',
			'group' => 'button',
			'label' => esc_html__( 'Typography', 'bricks' ),
			'type'  => 'typography',
			'css'   => array(
				array(
					'property' => 'font',
					'selector' => '.x-add-to-calendar_button',
				),
			),
		);

		$this->controls['buttonBackground'] = array(
			'tab'   => 'content',
			'group' => 'button',
			'label' => esc_html__( 'Background', 'bricks' ),
			'type'  => 'color',
			'css'   => array(
				array(
					'property' => 'background-color',
					'selector' => '.x-add-to-calendar_button',
				),
			),
		);

		$this->controls['buttonBorder'] = array(
			'tab'   => 'content',
			'group' => 'button',
			'label' => esc_html__( 'Border', 'bricks' ),
			'type'  => 'border',
			'css'   => array(
				array(
					'property' => 'border',
					'selector' => '.x-add-to-calendar_button',
				),
			),
		);

		$this->controls['buttonBoxShadow'] = array(
			'tab'   => 'content',
			'group' => 'button',
			'label' => esc_html__( 'Box shadow', 'bricks' ),
			'type'  => 'box-shadow',
			'css'   => array(
				array(
					'property' => 'box-shadow',
					'selector' => '.x-add-to-calendar_button',
				),
			),
		);

		$this->controls['buttonPadding'] = array(
			'tab'   => 'content',
			'group' => 'button',
			'label' => esc_html__( 'Padding', 'bricks' ),
			'type'  => 'spacing',
			'css'   => array(
				array(
					'property' => 'padding',
					'selector' => '.x-add-to-calendar_button',
				),
			),
		);

		// Tooltip styles.
		$this->controls['tooltipStylesSep'] = array(
			'tab'   => 'content',
			'group' => 'tooltip',
			'label' => esc_html__( 'Tooltip Styles', 'bricks' ),
			'type'  => 'separator',
		);

		// Tooltip styles.
		$this->controls['tooltipTypography'] = array(
			'tab'   => 'content',
			'group' => 'tooltip',
			'label' => esc_html__( 'Typography', 'bricks' ),
			'type'  => 'typography',
			'css'   => array(
				array(
					'property' => 'font',
					'selector' => '.tippy-content',
				),
			),
		);

		$this->controls['tooltipBackground'] = array(
			'tab'   => 'content',
			'group' => 'tooltip',
			'label' => esc_html__( 'Background', 'bricks' ),
			'type'  => 'color',
			'css'   => array(
				array(
					'property' => 'background-color',
					'selector' => '.tippy-content',
				),
				array(
					'property' => '--x-popover-background',
					'selector' => '.tippy-box',
				),
			),
		);

		$this->controls['tooltipBorder'] = array(
			'tab'   => 'content',
			'group' => 'tooltip',
			'label' => esc_html__( 'Border', 'bricks' ),
			'type'  => 'border',
			'css'   => array(
				array(
					'property' => 'border',
					'selector' => '.tippy-content',
				),
			),
		);

		$this->controls['tooltipBoxShadow'] = array(
			'tab'   => 'content',
			'group' => 'tooltip',
			'label' => esc_html__( 'Box shadow', 'bricks' ),
			'type'  => 'box-shadow',
			'css'   => array(
				array(
					'property' => 'box-shadow',
					'selector' => '.tippy-content',
				),
			),
		);

		$this->controls['tooltipPadding'] = array(
			'tab'   => 'content',
			'group' => 'tooltip',
			'label' => esc_html__( 'Padding', 'bricks' ),
			'type'  => 'spacing',
			'css'   => array(
				array(
					'property' => 'padding',
					'selector' => '.tippy-content',
				),
			),
		);

		// Link styles.

		$this->controls['linkGap'] = array(
			'tab'   => 'content',
			'group' => 'links',
			'label' => esc_html__( 'Gap', 'bricks' ) . ' (' . esc_html__( 'Icon', 'bricks' ) . '/' . esc_html__( 'Text', 'bricks' ) . ')',
			'type'  => 'number',
			'css'   => array(
				array(
					'property' => 'gap',
					'selector' => '.x-add-to-calendar_link',
				),
			),
		);

		$this->controls['linkTypography'] = array(
			'tab'   => 'content',
			'group' => 'links',
			'label' => esc_html__( 'Typography', 'bricks' ),
			'type'  => 'typography',
			'css'   => array(
				array(
					'property' => 'font',
					'selector' => '.x-add-to-calendar_link',
				),
			),
		);

		$this->controls['linkBackground'] = array(
			'tab'   => 'content',
			'group' => 'links',
			'label' => esc_html__( 'Background', 'bricks' ),
			'type'  => 'color',
			'css'   => array(
				array(
					'property' => 'background-color',
					'selector' => '.x-add-to-calendar_link',
				),
			),
		);

		$this->controls['linkBorder'] = array(
			'tab'   => 'content',
			'group' => 'links',
			'label' => esc_html__( 'Border', 'bricks' ),
			'type'  => 'border',
			'css'   => array(
				array(
					'property' => 'border',
					'selector' => '.x-add-to-calendar_link',
				),
			),
		);

		$this->controls['linkPadding'] = array(
			'tab'   => 'content',
			'group' => 'links',
			'label' => esc_html__( 'Padding', 'bricks' ),
			'type'  => 'spacing',
			'css'   => array(
				array(
					'property' => 'padding',
					'selector' => '.x-add-to-calendar_link',
				),
			),
		);
	}

	/**
	 * Render element HTML.
	 */
	public function render() {
		$settings = $this->settings;

		$data_mode         = isset( $settings['dataMode'] ) ? $settings['dataMode'] : 'auto';
		$button_icon       = isset( $settings['buttonIcon'] ) ? $settings['buttonIcon'] : array();
		$button_display    = isset( $settings['buttonDisplay'] ) && ! empty( $settings['buttonDisplay'] ) ? $settings['buttonDisplay'] : 'both';
		$service_display   = isset( $settings['serviceDisplay'] ) && ! empty( $settings['serviceDisplay'] ) ? $settings['serviceDisplay'] : 'both';
		$services          = isset( $settings['calendarServices'] ) ? $settings['calendarServices'] : array();
		$link_target       = isset( $settings['linkTarget'] ) && $settings['linkTarget'] ? '_blank' : '_self';
		$tooltip_trigger   = isset( $settings['tooltipTrigger'] ) ? $settings['tooltipTrigger'] : 'click';
		$tooltip_placement = isset( $settings['tooltipPlacement'] ) ? $settings['tooltipPlacement'] : 'bottom';
		$delay             = isset( $settings['delay'] ) ? intval( $settings['delay'] ) : 0;
		$offset_skidding   = isset( $settings['offsetSkidding'] ) ? intval( $settings['offsetSkidding'] ) : 0;
		$offset_distance   = isset( $settings['offsetDistance'] ) ? intval( $settings['offsetDistance'] ) : 10;
		$move_transition   = isset( $settings['moveTransition'] ) ? intval( $settings['moveTransition'] ) : 50;
		$append_body       = isset( $settings['appendBody'] ) && $settings['appendBody'];

		if ( empty( $services ) ) {
			return;
		}

		// Prepare event data for manual mode.
		$event_data = null;
		if ( 'dynamicdata' === $data_mode ) {
			// Render dynamic data tags first.
			$title       = isset( $settings['eventTitle'] ) ? bricks_render_dynamic_data( $settings['eventTitle'] ) : '';
			$start       = isset( $settings['eventStart'] ) ? sanitize_text_field( bricks_render_dynamic_data( $settings['eventStart'] ) ) : '';
			$end         = isset( $settings['eventEnd'] ) ? sanitize_text_field( bricks_render_dynamic_data( $settings['eventEnd'] ) ) : '';
			$description = isset( $settings['eventDescription'] ) ? bricks_render_dynamic_data( $settings['eventDescription'] ) : '';
			$location    = isset( $settings['eventLocation'] ) ? bricks_render_dynamic_data( $settings['eventLocation'] ) : '';
			$all_day_raw = isset( $settings['eventAllDay'] ) ? bricks_render_dynamic_data( $settings['eventAllDay'] ) : '';

			// Convert dates to ISO 8601 format.
			$start = $this->convert_to_iso_date( $start );
			$end   = $this->convert_to_iso_date( $end );

			// Convert allDay to boolean.
			$all_day = filter_var( $all_day_raw, FILTER_VALIDATE_BOOLEAN );

			// If no end date provided and we have a start date, default to a one-hour
			// (or one-day, for all-day events) duration, matching FullCalendar's own
			// default behaviour when an event has no explicit end.
			if ( empty( $end ) && ! empty( $start ) ) {
				try {
					$start_date = new \DateTime( $start );
					if ( $all_day ) {
						$start_date->add( new \DateInterval( 'P1D' ) );
						$end = $start_date->format( 'Y-m-d' );
					} else {
						$start_date->add( new \DateInterval( 'PT1H' ) );
						$end = $start_date->format( 'Y-m-d\TH:i:s' );
					}
				} catch ( \Exception $e ) {
					// Silently fail - event will use start date only.
				}
			}

			$event_data = array(
				'title'         => sanitize_text_field( $title ),
				'start'         => $start,
				'end'           => $end,
				'allDay'        => $all_day,
				'extendedProps' => array(
					'description' => wp_kses_post( $description ),
					'location'    => sanitize_text_field( $location ),
				),
			);
		}

		// Generate and set a unique identifier for this instance.
		$identifier = \BricksExtras\Helpers::set_identifier_attribute( $this );

		// Set root attributes.
		$this->set_attribute( '_root', 'class', 'x-add-to-calendar' );
		$this->set_attribute( '_root', 'data-mode', esc_attr( $data_mode ) );
		$this->set_attribute( '_root', 'data-tooltip-trigger', esc_attr( $tooltip_trigger ) );
		$this->set_attribute( '_root', 'data-tooltip-placement', esc_attr( $tooltip_placement ) );
		$this->set_attribute( '_root', 'data-delay', esc_attr( $delay ) );
		$this->set_attribute( '_root', 'data-offset-skidding', esc_attr( $offset_skidding ) );
		$this->set_attribute( '_root', 'data-offset-distance', esc_attr( $offset_distance ) );
		$this->set_attribute( '_root', 'data-move-transition', esc_attr( $move_transition ) );

		if ( $event_data ) {
			$this->set_attribute( '_root', 'data-event', wp_json_encode( $event_data ) );
		}

		if ( $append_body ) {
			$this->set_attribute( '_root', 'data-append-body', 'true' );
		}

		// Generate unique IDs for ARIA relationships.
		$button_id = 'x-add-to-calendar_button-' . $identifier;
		$menu_id   = 'x-add-to-calendar_menu-' . $identifier;

		// Render the button.
		$show_button_icon  = in_array( $button_display, array( 'icon', 'both' ), true );
		$show_button_label = in_array( $button_display, array( 'label', 'both' ), true );

		$button_icon_markup = ! empty( $button_icon ) ? self::render_icon( $button_icon ) : $this->get_default_button_icon();

		$button_text = isset( $settings['buttonText'] ) ? esc_html( bricks_render_dynamic_data( $settings['buttonText'] ) ) : esc_html__( 'Add to Calendar', 'bricksextras' );

		$button_icon_html = ( $show_button_icon && $button_icon_markup ) ? '<span class="x-add-to-calendar_icon" aria-hidden="true">' . $button_icon_markup . '</span>' : '';
		$button_text_html = $show_button_label ? '<span class="x-add-to-calendar_text">' . $button_text . '</span>' : '';

		$this->set_attribute( 'x-add-to-calendar_button', 'type', 'button' );
		$this->set_attribute( 'x-add-to-calendar_button', 'id', $button_id );
		$this->set_attribute( 'x-add-to-calendar_button', 'class', 'x-add-to-calendar_button' );
		$this->set_attribute( 'x-add-to-calendar_button', 'aria-expanded', 'false' );
		$this->set_attribute( 'x-add-to-calendar_button', 'aria-controls', $menu_id );

		$this->set_attribute( 'x-add-to-calendar_menu', 'id', $menu_id );
		$this->set_attribute( 'x-add-to-calendar_menu', 'class', 'x-add-to-calendar_menu' );
		$this->set_attribute( 'x-add-to-calendar_menu', 'style', 'display: none;' );

		$output  = "<div {$this->render_attributes( '_root' )}>";
		$output .= "<button {$this->render_attributes( 'x-add-to-calendar_button' )}>";

		$output .= $button_icon_html . $button_text_html;

		$output .= '</button>';

		// Render tooltip content (hidden).
		$output .= "<div {$this->render_attributes( 'x-add-to-calendar_menu' )}>";
		foreach ( $services as $index => $service_item ) {
			$service = isset( $service_item['service'] ) ? $service_item['service'] : 'google';
			$label   = isset( $service_item['label'] ) ? esc_html( $service_item['label'] ) : 'Calendar';
			$icon    = isset( $service_item['icon'] ) ? $service_item['icon'] : array();

			$show_icon  = in_array( $service_display, array( 'icon', 'both' ), true );
			$show_label = in_array( $service_display, array( 'label', 'both' ), true );

			$icon_markup = ! empty( $icon ) ? self::render_icon( $icon ) : $this->get_default_service_icon( $service );

			$link_key = "x-add-to-calendar_link-{$index}";

			$this->set_attribute( $link_key, 'href', '#' );
			$this->set_attribute( $link_key, 'class', 'x-add-to-calendar_link' );
			$this->set_attribute( $link_key, 'data-service', $service );
			$this->set_attribute( $link_key, 'target', $link_target );

			if ( '_blank' === $link_target ) {
				$this->set_attribute( $link_key, 'rel', 'noopener noreferrer' );
			}

			$output .= "<a {$this->render_attributes( $link_key )}>";

			$icon_html  = ( $show_icon && $icon_markup ) ? '<span class="x-add-to-calendar_service-icon" aria-hidden="true">' . $icon_markup . '</span>' : '';
			$label_html = $show_label ? '<span class="x-add-to-calendar_service-label">' . $label . '</span>' : '';

			if ( 'label-icon' === $service_display ) {
				$output .= $label_html . $icon_html;
			} else {
				$output .= $icon_html . $label_html;
			}

			$output .= '</a>';
		}
		$output .= '</div>';
		$output .= '</div>';

		echo $output;
	}

	/**
	 * Get default button icon SVG.
	 *
	 * @return string SVG markup.
	 */
	private function get_default_button_icon() {
		return '<svg xmlns="http://www.w3.org/2000/svg" width="17" height="17" viewBox="0 0 17 17"><path d="M14 2v-1h-3v1h-5v-1h-3v1h-3v15h17v-15h-3zM12 2h1v2h-1v-2zM4 2h1v2h-1v-2zM16 16h-15v-8.921h15v8.921zM1 6.079v-3.079h2v2h3v-2h5v2h3v-2h2v3.079h-15z" fill="currentColor"/></svg>';
	}

	/**
	 * Get default service icon SVG.
	 *
	 * @param string $service Service key: 'google', 'ics', 'outlook', or 'yahoo'.
	 * @return string SVG markup.
	 */
	private function get_default_service_icon( $service ) {
		$icons = array(
			'google'  => '<svg xmlns="http://www.w3.org/2000/svg" width="17" height="17" viewBox="0 0 17 17"><path d="M17 2.615v1.308h-2.615v2.616h-1.308v-2.616h-2.615v-1.308h2.615v-2.615h1.308v2.615h2.615zM10.39 12.78c0 1.175-0.561 2.125-1.43 2.881-1.175 1.012-2.779 1.339-4.291 1.339-1.91 0-4.669-0.817-4.669-3.187 0-0.48 0.164-0.951 0.379-1.38 0.908-1.838 3.748-2.309 5.577-2.36-0.336-0.439-0.653-0.949-0.653-1.522 0-0.337 0.121-0.562 0.225-0.878-0.246 0.030-0.48 0.051-0.715 0.051-1.982 0-3.698-1.461-3.698-3.515 0-1.941 1.501-3.596 3.36-4.004 0.624-0.134 1.266-0.205 1.91-0.205h4.466l-1.379 0.807h-1.381c1.013 0.633 1.523 1.87 1.523 3.014 0 2.707-2.289 2.911-2.289 4.26 0 1.318 3.065 1.839 3.065 4.699zM7.725 4.853c0-1.553-0.839-4.117-2.729-4.117-1.337 0-1.991 1.185-1.991 2.391 0 1.552 1.021 3.984 2.84 3.984 1.398-0.001 1.88-1.012 1.88-2.258zM8.95 13.659c0-1.43-1.34-2.227-2.381-2.953-0.174-0.020-0.338-0.020-0.512-0.020-1.674 0-4.168 0.531-4.168 2.656 0 1.961 2.207 2.718 3.842 2.718 1.522 0 3.219-0.613 3.219-2.401z" fill="currentColor"/></svg>',
			'ics'     => '<svg xmlns="http://www.w3.org/2000/svg" width="17" height="17" viewBox="0 0 17 17"><path d="M17 16v1h-17v-1h17zM13.354 8.854l-0.707-0.707-3.646 3.646v-11.793h-1v11.794l-3.647-3.648-0.708 0.708 4.854 4.853 4.854-4.853z" fill="currentColor"/></svg>',
			'outlook' => '<svg xmlns="http://www.w3.org/2000/svg" width="17" height="17" viewBox="0 0 17 17"><path d="M7.734 15.723l9.266 1.277v-8.030h-9.266v6.753zM8.734 9.97h7.266v5.883l-7.266-1.002v-4.881zM0 14.661l6.967 0.959v-6.65h-6.967v5.691zM1 9.97h4.967v4.503l-4.967-0.684v-3.819zM0 8.112h6.967v-6.732l-6.967 0.959v5.773zM1 3.21l4.967-0.684v4.585h-4.967v-3.901zM7.734 1.277v6.835h9.266v-8.112l-9.266 1.277zM16 7.112h-7.266v-4.963l7.266-1.002v5.965z" fill="currentColor"/></svg>',
			'yahoo'   => '<svg xmlns="http://www.w3.org/2000/svg" width="17" height="17" viewBox="0 0 17 17"><path d="M9.5 16.5c-0.327-0.058-0.664-0.105-1.010-0.105-0.336 0-0.673 0.048-1.009 0.105l0.125-6.799c-1.789-3.086-3.472-6.229-5.442-9.201 0.336 0.087 0.682 0.144 1.038 0.144s0.721-0.068 1.067-0.144c1.346 2.386 2.808 4.702 4.221 7.048 1.423-2.327 2.933-4.644 4.222-7.048 0.336 0.087 0.683 0.135 1.028 0.135 0.366 0 0.741-0.048 1.096-0.135-0.77 1.058-1.404 2.211-2.067 3.336-1.145 1.952-2.27 3.905-3.395 5.865l0.126 6.799z" fill="currentColor"/></svg>',
		);

		return isset( $icons[ $service ] ) ? $icons[ $service ] : '';
	}

	/**
	 * Convert date string to ISO 8601 format.
	 *
	 * @param string $date_string Date string in various formats.
	 * @return string ISO 8601 formatted date or empty string.
	 */
	private function convert_to_iso_date( $date_string ) {
		if ( empty( $date_string ) ) {
			return '';
		}

		// If already in ISO format (YYYY-MM-DD or YYYY-MM-DDTHH:mm:ss), normalize it.
		if ( preg_match( '/^\d{4}-\d{2}-\d{2}/', $date_string ) ) {
			// Replace space with T for proper ISO 8601 format.
			$date_string = str_replace( ' ', 'T', $date_string );
			if ( ! preg_match( '/T/', $date_string ) ) {
				return $date_string . 'T00:00:00';
			}
			// Return with T separator.
			return $date_string;
		}

		$date = false;

		// Try common ACF date/time formats.
		// Prioritize ACF return formats (Y-m-d) before display formats (d/m/Y).
		$formats = array(
			'Y-m-d H:i:s',
			'Y-m-d',
			'd/m/Y H:i:s',
			'd/m/Y H:i',
			'd/m/Y g:i a',
			'd/m/Y',
			'm/d/Y H:i:s',
			'm/d/Y H:i',
			'm/d/Y g:i a',
			'm/d/Y',
		);

		foreach ( $formats as $format ) {
			$date = \DateTime::createFromFormat( $format, $date_string );
			if ( false !== $date ) {
				break;
			}
		}

		// If all formats failed, try generic parsing as last resort.
		if ( false === $date ) {
			try {
				$date = new \DateTime( $date_string );
			} catch ( \Exception $e ) {
				return '';
			}
		}

		// Always return date with time (YYYY-MM-DDTHH:mm:ss).
		return $date->format( 'Y-m-d\TH:i:s' );
	}
}
