<?php

defined( 'ABSPATH' ) || exit; // Exit if accessed directly


add_shortcode( 'gravitycalendar', 'gv_calendar_shortcode_handler' );
add_shortcode( 'gravitycalendar_link', 'gv_calendar_link_shortcode_handler' );
add_shortcode( 'gravitycalendar_copy_button', 'gv_calendar_copy_button_shortcode_handler' );
add_shortcode( 'gravitycalendar_export_button', 'gv_calendar_export_button_shortcode_handler' );

// Back-compat
add_shortcode( 'gv_calendar', 'gv_calendar_shortcode_handler' );
add_shortcode( 'gv_calendar_link', 'gv_calendar_link_shortcode_handler' );
add_shortcode( 'gv_calendar_copy_button', 'gv_calendar_copy_button_shortcode_handler' );
add_shortcode( 'gv_calendar_export_button', 'gv_calendar_export_button_shortcode_handler' );

/**
 * Returns whether a calendar feed is valid: exists, is active, and has a valid secret (if the feed is secure).
 *
 * @since 2.6
 *
 * @param int $feed_id The ID of the feed to check.
 * @param string $secret The secret to check, if the feed is set to be secure.
 * @param string $thing_to_display Optional. The type of shortcode being displayed. This is only used in error messages.
 *
 * @return true|WP_Error True if valid, WP_Error if invalid.
 */
function gravitycalendar_is_valid_feed( $feed_id, string $secret = '', $thing_to_display = 'shortcode' ) {
	$calendar = GV_Extension_Calendar_Feed::get_instance();
	$feed     = $calendar->get_feed( $feed_id );
	$form_id  = (int) rgar( $feed, 'form_id' );

	// Invalid feed ID.
	if ( empty( $feed ) || empty( $form_id )) {
		return new WP_Error( 'invalid_feed', sprintf( esc_html__( '%s: Invalid Feed ID provided.', 'gk-gravitycalendar' ), 'Gravity Forms Calendar' ) );
	}

	if ( $calendar->is_secure( $feed ) && ! $calendar->is_valid_secret( $feed, $secret ) ) {
		return new WP_Error( 'invalid_secret', sprintf(
			esc_html__( '%1$s: Invalid feed secret provided. Update with the new shortcode: %2$s', 'gk-gravitycalendar' ),
			'Gravity Forms Calendar',
			'<code>' . GV_Extension_Calendar_Feed::get_instance()->get_shortcode_for_feed( $feed ) . '</code>'
		) );
	}

	if( empty( $feed['is_active'] ) ) {
		$admin_url = admin_url( 'admin.php?page=gf_edit_forms&view=settings&subview=gravityview-calendar&id=' . $form_id );

		$error = strtr(
			'{error_message} <a href="{admin_url}">{enable_the_feed}</a> {to_display}',
			array(
				/** translators: The first replacement is the name of the plugin. The second %s is replaced with what is being displayed; "calendar", "link", etc. */
				'{error_message}'   => sprintf( esc_html__( '%1$s: This %2$s is not being displayed because the feed is disabled.', 'gk-gravitycalendar' ), 'Gravity Forms Calendar', $thing_to_display ),
				'{admin_url}'       => esc_url( $admin_url ),
				'{enable_the_feed}' => esc_html__( 'Enable the feed', 'gk-gravitycalendar' ),
				/** translators: %s is replaced with what is being displayed; "calendar", "link", etc. */
				'{to_display}'      => sprintf( esc_html__( 'to display this %s.', 'gk-gravitycalendar' ), $thing_to_display ),
			)
		);

		return new WP_Error( 'inactive_feed', $error );
	}

	return true;
}

/**
 * Return the error message for a WP_Error object. If the user can't edit forms, don't show the error message at all.
 *
 * @since 2.6
 *
 * @param WP_Error $wp_error The error object to display.
 *
 * @return string Empty string if the user lacks permission to view the error. Otherwise, the error message.
 */
function gv_calendar_get_feed_error_message( WP_Error $wp_error ) {

	// If the user can't edit forms, don't show the error message at all.
	if ( ! GFCommon::current_user_can_any( [ 'gravityforms_edit_forms' ] ) ) {
		return '';
	}

	return '<div><p>' . $wp_error->get_error_message() . '</p></div>';
}

/**
 * Wrapper function for `gv_calendar` shortcode
 *
 * @since 1.1
 *
 * @param array|string $atts Shortcode attributes
 *
 * @return string Shortcode markup
 */
function gv_calendar_shortcode_handler( $atts = [] ) {

	$addon = GV_Extension_Calendar_Feed::get_instance();

	if ( $addon->is_rest_request() ) {
		return '';
	}

	if ( is_string( $atts ) ) {
		$atts = shortcode_parse_atts( $atts );
	}

	if ( empty( $atts ) ) {
		$atts = [];
	}

	$atts = shortcode_atts(
		[
			'id' => 0,
			'secret' => '',
		],
		$atts
	);

	return gv_calendar_render( $atts['id'], 0, $atts['secret'] );
}

/**
 * Render the `gv_calendar` shortcode
 *
 * @since 1.1
 *
 * @param int $feed_id ID of the feed to render
 * @param int $view_id GravityView View ID
 *
 * @return string Shortcode markup
 */
function gv_calendar_render( $feed_id = 0, $view_id = 0, string $secret = '' ) {
	$feed_id = (int) $feed_id;
	$view_id = (int) $view_id;
	$feed    = GV_Extension_Calendar_Feed::get_instance()->get_feed( $feed_id );
	$form_id = (int) rgar( $feed, 'form_id' );

	$error = gravitycalendar_is_valid_feed( $feed_id, $secret, esc_html_x( 'calendar', 'The type of shortcode being displayed. This text is used in an error message.', 'gk-gravitycalendar' ) );

	if ( is_wp_error( $error ) ) {
		return gv_calendar_get_feed_error_message( $error );
	}

	/**
	 * @filter `gravityview/calendar/shortcode/render/element_attributes` Modify Calendar element attributes
	 *
	 * @since  1.4
	 *
	 * @param array HTML attributes (e.g., array('data-some-key => 'key'))
	 */
	$attributes = apply_filters(
		'gravityview/calendar/shortcode/render/element_attributes',
		array(
			'class'            => 'gv-fullcalendar',
			'data-feed_id'     => $feed_id,
			'data-calendar_id' => wp_generate_password(),
			'data-view_id'     => $view_id,
		)
	);

	array_walk(
		$attributes,
		function ( &$v, $k ) {
			$v = sprintf( '%s="%s"', esc_attr( $k ), esc_attr( $v ) );
		}
	);

	$markup = sprintf( '<div %s></div>', join( ' ', $attributes ) );

	$GV_Calendar_Feed = GV_Extension_Calendar_Feed::get_instance();

	$GV_Calendar_Feed->output_calendar_scripts( $feed_id, $form_id, $view_id );

	$GV_Calendar_Feed->output_calendar_styles( $feed_id );

	return $markup;
}

/**
 * Render the `gv_calendar_link` shortcode
 *
 * @since 2.0
 *
 * @param array|string $atts
 *
 * @return string Empty string if feed not found or feed doesn't have ICS URL enabled. Escaped URL or link otherwise.
 */
function gv_calendar_link_shortcode_handler( $atts = array() ) {

	if ( is_string( $atts ) ) {
		$atts = shortcode_parse_atts( $atts );
	}

	if ( empty( $atts ) ) {
		$atts = array();
	}

	if ( ! empty( $atts['html_link'] ) && empty( $atts['text'] ) ) {
		if ( empty( $atts['webcal'] ) ) {
			$atts['text'] = esc_html__( 'Export Calendar', 'gk-gravitycalendar' );
		} else {
			$atts['text'] = esc_html__( 'Subscribe to Calendar', 'gk-gravitycalendar' );
		}
	}

	$defaults = array(
		'id'        => 0,
		'text'      => esc_html__( 'Subscribe to Calendar', 'gk-gravitycalendar' ),
		'webcal'    => 1,
		'target'    => '_self',
		'html_link' => 1,
		'event_id'  => 0,
		'secret'    => '',
	);

	$atts = shortcode_atts(
		$defaults,
		$atts
	);

	if ( empty( $atts['id'] ) ) {
		return '';
	}

	$error = gravitycalendar_is_valid_feed( $atts['id'], $atts['secret'], esc_html_x( 'link', 'The type of shortcode being displayed. This text is used in an error message.', 'gk-gravitycalendar' ) );

	if ( is_wp_error( $error ) ) {
		return gv_calendar_get_feed_error_message( $error );
	}

	try {
		$url = gv_calendar_get_ics_url( $atts['id'], $atts['event_id'], $atts['secret'] );
	} catch ( Exception $e ) {
		return $e->getMessage();
	}

	if ( empty( $url ) ) {
		return '';
	}

	/** @var bool $is_iphone Is iPhone or other Apple browsers (iPad, for example), which support webcal:// */
	global $is_iphone;

	if ( ! empty( $atts['webcal'] ) && ( $is_iphone || ! wp_is_mobile() ) ) {
		// Replace scheme with webcal
		$url = preg_replace( '#^\w+://#', 'webcal://', $url );
	}

	$url = esc_url( $url );

	if ( empty( $atts['html_link'] ) ) {
		return $url;
	}

	return strtr(
		'<a href="{url}" {target}>{content}</a>',
		array(
			'{url}'     => $url,
			'{target}'  => ! empty( $atts['target'] ) ? ' target="' . esc_attr( $atts['target'] ) . '"' : '',
			'{content}' => $atts['text'],
		)
	);
}

/**
 * Render `gv_calendar_copy_button` shortcode
 *
 * @since 2.0
 *
 * @param array|string $atts Shortcode attributes
 *
 * @return string Shortcode markup
 */
function gv_calendar_copy_button_shortcode_handler( $atts = array() ) {
	if ( is_string( $atts ) ) {
		$atts = shortcode_parse_atts( $atts );
	}

	if ( empty( $atts ) ) {
		$atts = array();
	}

	if ( empty( $atts['text'] ) ) {
		if ( empty( $atts['webcal'] ) ) {
			$atts['text'] = esc_html__( 'Copy Calendar Export URL', 'gk-gravitycalendar' );
		} else {
			$atts['text'] = esc_html__( 'Copy Calendar Feed URL', 'gk-gravitycalendar' );
		}
	}

	$atts = shortcode_atts(
		array(
			'id'     => 0,
			'text'   => esc_html__( 'Copy Calendar Feed URL', 'gk-gravitycalendar' ),
			'webcal' => 1,
			'secret' => '',
		),
		$atts
	);

	try {
		$url = gv_calendar_get_ics_url( $atts['id'], 0, $atts['secret'] );
	} catch ( Exception $e ) {
		return $e->getMessage();
	}

	if ( empty( $url ) ) {
		return '';
	}

	$output  = '<div class="gv-calendar-ics-copy">';
	$output .= '<span class="screen-reader-text">|</span> ';
	$output .= '<a onclick="return false;" data-clipboard-text="' . esc_url( $url ) . '" class="button" href="' . esc_url( $url ) . '" role="button">' . esc_html( $atts['text'] ) . '</a>';
	$output .= '<span class="success hidden gv_calendar_copy_text" style="padding: 0 1em;" aria-hidden="true">' . esc_html__( 'Copied!', 'gk-gravitycalendar' ) . '</span>';
	$output .= '</div>';

	if ( ! defined( 'REST_REQUEST' ) || ! REST_REQUEST ) {
		GV_Extension_Calendar_Feed::gv_calendar_clipboard_frontend_scripts();

		wp_enqueue_script( 'clipboard' );

		wp_register_style( 'gv-calendar-ics-copy', false );
		wp_enqueue_style( 'gv-calendar-ics-copy' );

		wp_add_inline_style(
			'gv-calendar-ics-copy',
			<<<CSS
			.gv-calendar-ics-copy {
			  display: flex;
			  align-items: center;
			}
			
			.gv-calendar-ics-copy .hidden {
			  display: none;
			}
			
			@media (max-width: 480px) {
			  .gv-calendar-ics-copy {
			    flex-direction: column;
			  }
			}
CSS
		);

	}

	return $output;
}

/**
 * Render `gv_calendar_export_button` shortcode
 *
 * @since 2.0
 *
 * @param array $atts Shortcode attributes
 *
 * @return string Shortcode markup
 */
function gv_calendar_export_button_shortcode_handler( $atts = array() ) {

	if ( is_string( $atts ) ) {
		$atts = shortcode_parse_atts( $atts );
	}

	if ( empty( $atts ) ) {
		$atts = array();
	}

	if ( empty( $atts['text'] ) ) {
		if ( empty( $atts['webcal'] ) ) {
			$atts['text'] = esc_html__( 'Export Calendar', 'gk-gravitycalendar' );
		} else {
			$atts['text'] = esc_html__( 'Subscribe to Calendar', 'gk-gravitycalendar' );
		}
	}

	$atts = shortcode_atts(
		array(
			'id'     => 0,
			'text'   => esc_html__( 'Export Calendar', 'gk-gravitycalendar' ),
			'webcal' => 1,
			'target' => '_self',
			'secret' => '',
		),
		$atts
	);

	try {
		$url = gv_calendar_get_ics_url( $atts['id'], 0, $atts['secret'] );
	} catch ( Exception $e ) {
		return $e->getMessage();
	}

	if ( empty( $url ) ) {
		return '';
	}

	/** @var bool $is_iphone Is iPhone or other Apple browsers (iPad, for example), which support webcal:// */
	global $is_iphone;

	if ( ! empty( $atts['webcal'] ) && ( $is_iphone || ! wp_is_mobile() ) ) {
		// Replace scheme with webcal
		$url = preg_replace( '#^\w+://#', 'webcal://', $url );
	}

	$output  = '<div class="gv-calendar-ics-export">';
	$output .= '<a class="button" role="button" href="' . esc_url( $url ) . '" target="' . esc_attr( $atts['target'] ) . '">' . esc_html( $atts['text'] ) . '</a>';
	$output .= '</div>';

	return $output;
}

/**
 * Get Calendar ICS URL
 *
 * @since 2.0
 *
 * @param string  $feed_id
 *
 * @param integer $event_id
 *
 * @throws Exception
 *
 * @return string
 */
function gv_calendar_get_ics_url( $feed_id, $event_id = 0, string $secret = '' ) {

	$error = gravitycalendar_is_valid_feed( $feed_id, $secret, esc_html_x( 'button', 'The type of shortcode being displayed. This text is used in an error message.', 'gk-gravitycalendar' ) );

	if ( is_wp_error( $error ) ) {
		$error_message = gv_calendar_get_feed_error_message( $error );

		throw new Exception( $error_message );
	}

	if ( is_numeric( $feed_id ) ) {
		$feed = GV_Extension_Calendar_Feed::get_instance()->get_feed( $feed_id );
	}

	if ( empty( $feed ) || is_wp_error( $feed ) ) {
		return '';
	}

	if ( empty( $feed['meta']['enable-ics-feed'] ) && ! $event_id ) {
		return '';
	}

	if ( empty( $feed['meta']['export-ics-url'] ) ) {
		return '';
	}

	$url = $feed['meta']['export-ics-url'];

	if ( $event_id !== 0 ) {
		$url = str_replace( 'v1/feeds', 'v1/feeds/' . $event_id . '', $url );
	}

	return set_url_scheme( $url );
}
