[Back]
<?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;
  }
}