<?php
/**
 * LifterLMS Instructor
 *
 * @package LifterLMS/Models/Classes
 *
 * @since 3.13.0
 * @version 6.0.0
 */

defined( 'ABSPATH' ) || exit;

/**
 * LLMS_Instructor model class
 *
 * Manages data and interactions with a LifterLMS Instructor or Instructor's Assistant.
 *
 * @since 3.13.0
 * @since 3.30.3 Fixed typo in "description" key of the the toArray() method.
 * @since 3.32.0 Add validation to data passed into the `get_students()` method.
 * @since 3.34.0 Fix issue causing `get_assistants()` to return assistants to the currently logged in user instead of using the user id of the current object.
 *               Add `has_student()` method.
 */
class LLMS_Instructor extends LLMS_Abstract_User_Data {

	/**
	 * Add a parent instructor to an assistant instructor
	 *
	 * @param    mixed $parent_ids WP User ID of the parent instructor or array of User IDs to add multiple
	 * @return   boolean
	 * @since    3.13.0
	 * @version  3.14.4
	 */
	public function add_parent( $parent_ids ) {

		// Get existing parents.
		$parents = $this->get( 'parent_instructors' );

		// No existing, use an empty array as the default.
		if ( ! $parents ) {
			$parents = array();
		}

		if ( ! is_array( $parent_ids ) ) {
			$parent_ids = array( $parent_ids );
		}

		// Make ints.
		$parent_ids = array_map( 'absint', $parent_ids );

		// Add the new parents.
		$parents = array_unique( array_merge( $parents, $parent_ids ) );

		// Remove duplicates and save.
		return $this->set( 'parent_instructors', array_unique( $parents ) );

	}

	/**
	 * Retrieve an array of user ids for assistant instructors attached to the instructor
	 *
	 * @since 3.14.4
	 * @since 3.34.0 Uses object ID instead of current user id.
	 *
	 * @return int[]
	 */
	public function get_assistants() {

		global $wpdb;
		$results = $wpdb->get_col(
			$wpdb->prepare(
				"SELECT user_id FROM {$wpdb->usermeta}
			 WHERE meta_key = 'llms_parent_instructors'
			   AND meta_value LIKE %s;",
				'%i:' . $this->get_id() . ';%'
			)
		); // db call ok; no-cache ok.

		return $results;

	}

	/**
	 * Retrieve instructor's courses
	 *
	 * @uses     $this->get_posts()
	 * @param    array  $args    query argument, see $this->get_posts()
	 * @param    string $return  return format, see $this->get_posts()
	 * @return   mixed
	 * @since    3.13.0
	 * @version  3.13.0
	 */
	public function get_courses( $args = array(), $return = 'llms_posts' ) {

		$args = wp_parse_args(
			$args,
			array(
				'post_type' => 'course',
			)
		);
		return $this->get_posts( $args, $return );

	}

	/**
	 * Retrieve instructor's memberships
	 *
	 * @uses     $this->get_posts()
	 * @param    array  $args    query argument, see $this->get_posts()
	 * @param    string $return  return format, see $this->get_posts()
	 * @return   mixed
	 * @since    3.13.0
	 * @version  3.13.0
	 */
	public function get_memberships( $args = array(), $return = 'llms_posts' ) {

		$args = wp_parse_args(
			$args,
			array(
				'post_type' => 'llms_membership',
			)
		);
		return $this->get_posts( $args, $return );

	}

	/**
	 * Retrieve instructor's posts (courses and memberships, mixed)
	 *
	 * @param    array  $args    query arguments passed to WP_Query
	 * @param    string $return  return format [llms_posts|ids|posts|query]
	 * @return   mixed
	 * @since    3.13.0
	 * @version  3.13.0
	 */
	public function get_posts( $args = array(), $return = 'llms_posts' ) {

		$serialized_id = serialize( // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.serialize_serialize
			array(
				'id' => $this->get_id(),
			)
		);
		$serialized_id = str_replace( array( 'a:1:{', '}' ), '', $serialized_id );

		$args = wp_parse_args(
			$args,
			array(
				'post_type'   => array( 'course', 'llms_membership' ),
				'post_status' => 'publish',
				'meta_query'  => array(
					array(
						'compare' => 'LIKE',
						'key'     => '_llms_instructors',
						'value'   => $serialized_id,
					),
				),
			)
		);

		$query = new WP_Query( $args );

		if ( 'llms_posts' === $return ) {
			$ret = array();
			foreach ( $query->posts as $post ) {
				$ret[] = llms_get_post( $post );
			}
			return $ret;
		} elseif ( 'ids' === $return ) {
			return wp_list_pluck( $query->posts, 'ID' );
		} elseif ( 'posts' === $return ) {
			return $query->posts;
		}

		// If 'query' === $return.
		return $query;

	}

	/**
	 * Retrieve instructor's students
	 *
	 * @since 3.13.0
	 * @since 3.32.0 Validate `post_id` data passed into this function to ensure only students
	 *               in courses/memberships for this instructor are returned.
	 * @since 6.0.0 Don't access `LLMS_Student_Query` properties directly.
	 *
	 * @see LLMS_Student_Query
	 *
	 * @param array $args Array of args passed to LLMS_Student_Query.
	 * @return LLMS_Student_Query
	 */
	public function get_students( $args = array() ) {

		$ids = $this->get_posts(
			array(
				'posts_per_page' => -1,
			),
			'ids'
		);

		// If post IDs were passed we need to verify they're IDs that the instructor has access to.
		if ( ! empty( $args['post_id'] ) ) {
			$args['post_id'] = ! is_array( $args['post_id'] ) ? array( $args['post_id'] ) : $args['post_id'];
			$args['post_id'] = array_intersect( $args['post_id'], $ids );
		} else {
			// No post IDs passed in, query all of the instructor's posts.
			$args['post_id'] = $ids;
		}
		// The instructor has no posts, so we want to force no results.
		// @todo add an instructor query parameter to the student query.
		if ( empty( $args['post_id'] ) ) {
			$args['per_page']      = 0;
			$args['no_found_rows'] = true;
		}

		return new LLMS_Student_Query( $args );

	}

	/**
	 * Determines if the instructor is an instructor to a specific student.
	 *
	 * @since 3.34.0
	 *
	 * @param LLMS_Student|WP_User|int $student Student or user object or WP User ID.
	 * @return bool
	 */
	public function has_student( $student ) {

		$student = llms_get_student( $student );
		if ( ! $student ) {
			return false;
		}

		$ids = $this->get_posts(
			array(
				'posts_per_page' => -1,
			),
			'ids'
		);

		if ( ! $ids ) {
			return false;
		}

		return $student->is_enrolled( $ids, 'any' );

	}

	/**
	 * Determine if the user is an instructor on a post
	 *
	 * @param    int $post_id  WP Post ID of a course or membership
	 * @return   boolean
	 * @since    3.13.0
	 * @version  3.13.0
	 */
	public function is_instructor( $post_id = null ) {

		$ret = false;

		// Use current post if no post is set.
		if ( ! $post_id ) {
			global $post;
			if ( ! $post ) {
				return apply_filters( 'llms_instructor_is_instructor', $ret, $post_id, $this );
			}
			$post_id = $post->ID;
		}

		$check_id = false;

		switch ( get_post_type( $post_id ) ) {

			case 'course':
				$check_id = $post_id;
				break;

			case 'llms_membership':
				$check_id = $post_id;
				break;

			case 'llms_question':
				$question = llms_get_post( $post_id );
				$check_id = array();
				foreach ( $question->get_quizzes() as $qid ) {
					$course = llms_get_post_parent_course( $qid );
					if ( $course ) {
						$check_id[] = $course->get( 'id' );
					}
				}
				break;

			default:
				$course = llms_get_post_parent_course( $post_id );
				if ( $course ) {
					$check_id = $course->get( 'id' );
				}
		}

		if ( $check_id ) {

			$check_ids = ! is_array( $check_id ) ? array( $check_id ) : $check_id;

			$query = $this->get_posts(
				array(
					'post__in'       => $check_ids,
					'posts_per_page' => 1,
				),
				'query'
			);

			$ret = $query->have_posts();

		}

		return apply_filters( 'llms_instructor_is_instructor', $ret, $post_id, $check_id, $this );

	}

	/**
	 * Used by exporter / cloner to get instructor data
	 *
	 * @since 3.16.11
	 * @since 3.30.3 Renamed "descrpition" key to "description".
	 *
	 * @return array
	 */
	public function toArray() {
		return array(
			'description' => $this->get( 'description' ),
			'email'       => $this->get( 'user_email' ),
			'first_name'  => $this->get( 'first_name' ),
			'id'          => $this->get_id(),
			'last_name'   => $this->get( 'last_name' ),
		);
	}

}
