<?php
namespace JET_APB\Workflows\Events;

use JET_APB\Workflows\Base_Object;
use JET_APB\Workflows\Actions_Dispatcher;
use JET_APB\Plugin;
use JET_APB\Cron\Manager as Cron_Manager;
use JET_APB\Resources\Appointment_Model;

abstract class Base_Event extends Base_Object {

	public function __construct() {
		add_action( 'jet-apb/workflows/event-controls', [ $this, 'register_event_controls' ] );
	}

	abstract public function get_id();

	public function register_event_controls() {
		
	}

	public function hook_event_handler( $settings ) {
		add_action( $this->hook(), function( $data ) use ( $settings ) {
			$this->handle_event( $data, $settings );
		} );
	}

	public function can_dispatch( $data = [], $appointment = [] ) {

		$appointment = wp_parse_args( $appointment, [ 'status' => '' ] );

		return apply_filters( 'jet-apb/workflows/events/can-dispatch', $this->_can_dispatch( $data, $appointment ), $data, $appointment, $this->get_id() );
	}

	public function _can_dispatch( $data, $appointment ) {
		return $this->is_relevant_status( $appointment['status'] );
	}

	/**
	 * Check if given appointment has a valid status.
	 * Required to prevent tirggering events for failed, cancelled or refunded appointments.
	 * https://github.com/Crocoblock/suggestions/issues/7620
	 */
	public function is_relevant_status( $status ) {
		if ( in_array( $status, Plugin::instance()->statuses->invalid_statuses() ) ) {
			return false;
		}
		return true;
	}

	public function dispatch( $data = [] ) {

		if ( empty( $data['actions'] ) ) {
			return;
		}

		add_action( $this->hook(), function( $appointment ) use ( $data ) {

			if ( ! $this->can_dispatch( $data, $appointment ) ) {
				return;
			}

			$appointment = Plugin::instance()->tools->get_appointments( $appointment );

			if ( 
				$data['event'] === 'appointment-created' 
				&& $data['excluded_group'] === true 
				&& $appointment[0]->get('group_ID') 
			) {
				return;
			}

			switch ( $data['schedule'] ) {
				case 'immediately':
					$actions = new Actions_Dispatcher( $data['actions'], $appointment );
					$actions->run();
					break;

				case 'after_appointment':
				case 'before_appointment':

					foreach ( $appointment as $single_app ) {
						Plugin::instance()->db->appointments_meta->insert( [
							'appointment_id' => $single_app->get('ID'),
							'meta_key'       => '_schedule_id_' . $data['hash'],
							'meta_value'     => 1,
						] );
					}

					$this->shcedule_dispatcher();

					break;
			}
		} );
	}

	public function dispatch_scheduled( $data = [] ) {
		
		if ( 'before_appointment' !== $data['schedule'] && 'after_appointment' !== $data['schedule'] ) {
			return;
		}

		$meta_table = Plugin::instance()->db->appointments_meta->table();
		$app_table  = Plugin::instance()->db->appointments->table();

		$days = 0;
		if ( array_key_exists( 'days_schedule', $data ) ) {
			$days = absint( $data['days_schedule'] );
		} elseif ( array_key_exists( 'days_before', $data ) ) {
			$days = absint( $data['days_before'] );
		}

		$hours = 0;
		if ( array_key_exists( 'hours_schedule', $data ) ) {
			$hours = absint( $data['hours_schedule'] );
		} elseif ( array_key_exists( 'hours_before', $data ) ) {
			$hours = absint( $data['hours_before'] );
		}
		
		if ( 'before_appointment' === $data['schedule'] ) {
			$until = time() + ( $days * DAY_IN_SECONDS ) + ( $hours * HOUR_IN_SECONDS );
		}

		if ( 'after_appointment' === $data['schedule'] ) {
			$until = time() - ( $days * DAY_IN_SECONDS ) - ( $hours * HOUR_IN_SECONDS );
		}

		$hash  = $data['hash'];

		// phpcs:disable
		if ( Plugin::instance()->workflows->is_debug() && ! empty( $_GET['date'] ) ) {
			$until = strtotime( $_GET['date'] ) + ( $days * DAY_IN_SECONDS ) + ( $hours * HOUR_IN_SECONDS );
		}
		// phpcs:enable

		$appointments = Plugin::instance()->db->appointments_meta->wpdb()->get_results( "SELECT ap.*, am.meta_key FROM $meta_table AS am INNER JOIN $app_table AS ap ON ap.ID = am.appointment_id WHERE am.meta_key = '_schedule_id_$hash' AND ap.slot <= $until", ARRAY_A );

		foreach ( $appointments as $appointment ) {

			if ( ! $this->can_dispatch( $data, $appointment ) ) {
				continue;
			}

			if ( Plugin::instance()->workflows->is_debug() ) {
				echo '<br>==============================================<br>';
				echo 'Run actions: ' . esc_html( $appointment['ID'] ) . ', ' . esc_html( date( 'Y-m-d H:i', $appointment['slot'] ) );
				echo '<br>==============================================<br>';
			}

			$actions = new Actions_Dispatcher( $data['actions'], $appointment );
			$actions->run();

			if ( ! Plugin::instance()->workflows->is_debug() ) {
				Plugin::instance()->db->appointments_meta->delete( [ 
					'appointment_id' => $appointment['ID'], 
					'meta_key' => '_schedule_id_' . $hash 
				] );
			} else {
				echo '<br>==============================================<br>';
				echo 'To delete - ' . esc_html( $appointment['ID'] ) . ', ' . esc_html( date( 'Y-m-d H:i', $appointment['slot'] ) );
				echo '<br>==============================================<br>';
			}

		}

	}

	public function shcedule_dispatcher() {
		$cron_shecdule = Cron_Manager::instance()->get_schedules( 'jet-apb-events-dispatcher' );
		$cron_shecdule->schedule_event();
	}

	abstract public function hook();

}
