<?php
/**
 * Masteriyo Multiple Instructors setup.
 *
 * @package Masteriyo\Addons\MultipleInstructors
 *
 * @since 2.3.2
 */

namespace Masteriyo\Addons\MultipleInstructors;

use Masteriyo\Enums\PostStatus;
use Masteriyo\PostType\PostType;
use WP_Query;

defined( 'ABSPATH' ) || exit;

/**
 * Main Masteriyo MultipleInstructors class.
 *
 * @class Masteriyo\Addons\MultipleInstructors\MultipleInstructors
 */

class MultipleInstructorsAddon {
	/**
	 * Initialize the application.
	 *
	 * @since 2.3.2
	 */
	public function init() {
		$this->init_hooks();
	}

	/**
	 * Initialize hooks.
	 *
	 * @since 2.3.2
	 */
	public function init_hooks() {
		add_filter( 'masteriyo_rest_course_schema', array( $this, 'add_multiple_instructors_schema' ) );
		add_action( 'masteriyo_new_course', array( $this, 'save_additional_authors' ), 10, 2 );
		add_action( 'masteriyo_update_course', array( $this, 'save_additional_authors' ), 10, 2 );
		add_filter( 'masteriyo_rest_response_course_data', array( $this, 'append_additional_authors_in_response' ), 10, 4 );
		add_filter( 'posts_where', array( $this, 'include_additional_authors_in_course_query' ), 10, 2 );
		add_filter( 'masteriyo_rest_check_permissions', array( $this, 'rest_check_permissions' ), 20, 4 );
		add_filter( 'masteriyo_rest_course_count', array( $this, 'update_course_count' ), 10, 2 );
		add_action( 'masteriyo_after_course_author', array( $this, 'display_multiple_instructors' ) );
		add_action( 'masteriyo_layout_1_single_course_aside_items', array( $this, 'display_single_course_layout1_co_authors' ), 20, 1 );

		add_filter( 'masteriyo_courses_analytics_data', array( $this, 'append_additional_authors_in_response' ), 10, 4 );
	}

	/**
	 * Update course count.
	 *
	 * @since 2.3.2
	 *
	 * @param array $post_count Course count.
	 * @param \Masteriyo\RestApi\Controllers\Version1\PostsController $controller
	 * @return void
	 */
	public function update_course_count( $post_count, $controller ) {

		// To fix additional post count for admin.
		if ( masteriyo_is_current_user_admin() ) {
			return $post_count;
		}

		$statuses_all           = array_merge( PostStatus::all(), array( PostStatus::ANY ) );
		$statuses_without_trash = array_diff( PostStatus::all(), array( PostStatus::TRASH ) );

		foreach ( $statuses_all as $status ) {
			$query_args = array(
				'post_type'      => 'mto-course',
				'post_status'    => PostStatus::ANY === $status ? $statuses_without_trash : $status,
				'posts_per_page' => -1,
				'fields'         => 'ids',
				'meta_query'     => array(
					'relation' => 'AND',
					array(
						'key'     => '_additional_authors',
						'compare' => 'EXISTS',
					),
					array(
						'key'     => '_additional_authors',
						'compare' => '=',
						'value'   => get_current_user_id(),
						'type'    => 'numeric',
					),
				),
			);

			$query = new \WP_Query( $query_args );

			if ( isset( $post_count[ $status ] ) ) {
				$post_count[ $status ] += $query->found_posts;
			}
		}

		return $post_count;
	}

	/**
	 * Display multiple instructors html.
	 *
	 * @since 2.3.2
	 *
	 * @param \Masteriyo\Models\Course $course Course object.
	 */
	public function display_multiple_instructors( $course ) {
		$additional_authors = $course->get_meta( '_additional_authors', false );
		$additional_authors = array_values( array_map( 'absint', array_unique( $additional_authors ) ) );

		if ( empty( $additional_authors ) ) {
			return;
		}

		$html = '';
		foreach ( $additional_authors as $author ) {
			$author = masteriyo_get_user( $author );

			if ( is_wp_error( $author ) || is_null( $author ) ) {
				continue;
			}

			$html .= join(
				'',
				array(
					'<div class="masteriyo-course-author masteriyo-course-additional-author">',
					sprintf(
						'<a href="%s">',
						$author->get_course_archive_url()
					),
					sprintf(
						'<img src="%s" alt="%s" title="%s"/>',
						$author->get_avatar_url(),
						$author->get_display_name(),
						$author->get_display_name()
					),
					sprintf(
						'<span class="masteriyo-course-author--name">%s</span>',
						$author->get_display_name()
					),
					'</a>',
					'</div>',
				)
			);
		}

		if ( ! empty( $html ) ) {
			echo wp_kses_post( $html );
		}
	}

	/**
	 * Update rest check permission.
	 *
	 * @since 2.3.2
	 * @param boolean $permission True if permission granted.
	 * @param string $context Permission context.
	 * @param integer $object_id Object ID which requires permission, if available.
	 * @param string $post_type Object's post type.
	 *
	 * @return boolean
	 */
	public function rest_check_permissions( $permission, $context, $object_id, $post_type ) {
		if ( 'revision' === $post_type ) {
			return $permission;
		}

		if ( ! in_array( $post_type, array( 'mto-course', 'mto-sections', 'mto-lesson', 'mto-quiz', 'mto-question' ), true ) ) {
			return $permission;
		}

		// For multiple instructors, when the permission is false, only then we need to verify the permission against additional authors.
		if ( $permission ) {
			return $permission;
		}

		if ( 'mto-course' === $post_type ) {
			$course = masteriyo_get_course( $object_id );
		} else {
			$request   = masteriyo_current_http_request();
			$course_id = get_post_meta( $object_id, '_course_id', true );
			$course_id = ( false === $course_id && isset( $request['course_id'] ) ) ? $request['course_id'] : $course_id;
			$course    = masteriyo_get_course( $course_id );
		}

		$additional_authors = array();
		if ( $course ) {
			$additional_authors = $course->get_meta( '_additional_authors', false );
			$additional_authors = array_values( array_map( 'absint', array_unique( $additional_authors ) ) );
		}

		// Bail early if the additional authors is empty.
		if ( empty( $additional_authors ) ) {
			return $permission;
		}

		$contexts = array(
			'read'   => 'read',
			'create' => 'publish_posts',
			'update' => 'edit_posts',
			'batch'  => 'edit_others_posts',
		); // Note: 'delete' context is intentionally omitted as it's not supported from this 2.8.3 version

		if ( ! isset( $contexts[ $context ] ) ) {
			return $permission;
		}

		$cap              = $contexts[ $context ];
		$post_type_object = get_post_type_object( $post_type );

		if ( $course ) {
			$permission = current_user_can( $post_type_object->cap->$cap ) && in_array( get_current_user_id(), $additional_authors, true );
		} else {
			$permission = current_user_can( $post_type_object->cap->$cap );
		}

		return $permission;
	}

	/**
	 * Include additional authors in course query.
	 *
	 * @since 2.3.2
	 *
	 * @param string $where The WHERE clause of the query.
	 * @param \WP_Query $query The WP_Query instance (passed by reference).

	 * @return string
	 */
	public function include_additional_authors_in_course_query( $where, $query ) {
		if ( PostType::COURSE !== $query->query_vars['post_type'] || ( ! is_author() && masteriyo_is_current_user_admin() ) ) {
			return $where;
		}

		remove_filter( 'posts_where', array( $this, 'include_additional_authors_in_course_query' ), 10 );

		$author_slug = $query->get( 'author_name' ) ?? $query->get( 'author' );
		$author_id   = $this->get_author_id( $author_slug );

		$course_query = new \WP_Query(
			array(
				'post_type'      => PostType::COURSE,
				'post_status'    => isset( $query->query_vars['post_status'] ) ? $query->query_vars['post_status'] : PostStatus::ANY,
				'posts_per_page' => -1,
				'fields'         => 'ids',
				'meta_query'     => array(
					'relation' => 'AND',
					array(
						'key'     => '_additional_authors',
						'compare' => 'EXISTS',
					),
					array(
						'key'     => '_additional_authors',
						'compare' => '=',
						'value'   => (int) $author_id,
						'type'    => 'numeric',
					),
				),
			)
		);

		add_filter( 'posts_where', array( $this, 'include_additional_authors_in_course_query' ), 10, 2 );

		global $wpdb;

		if ( $course_query->posts ) {

			$where .= " OR $wpdb->posts.ID IN (" . implode( ',', $course_query->posts ) . ')';
		}

		return $where;
	}

	/**
	 * Get the ID of the author based on the slug or default to the current user.
	 *
	 * @since 2.14.0
	 *
	 * @param string|null $author_slug The author slug.
	 *
	 * @return int The author ID.
	 */
	private function get_author_id( $author_slug ) {
		if ( empty( $author_slug ) ) {
			return get_current_user_id();
		}

		$author = get_user_by( 'slug', $author_slug );
		return $author ? $author->ID : get_current_user_id();
	}

	/**
	 * Append course faq to course response.
	 *
	 * @since 2.2.7
	 *
	 * @param array $data Course data.
	 * @param \Masteriyo\Models\Course $course Course object.
	 * @param string $context What the value is for. Valid values are view and edit.
	 * @param \Masteriyo\RestApi\Controllers\Version1\CoursesController $controller REST courses controller object.
	 */
	public function append_additional_authors_in_response( $data, $course, $context, $controller ) {
		$additional_author_ids      = (array) $course->get_meta( '_additional_authors', false );
		$additional_author_ids      = array_unique( array_values( $additional_author_ids ) );
		$additional_authors         = $this->filter_instructors( $additional_author_ids );
		$data['additional_authors'] = array_map(
			function( $author ) {
				return $this->get_author_data( $author );
			},
			$additional_authors
		);

		return $data;
	}

	/**
	 * Return additional author data.
	 *
	 * @since 2.3.2
	 *
	 * @param \Masteriyo\Models\User $author User/Author object.
	 * @return array
	 */
	protected function get_author_data( $author ) {
		return array(
			'id'           => $author->get_id(),
			'display_name' => $author->get_display_name(),
			'avatar_url'   => $author->get_avatar_url(),
		);
	}

	/**
	 * Filter instructors from author IDs.
	 *
	 * @since 2.3.2
	 *
	 * @param array $author_ids Author IDs.
	 *
	 * @return \Masteriyo\Models\User[]
	 */
	protected function filter_instructors( $author_ids ) {
		$authors = array_map(
			function( $author_id ) {
				return masteriyo_get_user( $author_id );
			},
			$author_ids
		);

		$authors = array_filter(
			$authors,
			function( $author ) {
				return ( is_wp_error( $author ) || is_null( $author ) ) ? null : $author;
			}
		);

		$authors = array_filter(
			$authors,
			function( $author ) {
				if ( in_array( 'masteriyo_instructor', $author->get_roles(), true ) ) {
					return $author;
				} elseif ( in_array( 'masteriyo_manager', $author->get_roles(), true ) ) {
					return $author;
				} elseif ( in_array( 'administrator', $author->get_roles(), true ) ) {
					return $author;
				} else {
					return null;
				}
			}
		);

		return array_values( $authors );
	}

	/**
	 * Save additional authors/instructors.
	 *
	 * @since 2.3.2
	 *
	 * @param integer $id The course ID.
	 * @param \Masteriyo\Models\Course $object The course object.
	 */
	public function save_additional_authors( $course_id, $course ) {
		$user_id = masteriyo_get_current_user_id();

		if ( ! $user_id ) {
			return;
		}

		$additional_authors = (array) $course->get_meta( '_additional_authors', false );
		$additional_authors = array_map( 'absint', $additional_authors );

		if ( in_array( $user_id, $additional_authors, true ) ) {
			return;
		}

		$request = masteriyo_current_http_request();

		if ( null === $request || ! isset( $request['additional_authors'] ) || ! is_array( $request['additional_authors'] ) ) {
			return;
		}

		$additional_author_ids = wp_list_pluck( $request['additional_authors'], 'id' );
		$course->delete_meta_data( '_additional_authors' );

		if ( $additional_author_ids ) {
			$additional_authors = $this->filter_instructors( $additional_author_ids );

			foreach ( $additional_authors  as $author ) {
				$course->add_meta_data( '_additional_authors', $author->get_id() );
			}
		}

		$course->save_meta_data();
	}

	/**
	 * Add multiple instructors fields to course schema.
	 *
	 * @since 2.3.2
	 *
	 * @param array $schema
	 * @return array
	 */
	public function add_multiple_instructors_schema( $schema ) {
		$schema = wp_parse_args(
			$schema,
			array(
				'additional_authors' => array(
					'description' => __( 'Additional authors', 'learning-management-system' ),
					'type'        => 'array',
					'context'     => array( 'view', 'edit' ),
					'items'       => array(
						'type'       => 'object',
						'properties' => array(
							'id'           => array(
								'description' => __( 'Author ID', 'learning-management-system' ),
								'type'        => 'integer',
								'context'     => array( 'view', 'edit' ),
								'readonly'    => true,
							),
							'display_name' => array(
								'description' => __( 'Author display name', 'learning-management-system' ),
								'type'        => 'string',
								'context'     => array( 'view', 'edit' ),
							),
							'avatar_url'   => array(
								'description' => __( 'Author avatar URL', 'learning-management-system' ),
								'type'        => 'string',
								'context'     => array( 'view', 'edit' ),
							),
						),
					),
				),
			)
		);

		return $schema;
	}

	/**
	 * Display multiple instructors html.
	 *
	 * @since 1.10.0 [Free]
	 *
	 * @param \Masteriyo\Models\Course $course Course object.
	 */
	public function display_single_course_layout1_co_authors( $course ) {
		$additional_authors = $course->get_meta( '_additional_authors', false );
		$additional_authors = array_values( array_map( 'absint', array_unique( $additional_authors ) ) );

		if ( empty( $additional_authors ) ) {
			return;
		}

		$html = '<div class="masteriyo-single-body__aside--course-instructor"><h3 class="masteriyo-single-body__aside--heading">';

		$html .= __( 'Additional Instructors', 'learning-management-system' );

		$html .= '</h3><ul class="masteriyo-single-body__aside--course-instructor-items">';

		foreach ( $additional_authors as $author ) {
			$author = masteriyo_get_user( $author );

			if ( is_wp_error( $author ) || is_null( $author ) ) {
				continue;
			}

			$html .= join(
				'',
				array(
					'<li class="masteriyo-single--author">',
					sprintf(
						'<a href="%s">',
						$author->get_course_archive_url()
					),
					sprintf(
						'<img src="%s" alt="%s" title="%s"/>',
						$author->get_avatar_url(),
						$author->get_display_name(),
						$author->get_display_name()
					),
					sprintf(
						'<a href="%s" class="masteriyo-single--author-name">%s</a>',
						$author->get_course_archive_url(),
						$author->get_display_name()
					),
					'</a>',
					'</li>',
				)
			);
		}

		$html .= '</div>';

		if ( ! empty( $html ) ) {
			echo wp_kses_post( $html );
		}
	}
}
