<?php
class OsServiceModel extends OsModel {
	public $id,
	  $name                            = '',
	  $short_description,
	  $selection_image_id,
	  $description_image_id,
	  $is_price_variable,
	  $price_min,
	  $price_max,
	  $charge_amount,
	  $deposit_amount,
		$duration_name                 = '',
	  $duration                        = 60,
	  $buffer_before,
	  $buffer_after,
	  $category_id,
	  $override_default_booking_status = false,
	  $status,
	  $bg_color,
	  $order_number,
	  $visibility                      = LATEPOINT_SERVICE_VISIBILITY_VISIBLE,
	  $capacity_min                    = 1,
	  $capacity_max                    = 1,
	  $timeblock_interval,
	  $is_custom_price                 = false,
	  $is_custom_hours                 = false,
	  $is_custom_duration              = false,
	  $meta_class                      = 'OsServiceMetaModel',
		$services_agents_table_name,
	  $is_deposit_required,
	  $earliest_possible_booking,
	  $latest_possible_booking,
	  $deposit_value,
	  $updated_at,
	  $created_at;

	function __construct( $id = false ) {
		parent::__construct();
		$this->table_name                 = LATEPOINT_TABLE_SERVICES;
		$this->services_agents_table_name = LATEPOINT_TABLE_AGENTS_SERVICES;
		$this->nice_names                 = array(
			'name'                 => __( 'Service Name', 'latepoint' ),
			'short_description'    => __( 'Service Short Description', 'latepoint' ),
			'selection_image_id'   => __( 'Service Selection Image', 'latepoint' ),
			'description_image_id' => __( 'Service Description Image', 'latepoint' ),
			'is_price_variable'    => __( 'Variable Price', 'latepoint' ),
			'price_min'            => __( 'Minimum Price', 'latepoint' ),
			'price_max'            => __( 'Maximum Price', 'latepoint' ),
			'charge_amount'        => __( 'Charge Amount', 'latepoint' ),
			'deposit_amount'       => __( 'Deposit Amount', 'latepoint' ),
			'duration_name'        => __( 'Duration Name', 'latepoint' ),
			'duration'             => __( 'Service Duration', 'latepoint' ),
			'buffer_before'        => __( 'Buffer Before', 'latepoint' ),
			'buffer_after'         => __( 'Buffer After', 'latepoint' ),
			'bg_color'             => __( 'Background Color', 'latepoint' ),
			'category_id'          => __( 'Service Category', 'latepoint' ),
		);

		if ( $id ) {
			$this->load_by_id( $id );
		}
	}

	protected function params_to_save( $role = 'admin' ) {
		$params_to_save = array(
			'id', 
			'name', 
			'short_description', 
			'category_id',
			'selection_image_id',
			'is_price_variable',
			'price_min',
			'price_max',
			'charge_amount',
			'deposit_amount',
			'duration_name',
			'duration',
			'buffer_before',
			'buffer_after',
			'bg_color',
			'timeblock_interval',
			'override_default_booking_status',
			'order_number',
			'visibility',
			'status',
			'earliest_possible_booking',
			'latest_possible_booking',
			'capacity_min',
			'capacity_max',
			'description_image_id',
		);
		return $params_to_save;
	}

	protected function allowed_params( $role = 'admin' ) {
		$allowed_params = array(
			'id', 
			'name', 
			'short_description', 
			'category_id',
			'selection_image_id',
			'is_price_variable',
			'price_min',
			'price_max',
			'charge_amount',
			'deposit_amount',
			'duration_name',
			'duration',
			'buffer_before',
			'buffer_after',
			'bg_color',
			'timeblock_interval',
			'override_default_booking_status',
			'order_number',
			'visibility',
			'earliest_possible_booking',
			'latest_possible_booking',
			'status',
			'capacity_min',
			'capacity_max',
			'description_image_id',
		);
		return $allowed_params;
	}


	public function prepare_data_before_it_is_set( $data ) {
		if ( isset( $data['name'] ) ) {
			$data['name'] = sanitize_text_field( $data['name'] );
		}
		if ( isset( $data['short_description'] ) ) {
			$data['short_description'] = wp_kses_post( $data['short_description'] );
		}


		return $data;
	}

	public function get_default_booking_status() {
		if ( ! empty( $this->override_default_booking_status ) ) {
			$all_statuses = OsBookingHelper::get_statuses_list();
			if ( isset( $all_statuses[ $this->override_default_booking_status ] ) ) {
				return $this->override_default_booking_status;
			}
		}
		return OsBookingHelper::get_default_booking_status();
	}

	public function get_category_name() {
		if ( $this->category_id ) {
			$category = new OsServiceCategoryModel( $this->category_id );
			if ( $category->exists() ) {
				return $category->name;
			}
		}
		return '';
	}


	// determine how much capacity service can accept before the slot is blocked
	public function get_capacity_needed_before_slot_is_blocked(): int {
		$capacity_min = $this->capacity_min ? $this->capacity_min : 1;
		$capacity_max = $this->capacity_max ? $this->capacity_max : 1;
		return ( $this->get_meta_by_key( 'block_timeslot_when_minimum_capacity_met', 'off' ) == 'on' ) ? $capacity_min : $capacity_max;
	}

	public function get_timeblock_interval() {
		if ( ! $this->timeblock_interval ) {
			$this->timeblock_interval = OsSettingsHelper::get_default_timeblock_interval();
		}
		return $this->timeblock_interval;
	}

	public function filter_allowed_records(): OsModel {
		if ( ! OsRolesHelper::are_all_records_allowed( 'service' ) ) {
			$this->filter_where_conditions( [ 'id' => OsRolesHelper::get_allowed_records( 'service' ) ] );
		}
		return $this;
	}


	protected function before_create() {
	}

	public function should_show_capacity_selector() {
		return ( ( $this->capacity_max != $this->capacity_min ) && ( $this->get_meta_by_key( 'fixed_total_attendees', 'off' ) != 'on' ) );
	}

	public function is_group_service() {
		return ( $this->capacity_max > 1 );
	}

	public function get_all_durations_arr() {
		$durations = [
			[
				'id'             => 'default',
				'name'           => $this->duration_name,
				'duration'       => $this->duration,
				'charge_amount'  => $this->charge_amount,
				'deposit_amount' => $this->deposit_amount,
			],
		];
		$durations = array_merge( $durations, $this->get_extra_durations() );
		return $durations;
	}

	public function get_extra_durations() {
		$durations       = [];
		$extra_durations = $this->get_meta_by_key( 'durations', false );
		if ( $extra_durations ) {
			$extra_durations_arr = json_decode( $extra_durations, true );
			if ( ! empty( $extra_durations_arr ) ) {
				foreach ( $extra_durations_arr as $duration_id => $extra_duration ) {
					$durations[] = [
						'id'             => $duration_id,
						'name'           => ( $extra_duration['name'] ?? '' ),
						'duration'       => $extra_duration['duration'],
						'charge_amount'  => $extra_duration['charge_amount'],
						'deposit_amount' => $extra_duration['deposit_amount'],
					];
				}
			}
		}
		return $durations;
	}

	public function get_full_amount_for_duration( $duration = false ) {
		if ( $duration && $duration != $this->duration ) {
			$extra_durations = $this->get_extra_durations();
			foreach ( $extra_durations as $extra_duration ) {
				if ( $extra_duration['duration'] == $duration ) {
					return $extra_duration['charge_amount'];
				}
			}
		}
		return $this->charge_amount;
	}

	public function get_deposit_amount_for_duration( $duration = false ) {
		if ( $duration && $duration != $this->duration ) {
			$extra_durations = $this->get_extra_durations();
			foreach ( $extra_durations as $extra_duration ) {
				if ( $extra_duration['duration'] == $duration ) {
					return $extra_duration['deposit_amount'];
				}
			}
		}
		return $this->deposit_amount;
	}

	protected function set_defaults() {
		if ( empty( $this->category_id ) ) {
			$this->category_id = 0;
		}
		if ( empty( $this->buffer_before ) ) {
			$this->buffer_before = 0;
		}
		if ( empty( $this->buffer_after ) ) {
			$this->buffer_after = 0;
		}
		if ( empty( $this->price_min ) ) {
			$this->price_min = 0;
		}
		if ( empty( $this->price_max ) ) {
			$this->price_max = 0;
		}
		if ( empty( $this->charge_amount ) ) {
			$this->charge_amount = 0;
		}
		if ( empty( $this->deposit_amount ) ) {
			$this->deposit_amount = 0;
		}
		if ( empty( $this->is_deposit_required ) ) {
			$this->is_deposit_required = false;
		}
		if ( empty( $this->status ) ) {
			$this->status = LATEPOINT_SERVICE_STATUS_ACTIVE;
		}
		if ( empty( $this->bg_color ) ) {
			$this->bg_color = $this->generate_new_bg_color();
		}
	}

	public function save_custom_schedule( $work_periods ) {
		foreach ( $work_periods as &$work_period ) {
			$work_period['service_id'] = $this->id;
		}
		unset( $work_period );
		OsWorkPeriodsHelper::save_work_periods( $work_periods );
	}

	public function delete_custom_schedule() {
		$work_periods_model = new OsWorkPeriodModel();
		$work_periods       = $work_periods_model->where(
			array(
				'service_id'  => $this->id,
				'agent_id'    => 0,
				'location_id' => 0,
				'custom_date' => 'IS NULL',
			) 
		)->get_results_as_models();
		if ( is_array( $work_periods ) ) {
			foreach ( $work_periods as $work_period ) {
				$work_period->delete();
			}
		}
	}

	public function generate_new_bg_color() {
		$services               = new OsServiceModel();
		$service_colors_results = $services->select( 'bg_color' )->group_by( 'bg_color' )->get_results( ARRAY_A );
		$services_used_colors   = array_map(
			function ( $service ) {
				return $service['bg_color'];
			},
			$service_colors_results 
		);
		$default_colors         = OsServiceHelper::get_default_colors();
		$colors_left            = array_diff( $default_colors, $services_used_colors );
		if ( ! empty( $colors_left ) ) {
			// reset array
			$colors_left = array_values( $colors_left );
			$bg_color    = $colors_left[0];
		} else {
			$bg_color = '#3d52ea';
		}
		return $bg_color;
	}


	public function delete( $id = false ) {
		if ( ! $id && isset( $this->id ) ) {
			$id = $this->id;
		}
		if ( $id && $this->db->delete( $this->table_name, array( 'id' => $id ), array( '%d' ) ) ) {
			$this->db->delete( LATEPOINT_TABLE_AGENTS_SERVICES, array( 'service_id' => $id ), array( '%d' ) );
			$this->db->delete( LATEPOINT_TABLE_WORK_PERIODS, array( 'service_id' => $id ), array( '%d' ) );
			$this->db->delete( LATEPOINT_TABLE_SERVICE_META, array( 'object_id' => $id ), array( '%d' ) );
			$this->db->delete( LATEPOINT_TABLE_BOOKINGS, array( 'service_id' => $id ), array( '%d' ) );
			do_action( 'latepoint_service_deleted', $id );
			return true;
		} else {
			return false;
		}
	}

	protected function params_to_sanitize() {
		return [
			'charge_amount'  => 'money',
			'deposit_amount' => 'money',
			'price_min'      => 'money',
			'price_max'      => 'money',
		];
	}

	public function is_hidden() {
		return ( $this->visibility == LATEPOINT_SERVICE_VISIBILITY_HIDDEN );
	}

	public function should_be_active() {
		return $this->where( [ 'status' => LATEPOINT_SERVICE_STATUS_ACTIVE ] );
	}

	public function should_not_be_hidden() {
		return $this->where( [ 'visibility !=' => LATEPOINT_SERVICE_VISIBILITY_HIDDEN ] );
	}

	public function is_active() {
		return ( $this->status == LATEPOINT_SERVICE_STATUS_ACTIVE );
	}


	protected function get_price_min_formatted() {
		if ( $this->price_min > 0 ) {
			return OsMoneyHelper::format_price( $this->price_min );
		} else {
			return OsMoneyHelper::format_price( 0 );
		}
	}


	public function get_selection_image_url() {
		$default_service_image_url = LATEPOINT_IMAGES_URL . 'service-image.png';
		return OsImageHelper::get_image_url_by_id( $this->selection_image_id, 'thumbnail', $default_service_image_url );
	}

	public function get_description_image_url() {
		$default_service_image_url = LATEPOINT_IMAGES_URL . 'service-image.png';
		return OsImageHelper::get_image_url_by_id( $this->description_image_id, 'full', $default_service_image_url );
	}


	public function connect_to_agent( $agent_id, $location_id ) {
		$agent_connection_row = $this->db->get_row( $this->db->prepare( 'SELECT id FROM ' . $this->services_agents_table_name . ' WHERE service_id = %d AND agent_id = %d AND location_id = %d', array( $this->id, $agent_id, $location_id ) ) );
		if ( $agent_connection_row ) {
			// update
		} else {
			$insert_data = array(
				'agent_id'    => $agent_id,
				'service_id'  => $this->id,
				'location_id' => $location_id,
			);
			if ( $this->db->insert( $this->services_agents_table_name, $insert_data ) ) {
				return $this->db->insert_id;
			}
		}
	}

	public function save_durations( $durations ) {
		foreach ( $durations as &$duration ) {
			$duration['charge_amount']  = OsParamsHelper::sanitize_param( $duration['charge_amount'], 'money' );
			$duration['deposit_amount'] = OsParamsHelper::sanitize_param( $duration['deposit_amount'], 'money' );
		}
		unset( $duration );
		$this->save_meta_by_key( 'durations', wp_json_encode( $durations ) );
		return true;
	}



	public function delete_meta_by_key( $meta_key ) {
		if ( $this->is_new_record() ) {
			return false;
		}

		$meta = new OsServiceMetaModel();
		return $meta->delete_by_key( $meta_key, $this->id );
	}

	public function get_meta_by_key( $meta_key, $default = false ) {
		if ( $this->is_new_record() ) {
			return $default;
		}

		$meta = new OsServiceMetaModel();
		return $meta->get_by_key( $meta_key, $this->id, $default );
	}

	public function save_meta_by_key( $meta_key, $meta_value ) {
		if ( $this->is_new_record() ) {
			return false;
		}

		$meta = new OsServiceMetaModel();
		return $meta->save_by_key( $meta_key, $meta_value, $this->id );
	}

	public function save_agents() {
		foreach ( $this->agents as $agent ) {
			$agent_connection_row = $this->db->get_row( $this->db->prepare( 'SELECT id FROM ' . $this->services_agents_table_name . ' WHERE service_id = %d AND agent_id = %d', array( $this->id, $agent->id ) ) );
			if ( $agent_connection_row ) {
				$update_data = array(
					'is_custom_hours'    => $agent->is_custom_hours,
					'is_custom_price'    => $agent->is_custom_price,
					'is_custom_duration' => $agent->is_custom_duration,
				);
				$this->db->update( $this->services_agents_table_name, $update_data, array( 'id' => $agent_connection_row->id ) );
			} else {
				$insert_data = array(
					'agent_id'           => $agent->id,
					'service_id'         => $this->id,
					'is_custom_hours'    => $agent->is_custom_hours,
					'is_custom_price'    => $agent->is_custom_price,
					'is_custom_duration' => $agent->is_custom_duration,
				);
				if ( $this->db->insert( $this->services_agents_table_name, $insert_data ) ) {
					$agent_connection_row_id = $this->db->insert_id;
				}
			}
		}
		return true;
	}



	public function remove_agents_by_ids( $ids_to_remove = array() ) {
		if ( $ids_to_remove ) {
			$query = $this->db->prepare( 'DELETE FROM %i WHERE service_id = %d AND agent_id IN ' . OsModel::where_in_array_to_string( $ids_to_remove ), [ $this->services_agents_table_name, $this->id ] );
			$this->db->query( $query );
		}
	}



	public function get_agent_ids_to_remove( $new_agents = array() ) {
		$current_agent_ids = $this->get_current_agent_ids_from_db();
		$new_agent_ids     = array();
		foreach ( $new_agents as $agent ) {
			if ( $agent['connected'] == 'yes' ) {
				$new_agent_ids[] = $agent['id'];
			}
		}
		$agent_ids_to_remove = array_diff( $current_agent_ids, $new_agent_ids );
		return $agent_ids_to_remove;
	}


	public function save_agents_and_locations( $agents ) {
		if ( ! $agents ) {
			return true;
		}
		$connections_to_save   = [];
		$connections_to_remove = [];
		foreach ( $agents as $agent_key => $locations ) {
			$agent_id = str_replace( 'agent_', '', $agent_key );
			foreach ( $locations as $location_key => $location ) {
				$location_id = str_replace( 'location_', '', $location_key );
				$connection  = [
					'service_id'  => $this->id,
					'agent_id'    => $agent_id,
					'location_id' => $location_id,
				];
				if ( $location['connected'] == 'yes' ) {
					$connections_to_save[] = $connection;
				} else {
					$connections_to_remove[] = $connection;
				}
			}
		}
		if ( ! empty( $connections_to_save ) ) {
			foreach ( $connections_to_save as $connection_to_save ) {
				OsConnectorHelper::save_connection( $connection_to_save );
			}
		}
		if ( ! empty( $connections_to_remove ) ) {
			foreach ( $connections_to_remove as $connection_to_remove ) {
				OsConnectorHelper::remove_connection( $connection_to_remove );
			}
		}
		return true;
	}

	public function get_current_agent_ids_from_db() {
		$query      = $this->db->prepare( 'SELECT agent_id FROM ' . $this->services_agents_table_name . ' WHERE service_id = %d', $this->id );
		$agent_rows = $this->db->get_results( $query );

		$agent_ids = array();

		if ( $agent_rows ) {
			foreach ( $agent_rows as $agent_row ) {
				$agent_ids[] = $agent_row->agent_id;
			}
		}
		return $agent_ids;
	}


	public function get_current_agent_ids() {
		$agent_ids = array();
		foreach ( $this->agents as $agent ) {
			$agent_ids[] = $agent->id;
		}
		return $agent_ids;
	}


	public function get_agents() {
		if ( ! isset( $this->agents ) ) {
			$query       = 'SELECT * FROM ' . $this->services_agents_table_name . ' WHERE service_id = %d GROUP BY agent_id';
			$query_args  = array( $this->id );
			$agents_rows = $this->get_query_results( $query, $query_args );

			$this->agents = array();

			if ( $agents_rows ) {
				foreach ( $agents_rows as $agent_row ) {
					$agent                     = new OsAgentModel( $agent_row->agent_id );
					$agent->is_custom_hours    = $agent_row->is_custom_hours;
					$agent->is_custom_price    = $agent_row->is_custom_price;
					$agent->is_custom_duration = $agent_row->is_custom_duration;
					$this->agents[]            = $agent;
				}
			}
		}
		return $this->agents;
	}

	public function set_agents( $agent_datas ) {
		$this->agents = array();

		foreach ( $agent_datas as $agent_data ) {
			if ( $agent_data['connected'] == 'yes' ) {
				$agent                     = new OsAgentModel();
				$agent->id                 = $agent_data['id'];
				$agent->is_custom_hours    = $agent_data['is_custom_hours'];
				$agent->is_custom_price    = $agent_data['is_custom_price'];
				$agent->is_custom_duration = $agent_data['is_custom_duration'];
				$this->agents[]            = $agent;
			}
		}
		return $this;
	}


	public function has_agent( $agent_id ) {
		return OsConnectorHelper::has_connection(
			[
				'service_id' => $this->id,
				'agent_id'   => $agent_id,
			] 
		);
	}

	public function has_agent_and_location( $agent_id, $location_id ) {
		if ( $this->is_new_record() ) {
			return false;
		}
		return OsConnectorHelper::has_connection(
			[
				'service_id'  => $this->id,
				'agent_id'    => $agent_id,
				'location_id' => $location_id,
			] 
		);
	}

	public function count_number_of_connected_locations( $agent_id = false ) {
		if ( $this->is_new_record() ) {
			return 0;
		}
		$args = [ 'service_id' => $this->id ];
		if ( $agent_id ) {
			$args['agent_id'] = $agent_id;
		}
		return OsConnectorHelper::count_connections( $args, 'location_id' );
	}


	protected function properties_to_validate() {
		$validations = array(
			'name'     => array( 'presence' ),
			'duration' => array( 'presence' ),
		);
		return $validations;
	}
}
