<?php
/**
 * Handle Ajax Request
 *
 * @package Tutor
 * @author Themeum <support@themeum.com>
 * @link https://themeum.com
 * @since 1.0.0
 */

namespace TUTOR;

defined( 'ABSPATH' ) || exit;

use Tutor\GDPR\Controllers\LegalConsent;
use Tutor\Helpers\HttpHelper;
use Tutor\Models\LessonModel;
use Tutor\Traits\JsonResponse;

/**
 * Ajax Class
 *
 * @since 1.0.0
 */
class Ajax {
	use JsonResponse;

	const LOGIN_ERRORS_TRANSIENT_KEY = 'tutor_login_errors';

	/**
	 * Constructor
	 *
	 * @since 1.0.0
	 * @since 2.6.2 added allow_hooks param.
	 *
	 * @param bool $allow_hooks default value true.
	 *
	 * @return void
	 */
	public function __construct( $allow_hooks = true ) {
		if ( $allow_hooks ) {
			add_action( 'wp_ajax_sync_video_playback', array( $this, 'sync_video_playback' ) );
			add_action( 'wp_ajax_tutor_place_rating', array( $this, 'tutor_place_rating' ) );
			add_action( 'wp_ajax_delete_tutor_review', array( $this, 'delete_tutor_review' ) );

			add_action( 'wp_ajax_tutor_course_add_to_wishlist', array( $this, 'tutor_course_add_to_wishlist' ) );
			add_action( 'wp_ajax_nopriv_tutor_course_add_to_wishlist', array( $this, 'tutor_course_add_to_wishlist' ) );

			/**
			 * Ajax login
			 *
			 * @since  v.1.6.3
			 */
			add_action( 'tutor_action_tutor_user_login', array( $this, 'process_tutor_login' ) );

			/**
			 * Announcement
			 *
			 * @since  v.1.7.9
			 */
			add_action( 'wp_ajax_tutor_announcement_create', array( $this, 'create_or_update_announcement' ) );
			add_action( 'wp_ajax_tutor_announcement_delete', array( $this, 'delete_announcement' ) );

			add_action( 'wp_ajax_tutor_youtube_video_duration', array( $this, 'ajax_youtube_video_duration' ) );
		}
	}



	/**
	 * Update video information and data when necessary
	 *
	 * @since 1.0.0
	 *
	 * @return void
	 */
	public function sync_video_playback() {
		tutor_utils()->checking_nonce();

		$user_id      = get_current_user_id();
		$post_id      = Input::post( 'post_id', 0, Input::TYPE_INT );
		$duration     = Input::post( 'duration' );
		$current_time = Input::post( 'currentTime' );

		if ( ! tutor_utils()->has_enrolled_content_access( 'lesson', $post_id ) ) {
			wp_send_json_error( array( 'message' => __( 'Access Denied', 'tutor' ) ) );
			exit;
		}

		/**
		 * Update posts attached video
		 */
		$video = tutor_utils()->get_video( $post_id );

		if ( $duration ) {
			$video['duration_sec'] = $duration; // Duration in sec.
			$video['playtime']     = tutor_utils()->playtime_string( $duration );
			$video['runtime']      = tutor_utils()->playtime_array( $duration );
		}
		tutor_utils()->update_video( $post_id, $video );

		/**
		 * Sync Lesson Reading Info by Users
		 */

		$best_watch_time = tutor_utils()->get_lesson_reading_info( $post_id, $user_id, 'video_best_watched_time' );
		if ( $best_watch_time < $current_time ) {
			LessonModel::update_lesson_reading_info( $post_id, $user_id, 'video_best_watched_time', $current_time );
		}

		if ( Input::post( 'is_ended', false, Input::TYPE_BOOL ) ) {
			LessonModel::mark_lesson_complete( $post_id );
			LessonModel::update_lesson_reading_info( $post_id, $user_id, 'video_best_watched_time', 0 );
		}
		exit();
	}

	/**
	 * Place rating
	 *
	 * @since 1.0.0
	 * @return void
	 */
	public function tutor_place_rating() {
		tutor_utils()->checking_nonce();

		$user_id   = get_current_user_id();
		$course_id = Input::post( 'course_id' );
		$rating    = Input::post( 'tutor_rating_gen_input', 0, Input::TYPE_INT );
		$review    = Input::post( 'review', '', Input::TYPE_TEXTAREA );

		$rating <= 0 ? $rating = 1 : 0;
		$rating > 5 ? $rating  = 5 : 0;

		$this->add_or_update_review( $user_id, $course_id, $rating, $review );
	}

	/**
	 * Add/Update rating
	 *
	 * @param int    $user_id the user id.
	 * @param int    $course_id the course id.
	 * @param int    $rating rating star number.
	 * @param string $review review description.
	 * @param int    $review_id review id needed for api update.
	 *
	 * @return void|string
	 */
	public function add_or_update_review( $user_id, $course_id, $rating, $review, $review_id = 0 ) {
		global $wpdb;

		$moderation = tutor_utils()->get_option( 'enable_course_review_moderation', false, true, true );
		$user       = get_userdata( $user_id );
		$date    = date( 'Y-m-d H:i:s', tutor_time() ); //phpcs:ignore

		if ( ! tutor_is_rest() && ! tutor_utils()->has_enrolled_content_access( 'course', $course_id ) ) {
			wp_send_json_error( array( 'message' => __( 'Access Denied', 'tutor' ) ) );
			exit;
		}

		do_action( 'tutor_before_rating_placed' );

		$is_edit = 0 === $review_id ? false : true;

		if ( ! tutor_is_rest() ) {
			$previous_rating_id = $wpdb->get_var(
				$wpdb->prepare(
					"SELECT comment_ID
				from {$wpdb->comments}
				WHERE comment_post_ID = %d AND
					user_id = %d AND
					comment_type = 'tutor_course_rating'
				LIMIT 1;",
					$course_id,
					$user_id
				)
			);

			if ( ! empty( $previous_rating_id ) ) {
				$review_id = $previous_rating_id;
				$is_edit   = true;
			}
		}

		if ( $is_edit ) {
			$wpdb->update(
				$wpdb->comments,
				array(
					'comment_content'  => $review,
					'comment_approved' => $moderation ? 'hold' : 'approved',
					'comment_date'     => $date,
					'comment_date_gmt' => get_gmt_from_date( $date ),
				),
				array( 'comment_ID' => $review_id )
			);

			$rating_info = $wpdb->get_row(
				$wpdb->prepare(
					"SELECT * FROM {$wpdb->commentmeta} 
				WHERE comment_id = %d 
					AND meta_key = 'tutor_rating'; ",
					$review_id
				)
			);

			if ( $rating_info ) {
				$wpdb->update(
					$wpdb->commentmeta,
					array( 'meta_value' => $rating ),
					array(
						'comment_id' => $review_id,
						'meta_key'   => 'tutor_rating',
					)
				);
			} else {
				$wpdb->insert(
					$wpdb->commentmeta,
					array(
						'comment_id' => $review_id,
						'meta_key'   => 'tutor_rating',
						'meta_value' => $rating,
					)
				);
			}
		} else {
			$data = array(
				'comment_post_ID'  => esc_sql( $course_id ),
				'comment_approved' => $moderation ? 'hold' : 'approved',
				'comment_type'     => 'tutor_course_rating',
				'comment_date'     => $date,
				'comment_date_gmt' => get_gmt_from_date( $date ),
				'user_id'          => $user_id,
				'comment_author'   => $user->user_login,
				'comment_agent'    => 'TutorLMSPlugin',
			);
			if ( $review ) {
				$data['comment_content'] = $review;
			}

			$wpdb->insert( $wpdb->comments, $data );
			$comment_id = (int) $wpdb->insert_id;
			$review_id  = $comment_id;

			if ( $comment_id ) {
				$wpdb->insert(
					$wpdb->commentmeta,
					array(
						'comment_id' => $comment_id,
						'meta_key'   => 'tutor_rating',
						'meta_value' => $rating,
					)
				);

				do_action( 'tutor_after_rating_placed', $comment_id );
			}
		}

		if ( ! tutor_is_rest() ) {
			wp_send_json_success(
				array(
					'message'   => __( 'Rating placed successfully!', 'tutor' ),
					'review_id' => $review_id,
				)
			);
		} else {
			return $is_edit ? 'updated' : 'created';
		}
	}

	/**
	 * Delete a review
	 *
	 * @since 1.0.0
	 * @since 2.6.2 added params user_id.
	 * @param int $user_id the user id.
	 * @return void|bool
	 */
	public function delete_tutor_review( $user_id = 0 ) {
		if ( ! tutor_is_rest() ) {
			tutor_utils()->checking_nonce();
		}

		$review_id = Input::post( 'review_id' );

		if ( ! tutor_utils()->can_user_manage( 'review', $review_id, tutils()->get_user_id( $user_id ) ) ) {
			wp_send_json_error( array( 'message' => __( 'Permissioned Denied!', 'tutor' ) ) );
			exit;
		}

		global $wpdb;
		$wpdb->delete( $wpdb->commentmeta, array( 'comment_id' => $review_id ) );
		$wpdb->delete( $wpdb->comments, array( 'comment_ID' => $review_id ) );

		if ( tutor_is_rest() ) {
			return true;
		}

		wp_send_json_success();
	}

	/**
	 * Add course in wishlist
	 *
	 * @since 1.0.0
	 * @return void|string
	 */
	public function tutor_course_add_to_wishlist() {
		tutor_utils()->checking_nonce();

		$is_enabled_wishlist = tutor_utils()->get_option( 'enable_wishlist', true );
		if ( ! $is_enabled_wishlist ) {
			wp_send_json_error( array( 'message' => __( 'Wishlist option is disabled', 'tutor' ) ) );
		}

		// Redirect login since only logged in user can add courses to wishlist.
		if ( ! is_user_logged_in() ) {
			wp_send_json_error(
				array(
					'redirect_to' => wp_login_url( wp_get_referer() ),
				)
			);
		}

		$user_id   = get_current_user_id();
		$course_id = Input::post( 'course_id', 0, Input::TYPE_INT );

		$result = $this->add_or_delete_wishlist( $user_id, $course_id );

		if ( tutor_is_rest() ) {
			return $result;
		} elseif ( 'added' === $result ) {
			wp_send_json_success(
				array(
					'status'  => 'added',
					'message' => __( 'Course added to wish list', 'tutor' ),
				)
			);
		} else {
			wp_send_json_success(
				array(
					'status'  => 'removed',
					'message' => __( 'Course removed from wish list', 'tutor' ),
				)
			);
		}
	}

	/**
	 * Add or Delete wishlist by user_id and course_id
	 *
	 * @since 2.6.2
	 *
	 * @param int $user_id the user id.
	 * @param int $course_id the course_id to add to the wishlist.
	 *
	 * @return string
	 */
	public function add_or_delete_wishlist( $user_id, $course_id ) {
		global $wpdb;

		$if_added_to_list = tutor_utils()->is_wishlisted( $course_id, $user_id );

		$result = '';

		if ( $if_added_to_list ) {
			$wpdb->delete(
				$wpdb->usermeta,
				array(
					'user_id'    => $user_id,
					'meta_key'   => '_tutor_course_wishlist',
					'meta_value' => $course_id,
				)
			);

			$result = 'removed';
		} else {
			add_user_meta( $user_id, '_tutor_course_wishlist', $course_id );

			$result = 'added';
		}

		return $result;
	}

	/**
	 * Process tutor login
	 *
	 * @since 1.6.3
	 * @since 2.1.3 Ajax removed, validation errors stores in session.
	 *
	 * @return void
	 */
	public function process_tutor_login() {
		$validation_error = new \WP_Error();

		/**
		 * Separate nonce verification added to show nonce verification
		 * failed message in a proper way.
		 *
		 * @since 2.1.4
		 */
		if ( ! tutor_utils()->is_nonce_verified( 'post' ) ) {
			$validation_error->add( 401, __( 'Nonce verification failed', 'tutor' ) );
			\set_transient( self::LOGIN_ERRORS_TRANSIENT_KEY, $validation_error->get_error_messages(), MINUTE_IN_SECONDS );
			return;
		}

		/**
		 * No sanitization/wp_unslash needed for log & pwd since WordPress
		 * does itself
		 *
		 * @since 2.1.3
		 *
		 * @see https://developer.wordpress.org/reference/functions/wp_signon/
		 */
		//phpcs:disable WordPress.Security.NonceVerification.Missing
		$username    = tutor_utils()->array_get( 'log', $_POST ); //phpcs:ignore
		$password    = tutor_utils()->array_get( 'pwd', $_POST ); //phpcs:ignore
		$redirect_to = isset( $_POST['redirect_to'] ) ? esc_url_raw( wp_unslash( $_POST['redirect_to'] ) ) : '';
		$remember    = isset( $_POST['rememberme'] );
		//phpcs:enable WordPress.Security.NonceVerification.Missing

		try {
			$creds = array(
				'user_login'    => trim( $username ),
				'user_password' => $password,
				'remember'      => $remember,
			);

			$validation_error = apply_filters( 'tutor_process_login_errors', $validation_error, $creds['user_login'], $creds['user_password'] );
			if ( $validation_error->get_error_code() ) {
				$validation_error->add(
					$validation_error->get_error_code(),
					$validation_error->get_error_message()
				);
			}

			if ( empty( $creds['user_login'] ) ) {
				$validation_error->add(
					400,
					__( 'Username is required.', 'tutor' )
				);
			}

			$validate_consent = LegalConsent::validate_consent( LegalConsent::DISPLAY_ON_LOGIN, $_POST );
			if ( is_wp_error( $validate_consent ) ) {
				$validation_error->add(
					$validate_consent->get_error_code(),
					$validate_consent->get_error_message(),
				);
			}

			if ( $validation_error->has_errors() ) {
				\set_transient( self::LOGIN_ERRORS_TRANSIENT_KEY, $validation_error->get_error_messages() );
				return;
			}

			// On multi-site, ensure user exists on current site, if not add them before allowing login.
			if ( is_multisite() ) {
				$user_data = get_user_by( is_email( $creds['user_login'] ) ? 'email' : 'login', $creds['user_login'] );
				if ( $user_data && ! is_user_member_of_blog( $user_data->ID, get_current_blog_id() ) ) {
					add_user_to_blog( get_current_blog_id(), $user_data->ID, 'customer' );
				}
			}

			// Perform the login.
			$user = wp_signon( apply_filters( 'tutor_login_credentials', $creds ), is_ssl() );
			if ( is_wp_error( $user ) ) {
				// If no error exist then add WP login error, to prevent error duplication.
				if ( ! $validation_error->has_errors() ) {
					$validation_error->add( 400, $user->get_error_message() );
				}
			} else {
				// @since 4.0.0 $validate _consent param added.
				do_action( 'tutor_after_login_success', $user->ID, $validate_consent );

				// Since 1.9.8 do enroll if guest attempt to enroll.
				$course_enroll_attempt = Input::post( 'tutor_course_enroll_attempt' );
				if ( ! empty( $course_enroll_attempt ) && is_a( $user, 'WP_User' ) ) {
					do_action( 'tutor_do_enroll_after_login_if_attempt', $course_enroll_attempt, $user->ID );
				}
				wp_safe_redirect( $redirect_to );
				exit();
			}
		} catch ( \Exception $e ) {
			do_action( 'tutor_login_failed' );
			$validation_error->add( 400, $e->getMessage() );
		} finally {
			// Store errors in transient data.
			\set_transient( self::LOGIN_ERRORS_TRANSIENT_KEY, $validation_error->get_error_messages(), MINUTE_IN_SECONDS );
		}
	}

	/**
	 * Create/Update announcement
	 *
	 * @since  1.7.9
	 *
	 * @return void
	 */
	public function create_or_update_announcement() {
		tutor_utils()->checking_nonce();

		$error                = array();
		$course_id            = Input::post( 'tutor_announcement_course' );
		$announcement_title   = Input::post( 'tutor_announcement_title' );
		$announcement_summary = Input::post( 'tutor_announcement_summary', '', Input::TYPE_TEXTAREA );

		// Check if user can manage this announcement.
		if ( ! tutor_utils()->can_user_manage( 'course', $course_id ) ) {
			wp_send_json_error( array( 'message' => __( 'Access Denied', 'tutor' ) ) );
		}

		// Set data and sanitize it.
		$form_data = array(
			'post_type'    => 'tutor_announcements',
			'post_title'   => $announcement_title,
			'post_content' => $announcement_summary,
			'post_parent'  => $course_id,
			'post_status'  => 'publish',
		);

		if ( Input::has( 'announcement_id' ) ) {
			$form_data['ID'] = Input::post( 'announcement_id' );
		}

		if ( ! empty( $form_data['ID'] ) ) {
			if ( ! tutor_utils()->can_user_manage( 'announcement', $form_data['ID'] ) ) {
				wp_send_json_error( array( 'message' => tutor_utils()->error_message() ) );
			}
		}

		// Validation message set.
		if ( empty( $form_data['post_parent'] ) ) {
			$error['post_parent'] = __( 'Course name required', 'tutor' );

		}

		if ( empty( $form_data['post_title'] ) ) {
			$error['post_title'] = __( 'Announcement title required', 'tutor' );
		}

		if ( empty( $form_data['post_content'] ) ) {
			$error['post_content'] = __( 'Announcement summary required', 'tutor' );

		}

		if ( empty( $form_data['post_content'] ) ) {
			$error['post_content'] = __( 'Announcement summary required', 'tutor' );

		}

		// If validation fails.
		if ( count( $error ) > 0 ) {
			wp_send_json_error(
				array(
					'message' => __( 'All fields required!', 'tutor' ),
					'fields'  => $error,
				)
			);
		}

		// Insert or update post.
		$post_id = wp_insert_post( $form_data );
		if ( $post_id > 0 ) {
			$announcement = get_post( $post_id );
			$action_type  = Input::post( 'action_type' );

			do_action( 'tutor_announcements/after/save', $post_id, $announcement, $action_type );

			$resp_message = 'create' === $action_type ? __( 'Announcement created successfully', 'tutor' ) : __( 'Announcement updated successfully', 'tutor' );
			wp_send_json_success( array( 'message' => $resp_message ) );
		}

		wp_send_json_error( array( 'message' => __( 'Something Went Wrong!', 'tutor' ) ) );
	}

	/**
	 * Delete announcement
	 *
	 * @since  1.7.9
	 *
	 * @return void
	 */
	public function delete_announcement() {
		tutor_utils()->checking_nonce();

		$announcement_id = Input::post( 'announcement_id' );

		if ( ! tutor_utils()->can_user_manage( 'announcement', $announcement_id ) ) {
			wp_send_json_error( array( 'message' => __( 'Access Denied', 'tutor' ) ) );
		}

		$delete = wp_delete_post( $announcement_id );
		if ( $delete ) {
			wp_send_json_success( array( 'message' => __( 'Announcement deleted successfully', 'tutor' ) ) );
		}

		wp_send_json_error( array( 'message' => __( 'Announcement delete failed', 'tutor' ) ) );
	}

	/**
	 * Get youtube video duration.
	 *
	 * @since 3.0.0
	 *
	 * @return void
	 */
	public function ajax_youtube_video_duration() {
		tutor_utils()->check_nonce();

		$video_id = Input::post( 'video_id' );
		if ( empty( $video_id ) ) {
			$this->json_response( __( 'Video ID is required', 'tutor' ), null, HttpHelper::STATUS_BAD_REQUEST );
		}

		tutor_utils()->check_current_user_capability( 'edit_tutor_courses' );

		$api_key = tutor_utils()->get_option( 'lesson_video_duration_youtube_api_key', '' );
		$url     = "https://www.googleapis.com/youtube/v3/videos?id=$video_id&part=contentDetails&key=$api_key";

		$request = HttpHelper::get( $url );
		if ( HttpHelper::STATUS_OK === $request->get_status_code() ) {
			$response = $request->get_json();
			if ( isset( $response->items[0]->contentDetails->duration ) ) {
				$duration = $response->items[0]->contentDetails->duration;
				$this->json_response(
					__( 'Fetched duration successfully', 'tutor' ),
					array(
						'duration' => $duration,
					)
				);
			}
		}

		$this->json_response(
			__( 'Failed to fetch duration', 'tutor' ),
			null,
			HttpHelper::STATUS_BAD_REQUEST
		);
	}
}
