<?php
/**
 * Course bundle class controller.
 *
 * @since 2.12.0
 * @package Masteriyo\RestApi
 * @subpackage Controllers
 */

namespace Masteriyo\Addons\CourseBundle\Controllers;

defined( 'ABSPATH' ) || exit;

use Masteriyo\PostType\PostType;
use Masteriyo\RestApi\Controllers\Version1\CoursesController;
use WP_Error;

class CourseBundlesController extends CoursesController {

	/**
	 * Endpoint namespace.
	 *
	 * @var string
	 */
	protected $namespace = 'masteriyo/v1';

	/**
	 * Route base.
	 *
	 * @var string
	 */
	protected $rest_base = 'course-bundles';

	/** Post type.
	 *
	 * @var string
	 */
	protected $post_type = PostType::COURSE_BUNDLE;

	/** Object type.
	 *
	 * @var string
	 */
	protected $object_type = 'course_bundle';

	/**
	 * If object is hierarchical.
	 *
	 * @var bool
	 */
	protected $hierarchical = true;

	/**
	 * Get object.
	 *
	 * @since 2.12.0
	 *
	 * @param  Model|WP_Post $object Model or WP_Post object.
	 * @return object Model object or WP_Error object.
	 */
	protected function get_object( $object ) {
		try {
			if ( is_int( $object ) ) {
				$id = $object;
			} else {
				$id = is_a( $object, '\WP_Post' ) ? $object->ID : $object->get_id();
			}

			$course = masteriyo( 'course-bundle' );
			$course->set_id( $id );
			$course_repo = masteriyo( 'course-bundle.store' );
			$course_repo->read( $course );
		} catch ( \Exception $e ) {
			return false;
		}

		return $course;
	}

	/**
	 * Process objects collection.
	 *
	 * @since 2.12.0
	 *
	 * @param array $objects Courses data.
	 * @param array $query_args Query arguments.
	 * @param array $query_results Courses query result data.
	 *
	 * @return array
	 */
	protected function process_objects_collection( $objects, $query_args, $query_results ) {
		return array(
			'data' => $objects,
			'meta' => array(
				'total'                => $query_results['total'],
				'pages'                => $query_results['pages'],
				'current_page'         => $query_args['paged'],
				'per_page'             => $query_args['posts_per_page'],
				'course_bundles_count' => $this->get_course_bundles_count(),
			),
		);
	}

	/**
	 * Get course bundles by status.
	 *
	 * @since 2.12.0
	 *
	 * @return Array
	 */
	protected function get_course_bundles_count() {
		return parent::get_courses_count();
	}

	/**
	 * Get course data.
	 *
	 * @since 2.12.0
	 *
	 * @param \Masteriyo\Addons\CourseBundle\Models\CourseBundle $course_bundle Course instance.
	 * @param string $context Request context.
	 *                        Options: 'view' and 'edit'.
	 *
	 * @return array
	 */
	protected function get_course_bundle_data( $course_bundle, $context = 'view' ) {
		$author = masteriyo_get_user( $course_bundle->get_author_id( $context ) );

		if ( is_wp_error( $author ) || is_null( $author ) ) {
			$author = null;
		} else {
			$author = array(
				'id'           => $author->get_id(),
				'display_name' => $author->get_display_name(),
				'avatar_url'   => $author->profile_image_url(),
			);
		}

		/**
		 * Filters short description of course.
		 *
		 * @since 2.12.0
		 *
		 * @param string $short_description Short description of course.
		 */
		$short_description = 'view' === $context ? apply_filters( 'masteriyo_short_description', $course_bundle->get_short_description() ) : $course_bundle->get_short_description();

		$courses = array_values(
			array_map(
				function( $courses ) {
					return parent::get_course_data( $courses );
				},
				$course_bundle->get_bundled_courses()
			)
		);

		$instructors = array_values(
			array_unique(
				array_map(
					function( $course ) {
						return $course['author'];
					},
					$courses
				),
				SORT_REGULAR
			)
		);

		$data = array(
			'id'                   => $course_bundle->get_id(),
			'name'                 => wp_specialchars_decode( $course_bundle->get_name( $context ) ),
			'slug'                 => $course_bundle->get_slug( $context ),
			'permalink'            => $course_bundle->get_permalink(),
			'preview_permalink'    => $course_bundle->get_preview_link(),
			'status'               => $course_bundle->get_status( $context ),
			'description'          => 'view' === $context ? wpautop( do_shortcode( $course_bundle->get_description() ) ) : $course_bundle->get_description( $context ),
			'short_description'    => $short_description,
			'reviews_allowed'      => $course_bundle->get_reviews_allowed( $context ),
			'parent_id'            => $course_bundle->get_parent_id( $context ),
			'menu_order'           => $course_bundle->get_menu_order( $context ),
			'author'               => $author,
			'date_created'         => masteriyo_rest_prepare_date_response( $course_bundle->get_date_created( $context ) ),
			'date_modified'        => masteriyo_rest_prepare_date_response( $course_bundle->get_date_modified( $context ) ),
			'price'                => $course_bundle->get_price( $context ),
			'formatted_price'      => $course_bundle->get_rest_formatted_price( $context ),
			'regular_price'        => $course_bundle->get_regular_price( $context ),
			'sale_price'           => $course_bundle->get_sale_price( $context ),
			'price_type'           => $course_bundle->get_price_type( $context ),
			'featured_image'       => $course_bundle->get_featured_image( $context ),
			'access_mode'          => $course_bundle->get_access_mode( $context ),
			'billing_cycle'        => $course_bundle->get_billing_cycle( $context ),
			'billing_expire_after' => $course_bundle->get_billing_expire_after( $context ),
			'billing_interval'     => $course_bundle->get_billing_interval( $context ),
			'billing_period'       => $course_bundle->get_billing_period( $context ),
			'highlights'           => $course_bundle->get_highlights( $context ),
			'edit_post_link'       => $course_bundle->get_edit_post_link(),
			'courses'              => $courses,
			'instructors'          => $instructors,

			// Pro.
			'featured_video_source'=> $course_bundle->get_featured_video_source( $context ),
			'featured_video_url'   => $course_bundle->get_featured_video_url( $context ),
		);

		/**
		 * Filter course bundle rest response data.
		 *
		 * @since 2.12.0
		 *
		 * @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.
		 */
		return apply_filters( "masteriyo_rest_response_{$this->object_type}_data", $data, $course_bundle, $context, $this );
	}

	/**
	 * Prepare objects query.
	 *
	 * @param \WP_REST_Request $request Full details about the request.
	 *
	 * @since  2.12.0
	 * @return array
	 */
	protected function prepare_objects_query( $request ) {
		$args = parent::prepare_objects_query( $request );

		// Set post_status.
		$args['post_status'] = $request['status'];

		if ( ! masteriyo_is_current_user_admin() ) {
			$args['author'] = get_current_user_id();
		}

		return $args;
	}

	/**
	 * Prepares the object for the REST response.-
	 *
	 * @since  2.12.0
	 *
	 * @param  Masteriyo\Database\Model $object  Model object.
	 * @param  WP_REST_Request $request Request object.
	 *
	 * @return WP_Error|WP_REST_Response Response object on success, or WP_Error object on failure.
	 */
	protected function prepare_object_for_response( $object, $request ) {
		$context = ! empty( $request['context'] ) ? $request['context'] : 'view';
		$data    = $this->get_course_bundle_data( $object, $context );

		$data     = $this->add_additional_fields_to_object( $data, $request );
		$data     = $this->filter_response_by_context( $data, $context );
		$response = rest_ensure_response( $data );
		$response->add_links( $this->prepare_links( $object, $request ) );

		/**
		 * Filter the data for a response.
		 *
		 * @since 2.12.0
		 *
		 * The dynamic portion of the hook name, $this->object_type,
		 * refers to object type being prepared for the response.
		 *
		 * @param WP_REST_Response $response The response object.
		 * @param Masteriyo\Database\Model $object   Object data.
		 * @param WP_REST_Request  $request  Request object.
		 */
		return apply_filters( "masteriyo_rest_prepare_{$this->object_type}_object", $response, $object, $request );
	}

	/**
	 * Prepare a single course for create or update.
	 *
	 * @param WP_REST_Request $request Request object.
	 * @param bool            $creating If is creating a new object.
	 *
	 * @return WP_Error|Masteriyo\Models\Course
	 */
	protected function prepare_object_for_database( $request, $creating = false ) {
		$id            = isset( $request['id'] ) ? absint( $request['id'] ) : 0;
		/** @var Masteriyo\Addons\CourseBundle\Models\CourseBundle $course_bundle */
		$course_bundle = masteriyo( 'course-bundle' );

		if ( 0 !== $id ) {
			$course_bundle->set_id( $id );
		/** @var Masteriyo\Addons\CourseBundle\Repository\CourseBundleRepository $course_bundle_repo */
			$course_bundle_repo = masteriyo( \Masteriyo\Addons\CourseBundle\Repository\CourseBundleRepository::class );
			$course_bundle_repo->read( $course_bundle );
		}

		// Post title.
		if ( isset( $request['name'] ) ) {
			$course_bundle->set_name( sanitize_text_field( $request['name'] ) );
		}

		// Post content.
		if ( isset( $request['description'] ) ) {
			$course_bundle->set_description( wp_slash( $request['description'] ) );
		}

		// Post excerpt.
		if ( isset( $request['short_description'] ) ) {
			$course_bundle->set_short_description( wp_kses_post( $request['short_description'] ) );
		}

		// Post status.
		if ( isset( $request['status'] ) ) {
			$course_bundle->set_status( get_post_status_object( $request['status'] ) ? $request['status'] : 'draft' );
		}

		// Post slug.
		if ( isset( $request['slug'] ) ) {
			$course_bundle->set_slug( sanitize_title( $request['slug'] ) );
		}

		// Author/Instructor.
		if ( isset( $request['author_id'] ) ) {
			$course_bundle->set_author_id( $request['author_id'] );
		}

		// Menu order.
		if ( isset( $request['menu_order'] ) ) {
			$course_bundle->set_menu_order( $request['menu_order'] );
		}

		// Regular Price.
		if ( isset( $request['regular_price'] ) ) {
			$course_bundle->set_regular_price( $request['regular_price'] );
		}

		// Sale Price.
		if ( isset( $request['sale_price'] ) ) {
			$course_bundle->set_sale_price( $request['sale_price'] );
		}

		// Course featured image.
		if ( isset( $request['featured_image'] ) ) {
			$course_bundle->set_featured_image( $request['featured_image'] );
		}

		// Featured video source.
		if ( isset( $request['featured_video_source'] ) ) {
			$course_bundle->set_featured_video_source( $request['featured_video_source'] );
		}

		// Featured video source url.
		if ( isset( $request['featured_video_url'] ) ) {
			$course_bundle->set_featured_video_url( $request['featured_video_url'] );
		}

		// Course access mode.
		if ( isset( $request['access_mode'] ) ) {
			$course_bundle->set_access_mode( $request['access_mode'] );
		}

		// Course billing cycle.
		if ( isset( $request['billing_period'] ) ) {
			$course_bundle->set_billing_period( $request['billing_period'] );
		}

		// Course billing interval.
		if ( isset( $request['billing_interval'] ) ) {
			$course_bundle->set_billing_interval( $request['billing_interval'] );
		}

		// Course billing expire after.
		if ( isset( $request['billing_expire_after'] ) ) {
			$course_bundle->set_billing_expire_after( $request['billing_expire_after'] );
		}

		// Course highlights.
		if ( isset( $request['highlights'] ) ) {
			$course_bundle->set_highlights( $request['highlights'] );
		}

		if ( ! isset( $request['courses'] ) || empty( $request['courses'] ) ) {
			return new \WP_Error(
				"masteriyo_rest_{$this->post_type}_cannot_create_or_update",
				__(
					'Sorry, you have to select at least one course.',
					'learning-management-system'
				),
				array( 'status' => 400 )
			);
		}

		if ( isset( $request['courses'] ) ) {
			$courses = $request['courses'];

			$courses = array_filter(
				array_map(
					function( $course ) {
						if ( is_array( $course ) && isset( $course['id'] ) ) {
							return absint( $course['id'] );
						}

						return $course;
					},
					$courses
				)
			);

			$courses = array_unique( $courses );

			$course_bundle->set_courses( $courses );
		}

		// Allow set meta_data.
		if ( isset( $request['meta_data'] ) && is_array( $request['meta_data'] ) ) {
			foreach ( $request['meta_data'] as $meta ) {
				$course_bundle->update_meta_data( $meta['key'], $meta['value'], isset( $meta['id'] ) ? $meta['id'] : '' );
			}
		}

		$changes = $course_bundle->get_changes();

		if ( $id && ! empty( $changes ) && key_exists( 'courses', $changes ) ) {
			$total_sold_count = masteriyo_get_total_bundle_sold_count( $id );
			if ( $total_sold_count ) {
				return new \WP_Error(
					"masteriyo_rest_{$this->post_type}_cannot_update",
					__(
						'Sorry, you cannot add or remove course(s) from a bundle with enrolled students, as it may disrupt their learning experience.',
						'learning-management-system'
					),
					array( 'status' => 400 )
				);
			}
		}

		/**
		 * Filters an object before it is inserted via the REST API.
		 *
		 * The dynamic portion of the hook name, `$this->object_type`,
		 * refers to the object type slug.
		 *
		 * @since 2.12.0
		 *
		 * @param \Masteriyo\Addons\CourseBundle\Models\CourseBundle $course  Course object.
		 * @param \WP_REST_Request $request  Request object.
		 * @param bool            $creating If is creating a new object.
		 */
		return apply_filters( "masteriyo_rest_pre_insert_{$this->object_type}_object", $course_bundle, $request, $creating );
	}

	/**
	 * Checks if a given request has access to get a specific item.
	 *
	 * @since 2.12.0
	 *
	 * @param WP_REST_Request $request Full details about the request.
	 * @return boolean|WP_Error True if the request has read access for the item, WP_Error object otherwise.
	 */
	public function get_item_permissions_check( $request ) {
		if ( is_null( $this->permission ) ) {
			return new \WP_Error(
				'masteriyo_null_permission',
				__( 'Sorry, the permission object for this resource is null.', 'learning-management-system' )
			);
		}

		$id            = absint( $request['id'] );
		$course_bundle = masteriyo_get_course_bundle( $id );

		if ( is_null( $course_bundle ) ) {
			return new \WP_Error(
				"masteriyo_rest_{$this->post_type}_invalid_id",
				__( 'Invalid course bundle ID', 'learning-management-system' ),
				array(
					'status' => 404,
				)
			);
		}

		if ( masteriyo_is_current_user_admin() || masteriyo_is_current_user_manager() ) {
			return true;
		}

		if ( ! $this->permission->rest_check_post_permissions( $this->post_type, 'read', $id ) ) {
			return new \WP_Error(
				'masteriyo_rest_cannot_read',
				__( 'Sorry, you are not allowed to read resources.', 'learning-management-system' ),
				array(
					'status' => rest_authorization_required_code(),
				)
			);
		}

		return true;
	}

	/**
	 * Check if a given request has access to read items.
	 *
	 * @since 2.12.0
	 *
	 * @param  WP_REST_Request $request Full details about the request.
	 * @return WP_Error|boolean
	 */
	public function get_items_permissions_check( $request ) {
		if ( is_null( $this->permission ) ) {
			return new WP_Error(
				'masteriyo_null_permission',
				__( 'Sorry, the permission object for this resource is null.', 'learning-management-system' )
			);
		}

		if ( masteriyo_is_current_user_admin() || masteriyo_is_current_user_manager() ) {
			return true;
		}

		if ( ! $this->permission->rest_check_post_permissions( $this->post_type, 'read' ) ) {
			return new \WP_Error(
				'masteriyo_rest_cannot_read',
				__( 'Sorry, you are not allowed to read resources.', 'learning-management-system' ),
				array(
					'status' => rest_authorization_required_code(),
				)
			);
		}

		return true;
	}

	/**
	 * Check if a given request has access to create an item.
	 *
	 * @since 2.12.0
	 *
	 * @param  WP_REST_Request $request Full details about the request.
	 * @return WP_Error|boolean
	 */
	public function create_item_permissions_check( $request ) {
		if ( is_null( $this->permission ) ) {
			return new WP_Error(
				'masteriyo_null_permission',
				__( 'Sorry, the permission object for this resource is null.', 'learning-management-system' )
			);
		}

		if ( masteriyo_is_current_user_admin() || masteriyo_is_current_user_manager() ) {
			return true;
		}

		if ( ! $this->permission->rest_check_post_permissions( $this->post_type, 'create' ) ) {
			return new WP_Error(
				'masteriyo_rest_cannot_create',
				__( 'Sorry, you are not allowed to create resources.', 'learning-management-system' ),
				array(
					'status' => rest_authorization_required_code(),
				)
			);
		}

		return true;
	}

	/**
	 * Check if a given request has access to update an item.
	 *
	 * @since 2.12.0
	 *
	 * @param  WP_REST_Request $request Full details about the request.
	 * @return WP_Error|boolean
	 */
	public function update_item_permissions_check( $request ) {
		if ( is_null( $this->permission ) ) {
			return new WP_Error(
				'masteriyo_null_permission',
				__( 'Sorry, the permission object for this resource is null.', 'learning-management-system' )
			);
		}

		$id            = absint( $request['id'] );
		$course_bundle = masteriyo_get_course_bundle( $id );

		if ( is_null( $course_bundle ) ) {
			return new \WP_Error(
				"masteriyo_rest_{$this->post_type}_invalid_id",
				__( 'Invalid course bundle ID', 'learning-management-system' ),
				array(
					'status' => 404,
				)
			);
		}

		if ( masteriyo_is_current_user_admin() || masteriyo_is_current_user_manager() ) {
			return true;
		}

		if ( ! $this->permission->rest_check_post_permissions( $this->post_type, 'update', $id ) ) {
			return new WP_Error(
				'masteriyo_rest_cannot_update',
				__( 'Sorry, you are not allowed to update resources.', 'learning-management-system' ),
				array(
					'status' => rest_authorization_required_code(),
				)
			);
		}

		return true;
	}

	/**
	 * Check if a given request has access to delete an item.
	 *
	 * @since 2.12.0
	 *
	 * @param  WP_REST_Request $request Full details about the request.
	 * @return WP_Error|boolean
	 */
	public function delete_item_permissions_check( $request ) {
		if ( is_null( $this->permission ) ) {
			return new WP_Error(
				'masteriyo_null_permission',
				__( 'Sorry, the permission object for this resource is null.', 'learning-management-system' )
			);
		}

		$id            = absint( $request['id'] );
		$course_bundle = masteriyo_get_course_bundle( $id );

		if ( is_null( $course_bundle ) ) {
			return new \WP_Error(
				"masteriyo_rest_{$this->post_type}_invalid_id",
				__( 'Invalid course bundle ID', 'learning-management-system' ),
				array(
					'status' => 404,
				)
			);
		}

		if ( masteriyo_is_current_user_admin() || masteriyo_is_current_user_manager() ) {
			return true;
		}

		if ( ! $this->permission->rest_check_post_permissions( $this->post_type, 'delete', $id ) ) {
			return new WP_Error(
				'masteriyo_rest_cannot_delete',
				__( 'Sorry, you are not allowed to delete resources.', 'learning-management-system' ),
				array(
					'status' => rest_authorization_required_code(),
				)
			);
		}

		return true;
	}
}
