<?php
namespace JET_APB\DB;

use JET_APB\Plugin;
use JET_APB\Tools;
use JET_APB\Resources\Appointment_Model;
use JET_APB\Calendar;
use JET_APB\Appointment_Eligibility_Validator;

/**
 * Database manager class
 */

// If this file is called directly, abort.
if ( ! defined( 'WPINC' ) ) {
	die;
}

/**
 * Define Base DB class
 */
class Manager {

	public $appointments;
	public $appointments_meta;
	public $excluded_dates;
	public $dates_to_exclude = array();
	public $appointments_external;
	public $external_meta;

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

		$this->appointments          = new Appointments();
		$this->appointments_meta     = new Appointments_Meta();
		$this->excluded_dates        = new Excluded_Dates();
		$this->appointments_external = new Appointments_External();
		$this->external_meta         = new External_Meta();

		// Adjust excluded dates state on appointments status switch from valid to invalid and vice versa
		add_action( 'jet-apb/db/update/appointments', [ $this, 'adjust_excluded_dates' ], 10, 3 );

	}

	/**
	 * Adjust excluded dates state on appointments status switch from valid to invalid and vice versa
	 *
	 * @param  [type] $new_appointment [description]
	 * @param  [type] $update_by       [description]
	 * @param  [type] $old_appointment [description]
	 * @return [type]                  [description]
	 */
	public function adjust_excluded_dates( $new_appointment, $update_by, $old_appointment ) {

		$new_status = ! empty( $new_appointment['status'] ) ? $new_appointment['status'] : false;
		$old_status = ! empty( $old_appointment['status'] ) ? $old_appointment['status'] : false;

		if ( in_array( $new_status, Plugin::instance()->statuses->invalid_statuses() )
			&& in_array( $old_status, Plugin::instance()->statuses->exclude_statuses() )
		) {
			Plugin::instance()->db->remove_appointment_date_from_excluded( $new_appointment );
		}

		if ( in_array( $old_status, Plugin::instance()->statuses->invalid_statuses() )
			&& in_array( $new_status, Plugin::instance()->statuses->exclude_statuses() )
		) {
			Plugin::instance()->db->maybe_exclude_appointment_date( $new_appointment );
		}

	}

	/**
	 * Remove date of passed appoinemtnt from excluded dates
	 *
	 * @param  [type] $appointment [description]
	 * @return [type]              [description]
	 */
	public function remove_appointment_date_from_excluded( $appointment ) {

		if ( is_integer( $appointment ) ) {
			$appointment = $this->get_appointment_by( 'ID', $appointment );
		}
		if ( is_array( $appointment ) ) {
			$appointment = new Appointment_Model( $appointment );
		}

		if ( ! $appointment ) {
			return;
		}

		$excluded_where = array();

		if ( ! empty( $appointment->get( 'date' ) ) ) {
			$excluded_where['date'] = $appointment->get( 'date' );
		}

		if ( ! empty( $appointment->get( 'service' ) ) ) {
			$excluded_where['service'] = $appointment->get( 'service' );
		}

		if ( ! empty( $appointment->get( 'provider' ) ) ) {
			$excluded_where['provider'] = $appointment->get( 'provider' );
		}

		$this->excluded_dates->delete( $excluded_where );

	}

	/**
	 * Check if this appointmetn is available
	 *
	 * @param  [type] $appointment_data [description]
	 * @return [type]                   [description]
	 */
	public function appointment_available( $appointment ) {
		$validator = new Appointment_Eligibility_Validator();
		return $validator->validate( $appointment );
	}

	/**
	 * Delete appointment from DB
	 *
	 * @param  [type] $appointment_id [description]
	 * @return [type]                 [description]
	 */
	public function delete_appointment( $appointment_id ) {

		$appointment = $this->get_appointment_by( 'ID', $appointment_id );

		if ( ! $appointment ) {
			return;
		}

		$appointment_where = array(
			'ID' => $appointment_id,
		);

		$this->appointments->delete( $appointment_where );

		$this->appointments_meta->create_table( false );
		$this->appointments_meta->delete( [
			'appointment_id' => $appointment_id,
		] );

		$this->remove_appointment_date_from_excluded( $appointment );
		$this->maybe_remove_excluded_app( $appointment );

		/**
		 * Trigger hook after appoinetment deletion
		 */
		do_action( 'jet-apb/db/delete/appointments', $appointment );
	}

	/**
	 * Insert new appointment and maybe add excluded date
	 *
	 * @param  array  $appointment [description]
	 * @return [type]              [description]
	 */
	public function add_appointment( $appointment = array() ) {
		$app_model = new Appointment_Model( $appointment );
		return $app_model->save();
	}

	/**
	 * Maybe add appointment date to excluded
	 *
	 * @param  [type] $appointment [description]
	 * @return [type]              [description]
	 */
	public function maybe_exclude_appointment_date( $appointment, $exclude_other = true ) {
		$this->excluded_dates->maybe_exclude_appointment_date( $appointment, $exclude_other = true );
	}

	public function maybe_exclude_other_app( $appointment ) {
		$this->excluded_dates->maybe_exclude_other_app( $appointment );
	}

	public function maybe_remove_excluded_app( $appointment ) {
		$this->excluded_dates->maybe_remove_excluded_app( $appointment );
	}

	/**
	 * Returns appointments detail by order id
	 *
	 * @return [type] [description]
	 */
	public function get_appointments_by( $field = 'ID', $value = null ) {

		$appointments = $this->appointments->query( [ $field => $value ] );

		if ( empty( $appointments ) ) {
			return false;
		}

		return $appointments;

	}

	/**
	 * Returns appointment detail by order id
	 *
	 * @return [type] [description]
	 */
	public function get_appointment_by( $field = 'ID', $value = null ) {

		$appointment = $this->get_appointments_by( $field, $value );
		$appointment = $this->get_appointments_meta( $appointment );

		return ! empty( $appointment ) ? $appointment[0] : false;
	}

	/**
	 * Get meta data of given appointments
	 *
	 * @param  array  $appointments [description]
	 * @return [type]               [description]
	 */
	public function get_appointments_meta( $appointments = [] ) {

		if ( empty( $appointments ) ) {
			return [];
		}

		$ids = [];
		$appointments_by_ids = [];

		foreach ( $appointments as $app ) {
			if ( is_object( $app ) ) {
				$app = $app->to_array();
			}

			$appointments_by_ids[ $app['ID'] ] = $app;
			$ids[] = $app['ID'];
		}

		$this->appointments_meta->create_table( false );

		$meta = $this->appointments_meta->query( [ 'appointment_id' => $ids ] );

		if ( ! empty( $meta ) ) {
			foreach ( $meta as $_row ) {
				if ( empty( $appointments_by_ids[ $_row['appointment_id'] ] ) ) {
					$appointments_by_ids[ $_row['appointment_id'] ]['meta'] = [];
				}

				$appointments_by_ids[ $_row['appointment_id'] ]['meta'][ $_row['meta_key'] ] = maybe_unserialize( $_row['meta_value'] );
			}

			return array_values( $appointments_by_ids );
		} else {
			return $appointments;
		}

	}

}
