<?php
namespace JET_ABAF\Components\Bricks_Views\Dynamic_Data;

use JET_ABAF\Price;
use Bricks\Integrations\Dynamic_Data\Providers\Base;

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

class Provider_Jet_Booking extends Base {

	/**
	 * Register tags.
	 *
	 * @since 4.1.0
	 *
	 * @return void
	 */
	public function register_tags() {

		$tags = $this->get_tags_config();

		foreach ( $tags as $key => $tag ) {
			$this->tags[ $key ] = [
				'name'     => '{' . $key . '}',
				'label'    => $tag['label'],
				'group'    => $tag['group'],
				'provider' => $this->name,
			];
		}

	}

	/**
	 * Get tags config.
	 *
	 * @since 4.1.0
	 *
	 * @return array
	 */
	public function get_tags_config() {
		return [
			'jet_booking_accommodation_status' => [
				'label' => __( 'Accommodation Status', 'jet-booking' ),
				'group' => __( 'JetBooking', 'jet-booking' ),
			],
			'jet_booking_booking_price'        => [
				'label' => __( 'Booking Price', 'jet-booking' ),
				'group' => __( 'JetBooking', 'jet-booking' ),
			],
			'jet_booking_bookings_count'       => [
				'label' => __( 'Bookings Count', 'jet-booking' ),
				'group' => __( 'JetBooking', 'jet-booking' ),
			],
			'jet_booking_price_per_night'      => [
				'label' => __( 'Price per day/night', 'jet-booking' ),
				'group' => __( 'JetBooking', 'jet-booking' ),
			],
			'jet_booking_units_count'          => [
				'label' => __( 'Units count', 'jet-booking' ),
				'group' => __( 'JetBooking', 'jet-booking' ),
			],
		];
	}

	/**
	 * Get tag value.
	 *
	 * @since 4.1.0
	 *
	 * @param string   $tag     Tag name.
	 * @param \WP_Post $post    Post object.
	 * @param array    $args    Tag arguments.
	 * @param string   $context Tag context.
	 *
	 * @return array|string
	 * @throws \Exception
	 */
	public function get_tag_value( $tag, $post, $args, $context ) {

		$post_id = ! empty( $post->ID ) ? $post->ID : get_the_ID();

		if ( ! $post_id ) {
			return '';
		}

		$filters = $this->get_filters_from_args( $args );
		$value   = '';

		switch ( $tag ) {
			case 'jet_booking_accommodation_status':
				$value = $this->get_accommodation_status( $post_id, $filters );
				break;

			case 'jet_booking_booking_price':
				$value = $this->get_booking_price( $post_id, $filters );
				break;

			case 'jet_booking_bookings_count':
				$value = $this->get_bookings_count( $post_id, $filters );
				break;

			case 'jet_booking_price_per_night':
				$value = $this->get_price_per_night( $post_id, $filters );
				break;

			case 'jet_booking_units_count':
				$value = $this->get_units_count( $post_id, $filters );
				break;
		}

		return $this->format_value_for_context( $value, $tag, $post_id, $filters, $context );

	}

	/**
	 * Get accommodation status value.
	 *
	 * @since 4.1.0
	 *
	 * @param int   $post_id Post ID.
	 * @param array $filters Dynamic data filters list.
	 *
	 * @return string
	 * @throws \Exception
	 */
	private function get_accommodation_status( $post_id, $filters ) {

		$show_reserved_label = apply_filters( 'jet-booking/accommodation-status/show-reserved-label', true );
		$from                = strtotime( 'today' );
		$to                  = $show_reserved_label ? strtotime( '+1 week', $from ) : strtotime( '+6 months', $from );
		$period              = jet_abaf()->tools->get_booking_period( $from, $to, $post_id );
		$booked_dates        = jet_abaf()->settings->get_off_dates( $post_id );
		$disabled_days       = jet_abaf()->settings->get_days_by_rule( $post_id );
		$dates_count         = 0;
		$available_date      = '';

		foreach ( $period as $value ) {
			$available_date = date_i18n( get_option( 'date_format' ), $value->getTimestamp() );

			if ( ! in_array( $value->format( 'Y-m-d' ), $booked_dates, true ) && ! in_array( $value->format( 'w' ), $disabled_days, true ) ) {
				break;
			}

			$dates_count ++;
		}

		$available_label = $this->get_filter_value( $filters, 'jb-available-label', __( 'Available', 'jet-booking' ) );
		$pending_label   = $this->get_filter_value( $filters, 'jb-pending-label', __( 'Available on', 'jet-booking' ) );
		$reserved_label  = $this->get_filter_value( $filters, 'jb-reserved-label', __( 'Reserved', 'jet-booking' ) );

		$format = '<span class="status %s">%s %s</span>';

		if ( ! $dates_count ) {
			return sprintf( $format, 'available', esc_html( $available_label ), '' );
		} elseif ( $dates_count >= 7 && $show_reserved_label ) {
			return sprintf( $format, 'reserved', esc_html( $reserved_label ), '' );
		}

		return sprintf( $format, 'pending', esc_html( $pending_label ), esc_html( $available_date ) );

	}

	/**
	 * Get booking price value.
	 *
	 * @since 4.1.0
	 *
	 * @param int   $post_id Post ID.
	 * @param array $filters Dynamic data filters list.
	 *
	 * @return string
	 * @throws \Exception
	 */
	private function get_booking_price( $post_id, $filters ) {

		$currency          = $this->get_filter_value( $filters, 'jb-currency', __( '$', 'jet-booking' ) );
		$currency_position = $this->get_filter_value( $filters, 'jb-position', 'before' );
		$currency_position = $this->normalize_choice( $currency_position, [ 'before', 'after' ], 'before' );

		$price  = new Price( $post_id );
		$period = apply_filters( 'jet-booking/bricks-views/dynamic-tags/booking-price/period', [] );

		if ( empty( $period ) ) {
			$from = $this->get_filter_value( $filters, 'jb-start', '' );
			$to   = $this->get_filter_value( $filters, 'jb-end', '' );

			$from = $from ? strtotime( $from ) : '';
			$to   = $to ? strtotime( $to ) : '';

			if ( empty( $from ) ) {
				return $price->get_min_price( $currency, $currency_position );
			}

			if ( empty( $to ) ) {
				$to = $from;
			}
		} else {
			$from = $period[0] ?? 0;
			$to   = $period[1] ?? $from;
		}

		$data = [
			'apartment_id'   => $post_id,
			'check_in_date'  => intval( $from ),
			'check_out_date' => intval( $to ),
		];

		return $price->formatted_price( $price->get_booking_price( $data ), $currency, $currency_position );

	}

	/**
	 * Get bookings count value.
	 *
	 * @since 4.1.0
	 *
	 * @param int   $post_id Post ID.
	 * @param array $filters Dynamic data filters list.
	 *
	 * @return string|int
	 */
	private function get_bookings_count( $post_id, $filters ) {

		$from = $this->get_filter_value( $filters, 'jb-start', '' );
		$to   = $this->get_filter_value( $filters, 'jb-end', '' );

		if ( empty( $from ) ) {
			return __( 'Please select date range.', 'jet-booking' );
		}

		$from_timestamp = strtotime( $from );
		$to_timestamp   = strtotime( $to );

		if ( empty( $to ) || $to_timestamp <= $from_timestamp ) {
			$to_timestamp = $from_timestamp + 12 * HOUR_IN_SECONDS;
		}

		$units = jet_abaf()->db->get_booked_units( [
			'apartment_id'   => $post_id,
			'check_in_date'  => $from_timestamp,
			'check_out_date' => $to_timestamp,
		] );

		return ! empty( $units ) ? count( $units ) : 0;

	}

	/**
	 * Get price per night value.
	 *
	 * @since 4.1.0
	 *
	 * @param int   $post_id Post ID.
	 * @param array $filters Dynamic data filters list.
	 *
	 * @return string
	 */
	private function get_price_per_night( $post_id, $filters ) {

		$show_price = $this->get_filter_value( $filters, 'jb-show', 'default' );
		$show_price = $this->normalize_choice( $show_price, [ 'default', 'min', 'max', 'range' ], 'default' );

		$change_dynamically = $this->get_filter_value( $filters, 'jb-dynamic', 'yes' );
		$change_dynamically = $this->normalize_bool( $change_dynamically, true );

		$currency          = $this->get_filter_value( $filters, 'jb-currency', __( '$', 'jet-booking' ) );
		$currency_position = $this->get_filter_value( $filters, 'jb-position', 'before' );
		$currency_position = $this->normalize_choice( $currency_position, [ 'before', 'after' ], 'before' );

		$price = new Price( $post_id );

		return $price->get_price_for_display( [
			'show_price'             => $show_price,
			'change_dynamically'     => $change_dynamically,
			'currency_sign'          => $currency,
			'currency_sign_position' => $currency_position,
		] );

	}

	/**
	 * Get units count value.
	 *
	 * @since 4.1.0
	 *
	 * @param int   $post_id Post ID.
	 * @param array $filters Dynamic data filters list.
	 *
	 * @return string
	 */
	private function get_units_count( $post_id, $filters ) {

		$period = apply_filters( 'jet-booking/bricks-views/dynamic-tags/units-count/period', [] );

		if ( empty( $period ) || ! is_array( $period ) ) {
			$from = $this->get_filter_value( $filters, 'jb-start', '' );
			$to   = $this->get_filter_value( $filters, 'jb-end', '' );

			$from = $from ? strtotime( $from ) : time();
			$to   = $to ? strtotime( $to ) : $from;
		} else {
			$from = $period[0] ?? time();
			$to   = $period[1] ?? $from;
		}

		$units_type = $this->get_filter_value( $filters, 'jb-units-type', 'available' );
		$units_type = $this->normalize_choice( $units_type, [ 'available', 'booked' ], 'available' );

		$units_count = jet_abaf()->db->get_units_count( [
			'apartment_id'   => $post_id,
			'check_in_date'  => (int) $from,
			'check_out_date' => (int) $to,
		], $units_type );

		$units_count = $units_count ? apply_filters( 'jet-booking/bricks-views/dynamic-tags/units-count', $units_count, $this ) : '';

		return sprintf(
			'<span data-post="%1$s" data-units-count="%2$s" data-units-type="%3$s">%2$s</span>',
			esc_attr( $post_id ),
			wp_kses_post( $units_count ),
			esc_attr( $units_type )
		);

	}

	/**
	 * Get filter value with default.
	 *
	 * @since 4.1.0
	 *
	 * @param array  $filters Filters array.
	 * @param string $key     Filter key.
	 * @param mixed  $default Default value.
	 *
	 * @return mixed
	 */
	private function get_filter_value( $filters, $key, $default ) {

		if ( isset( $filters[ $key ] ) && '' !== $filters[ $key ] ) {
			return $filters[ $key ];
		}

		return $default;

	}

	/**
	 * Normalize selection value.
	 *
	 * @since 4.1.0
	 *
	 * @param string $value   Value.
	 * @param array  $allowed Allowed values.
	 * @param string $default Default value.
	 *
	 * @return string
	 */
	private function normalize_choice( $value, $allowed, $default ) {
		return in_array( $value, $allowed, true ) ? $value : $default;
	}

	/**
	 * Normalize boolean value.
	 *
	 * @since 4.1.0
	 *
	 * @param mixed   $value   Value.
	 * @param boolean $default Default boolean.
	 *
	 * @return boolean
	 */
	private function normalize_bool( $value, $default ) {

		$normalized = filter_var( $value, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE );

		if ( null === $normalized ) {
			return $default;
		}

		return $normalized;

	}

}
