<?php
/**
 * Register Advanced date meta field type
 */

class Jet_Engine_Advanced_Date_Field_Data {

	public $field_type;

	/**
	 * Constructor for the class
	 */
	public function __construct( $field_type ) {

		$this->field_type = $field_type;

		add_action(
			'cx_post_meta/before_save',
			array( $this, 'save_advanced_date_meta' ),
			10, 3
		);

		add_filter(
			'cx_post_meta/pre_get_meta',
			array( $this, 'get_advacned_meta_for_editor' ),
			10, 5
		);

	}

	/**
	 * Check if given meta field config is for advanced date field
	 * 
	 * @param  array   $field [description]
	 * @return boolean        [description]
	 */
	public function is_advanced_date_field( $field = [] ) {
		return ! empty( $field['custom_type'] ) && $this->field_type === $field['custom_type'];
	}

	/**
	 * Returns value of advanced date field adapted to use in WP editor
	 * 
	 * @param  [type] $res        [description]
	 * @param  [type] $post       [description]
	 * @param  [type] $field_name [description]
	 * @param  [type] $default    [description]
	 * @param  [type] $field      [description]
	 * @return [type]             [description]
	 */
	public function get_advacned_meta_for_editor( $res, $post, $field_name, $default, $field ) {
		
		if ( ! $this->is_advanced_date_field( $field ) ) {
			return $res;
		}

		return $this->get_field_config( $post->ID, $field_name );

	}

	/**
	 * Returns end date subfield name for given advanced date field
	 * 
	 * @param  [type] $field [description]
	 * @return [type]        [description]
	 */
	public function end_date_field_name( $field ) {
		return $field . '__end_date';
	}

	/**
	 * Returns end dates subfield data for given advanced date field
	 * 
	 * @param  [type] $post_id [description]
	 * @param  [type] $field   [description]
	 * @return [type]          [description]
	 */
	public function get_end_dates( $post_id, $field ) {
		return get_post_meta( $post_id, $field . '__end_date', false );
	}

	/**
	 * Returns dates data for given advanced date field
	 * 
	 * @param  [type] $post_id [description]
	 * @param  [type] $field   [description]
	 * @return [type]          [description]
	 */
	public function get_dates( $post_id, $field ) {
		return get_post_meta( $post_id, $field, false );
	}

	public function get_date_pairs_cache_key( $post_id, $field ) {
		return 'jet_engine_advanced_date_field_pairs_' . $post_id . '_' . $field;
	}

	/**
	 * Returns date pairs for given advanced date field
	 * (if date has no end date - 'end' element in array will be false)
	 * 
	 * @return array[] Array of date pairs. Each pair is an array with 'start' and 'end' keys.
	 */
	public function get_date_pairs( $post_id, $field ) {
		global $wpdb;

		$cached = wp_cache_get( $this->get_date_pairs_cache_key( $post_id, $field ), 'jet-engine' );

		if ( ! empty( $cached ) ) {
			return $cached;
		}

		$prefix = $wpdb->prefix;
		$end_field = $this->end_date_field_name( $field );

		$sql = "SELECT meta_key, meta_value 
                FROM
                {$prefix}postmeta
                WHERE
                post_id = %d
                AND
                meta_key IN ( %s, %s )
                ORDER BY meta_id ASC";

		$records =  $wpdb->get_results(
			$wpdb->prepare(
				$sql,
				$post_id,
				$field,
				$end_field
			), \ARRAY_A
		);

		$result = [];

		foreach ( $records as $i => $record ) {
			if ( $record['meta_key'] !== $field ) {
				continue;
			}

			$next_record = isset( $records[ $i + 1 ] ) ? $records[ $i + 1 ] : false;

			if ( ! $next_record || $next_record['meta_key'] !== $end_field ) {
				$result[] = [
					'start' => $record['meta_value'],
					'end'   => false,
				];

				continue;
			}

			$result[] = [
				'start' => $record['meta_value'],
				'end'   => $next_record['meta_value'],
			];
		}

		wp_cache_set( $this->get_date_pairs_cache_key( $post_id, $field ), $result, 'jet-engine', 1 );

		return $result;
	}

	/**
	 * Returns config subfield name for given advanced date field
	 * 
	 * @param  [type] $field [description]
	 * @return [type]        [description]
	 */
	public function config_field_name( $field ) {
		return $field . '__config';
	}

	/**
	 * Returns config subfield data for given advanced date field
	 * 
	 * @param  [type] $post_id [description]
	 * @param  [type] $field   [description]
	 * @return [type]          [description]
	 */
	public function get_field_config( $post_id, $field, $decode = false ) {
	
		$config = get_post_meta( $post_id, $this->config_field_name( $field ), true );

		if ( $decode ) {
			$config = json_decode( $config );
		}

		return $config;
	}

	/**
	 * Attach custom callback to save advanced date field if given saved meta box contain it
	 * 
	 * @param  [type] $post_id            [description]
	 * @param  [type] $post               [description]
	 * @param  [type] $post_meta_instance [description]
	 * @return [type]                     [description]
	 */
	public function save_advanced_date_meta( $post_id, $post, $post_meta_instance ) {
		
		// find and advanced date fields if needed
		foreach( $post_meta_instance->args['fields'] as $name => $field ) {

			if ( ! $this->is_advanced_date_field( $field ) ) {
				continue;
			}

			add_filter( 'cx_post_meta/pre_process_key/' . $name, [ $this, 'update_field' ], 10, 3 );

		}

	}

	/**
	 * Custom callback to update advanced date value. 
	 * Using of this callback prevents default meta data processing
	 * 
	 * @param  [type] $result     [description]
	 * @param  [type] $post_id    [description]
	 * @param  [type] $field_name [description]
	 * @return [type]             [description]
	 */
	public function update_field( $result, $post_id, $field_name ) {
		
		$update_field = ! empty( $_REQUEST[ $field_name ] ) ? $_REQUEST[ $field_name ] : [];

		if ( ! is_array( $update_field ) ) {
			$update_field = json_decode( wp_unslash( $update_field ), true );
		}

		delete_post_meta( $post_id, $field_name );
		delete_post_meta( $post_id, $this->end_date_field_name( $field_name ) );
		wp_cache_delete( $this->get_date_pairs_cache_key( $post_id, $field_name ), 'jet-engine' );

		if ( ! empty( $update_field ) ) {

			if ( ! empty( $update_field['dates'] ) ) {
				$update_field['dates'] = array_values( $update_field['dates'] );
			}

			update_post_meta( $post_id, $this->config_field_name( $field_name ), json_encode( $update_field ) );
			$this->add_field_data( $post_id, $field_name, $update_field );
		}

		return true;
	}

	/**
	 * Add actual advanced dates schedule
	 * 
	 * @param [type] $post_id    [description]
	 * @param [type] $field_name [description]
	 * @param [type] $data       [description]
	 */
	public function add_field_data( $post_id, $field_name, $data ) {

		// process custom data if exists and return
		if ( ! empty( $data['dates'] ) ) {

			foreach ( $data['dates'] as $date_item ) {

				if ( empty( $date_item['date'] ) ) {
					continue;
				}

				$date_string = $date_item['date'];

				if ( ! empty( $date_item['time'] ) ) {
					$date_string .= ' ' . $date_item['time'];
				}

				$start_date = strtotime( $date_string );

				if ( ! $start_date ) {
					continue;
				}

				add_post_meta( $post_id, $field_name, $start_date, false );

				if ( ! empty( $date_item['is_end_date'] ) ) {

					$end_date_string = $date_item['end_date'] ?? '';

					if ( ! empty( $end_date_string ) && ! empty( $date_item['end_time'] ) ) {
						$end_date_string .= ' ' . $date_item['end_time'];
					}

					if ( ! empty( $end_date_string ) ) {
						$end_date = strtotime( $end_date_string );
					} else {
						$end_date = '';
					}

					add_post_meta( $post_id, $this->end_date_field_name( $field_name ), $end_date, false );

				}

			}

			// Do not process recurring dates if we already done with manual
			return;
		}

		$start_date_string = ! empty( $data['date'] ) ? $data['date'] : '';

		if ( ! empty( $data['time'] ) ) {
			$start_date_string .= ' ' . $data['time'];
		}

		$start_date = ! empty( $data['date'] ) ? strtotime( $start_date_string ) : false;

		if ( ! $start_date ) {
			return;
		}

		// Add first date
		add_post_meta( $post_id, $field_name, $start_date, false );

		$end_date_string = ! empty( $data['is_end_date'] ) && ! empty( $data['end_date'] ) ? $data['end_date'] : false;

		if ( $end_date_string && $data['end_time'] ) {
			$end_date_string .= ' ' . $data['end_time'];
		}

		$end_date = ! empty( $end_date_string ) ? strtotime( $end_date_string ) : false;

		if ( $end_date ) {
			// Add real first end date
			add_post_meta( $post_id, $this->end_date_field_name( $field_name ), $end_date, false );
		}

		// if not recurring - nothing to do more. If yes - iterate dates
		if ( empty( $data['is_recurring'] ) ) {
			return;
		}

		if ( ! class_exists( 'Jet_Engine_Advanced_Date_Recurring_Dates' ) ) {
			require_once jet_engine()->plugin_path( 'includes/modules/calendar/advanced-date-field/recurring-dates.php' );
		}

		// Add initial timestamp to start recurring from
		$data['initial_timestamp'] = $start_date;

		$recurring_dates = new Jet_Engine_Advanced_Date_Recurring_Dates( $data );
		$dates           = $recurring_dates->generate();
		$end_dates       = [];

		/*
		error_log( 'Start dates:' );
		error_log( print_r( array_map( function( $date ) {
			return date( 'l, d F Y', $date );
		}, $dates ), true ) );
		*/

		if ( $end_date && ! empty( $dates ) ) {
			$diff      = $end_date - $start_date;
			$end_dates = array_map( function( $date ) use ( $diff ) {
				return $date + $diff;
			}, $dates );
		}

		/*
		if ( ! empty( $end_dates ) ) {
			error_log( 'End dates:' );
			error_log( print_r( array_map( function( $date ) {
				return date( 'l, d F Y', $date );
			}, $end_dates ), true ) );
		}
		*/

		foreach( $dates as $index => $date ) {
			add_post_meta( $post_id, $field_name, $date, false );
			if ( ! empty( $end_dates[ $index ] ) ) {
				add_post_meta( $post_id, $this->end_date_field_name( $field_name ), $end_dates[ $index ], false );
			}
		}

	}

}
