<?php
/**
 * License controller class.
 *
 * @since 2.15.0
 */

namespace Masteriyo\Pro\Controllers;

use Masteriyo\Helper\Permission;
use Masteriyo\RestApi\Controllers\Version1\RestController;
use WP_Error;

/**
 * Class ActivityLogController
 *
 * @since 2.15.0
 */
class ActivityLogController extends RestController {
	/**
	 * @var string Endpoint namespace
	 *
	 * @since 2.15.0
	 */
	protected $namespace = 'masteriyo/pro/v1';

	/**
	 * @var string Route base
	 *
	 * @since 2.15.0
	 */
	protected $rest_base = 'activity-logs';

	/**
	 * @var Permission Permission class instance
	 *
	 * @since 2.15.0
	 */
	protected $permission = null;

	/**
	 * StudentReportsController constructor.
	 *
	 * @param Permission $permission
	 */
	public function __construct( Permission $permission ) {
		$this->permission = $permission;
	}

	/**
	 * Register the REST API routes for this controller.
	 *
	 * @since 2.15.0
	 *
	 * @return void
	 */
	public function register_routes() {
		register_rest_route(
			$this->namespace,
			'/' . $this->rest_base,
			array(
				array(
					'methods'             => \WP_REST_Server::CREATABLE,
					'callback'            => array( $this, 'add_or_update_activity_log' ),
					'permission_callback' => array( $this, 'create_activity_log_permission_check' ),
					'args'                => $this->get_endpoint_args_for_item_schema( \WP_REST_Server::CREATABLE ),
				),
			)
		);
	}

	/**
	 * Check permission for fetching a student report.
	 *
	 * @since 2.15.0
	 *
	 * @param \WP_REST_Request $request Full details about the request, including the ID of the student.
	 *
	 * @return \WP_Error|boolean
	 */
	public function create_activity_log_permission_check( \WP_REST_Request $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 ( ! is_user_logged_in() ) {
			return new WP_Error(
				'masteriyo_rest_not_logged_in',
				__( 'Sorry, you must be logged in to create resources.', 'learning-management-system' ),
				array(
					'status' => rest_authorization_required_code(),
				)
			);
		}

		$course_id = isset( $request['course_id'] ) ? absint( $request['course_id'] ) : 0;
		$course    = masteriyo_get_course( $course_id );

		if ( is_null( $course ) || ! masteriyo_is_user_enrolled_in_course( $course_id ) || ! $this->permission->rest_check_course_progress_permissions( '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;
	}

	/**
	 * Add or update activity log for a user.
	 *
	 * @since 2.15.0
	 *
	 * @param \WP_REST_Request $request Full details about the request.
	 *
	 * @return \WP_REST_Response|\WP_Error Response object on success, or WP_Error object on failure.
	 */
	public function add_or_update_activity_log( \WP_REST_Request $request ) {
		$user_id   = get_current_user_id();
		$course_id = isset( $request['course_id'] ) ? absint( $request['course_id'] ) : 0;

		if ( ! $user_id || ! $course_id ) {
			return new \WP_Error( 'masteriyo_rest_invalid_request', __( 'Invalid request.', 'learning-management-system' ), array( 'status' => 400 ) );
		}

		$items_log = isset( $request['items_log'] ) ? $request['items_log'] : array();

		if ( empty( $items_log ) ) {
			return new \WP_Error( 'masteriyo_rest_invalid_request', __( 'No logs found to add.', 'learning-management-system' ), array( 'status' => 400 ) );
		}

		if ( ! $user_id || ! $course_id ) {
			return new \WP_Error( 'masteriyo_rest_invalid_request', __( 'Invalid request.', 'learning-management-system' ), array( 'status' => 400 ) );
		}

		$allowed_item_types = array( 'lesson', 'quiz', 'assignment', 'zoom', 'google-meet' );

		$course_activity_id = $this->get_user_activity_id( $user_id, $course_id );

		if ( ! $course_activity_id ) {
			return new \WP_Error( 'masteriyo_rest_invalid_request', __( 'No course progress found.', 'learning-management-system' ), array( 'status' => 400 ) );
		}

		foreach ( $items_log as $item_log ) {
			$item_id   = isset( $item_log['item_id'] ) ? absint( $item_log['item_id'] ) : 0;
			$item_type = isset( $item_log['item_type'] ) ? sanitize_text_field( $item_log['item_type'] ) : 'course_progress';

			$item_log = masteriyo_array_except( $item_log, array( 'item_id', 'item_type' ) );

			if ( ! in_array( $item_type, $allowed_item_types, true ) || ( isset( $item_log['active'] ) && ! masteriyo_string_to_bool( $item_log['active'] ) ) ) {
				continue;
			}

			$item_activity_id = $this->get_user_activity_id( $user_id, $item_id, $item_type, $course_activity_id );

			if ( ! $item_activity_id ) {
				$item_activity_id = $this->create_user_activity( $user_id, $item_id, $item_type, $course_activity_id );
			}

			if ( ! $item_activity_id ) {
				continue;
			}

			$this->create_or_update_activity_meta( $item_activity_id, $item_log );
		}

		return rest_ensure_response(
			array(
				'message' => 'Added log successfully.',
				'status'  => 'success',
			)
		);
	}

	/**
	 * Create a user activity.
	 *
	 * @since 2.15.0
	 *
	 * @param int $user_id         User ID.
	 * @param int $item_id         Item ID.
	 * @param string $item_type    Item type. Default 'lesson'.
	 * @param int $parent_id       Parent ID. Default 0.
	 * @param string $status      Activity status. Default 'started'.
	 *
	 * @return int User activity ID on success, 0 on failure.
	 */
	private function create_user_activity( $user_id, $item_id, $item_type = 'lesson', $parent_id = 0, $status = 'started' ) {
		global $wpdb;

		$wpdb->insert(
			"{$wpdb->prefix}masteriyo_user_activities",
			array(
				'item_id'         => $item_id,
				'user_id'         => $user_id,
				'activity_type'   => $item_type,
				'parent_id'       => $parent_id,
				'activity_status' => $status,
				'created_at'      => current_time( 'mysql', true ),
			),
			array( '%d', '%d', '%s', '%d', '%s', '%s' )
		);

		$user_activity_id = $wpdb->insert_id;

		return $user_activity_id ? absint( $user_activity_id ) : 0;
	}

	/**
	 * Get user activity ID.
	 *
	 * @since 2.15.0
	 *
	 * @param int $user_id         User ID.
	 * @param int $item_id         Item ID.
	 * @param string $item_type    Item type. Default 'course_progress'.
	 * @param int $parent_id      Parent ID. Default 0.
	 *
	 * @return int User activity ID.
	 */
	private function get_user_activity_id( $user_id, $item_id, $item_type = 'course_progress', $parent_id = 0 ) {
		global $wpdb;

		$user_activity_id = $wpdb->get_var(
			$wpdb->prepare(
				"SELECT id FROM {$wpdb->prefix}masteriyo_user_activities
					WHERE item_id = %d
					AND user_id = %d
					AND activity_type = %s
					AND parent_id = %d",
				absint( $item_id ),
				absint( $user_id ),
				$item_type,
				absint( $parent_id )
			)
		);

		return $user_activity_id ? absint( $user_activity_id ) : 0;
	}

	/**
	 * Retrieves the meta ID for a given user activity ID and meta key.
	 *
	 * @since 2.15.0
	 *
	 * @param int    $user_activity_id The user activity ID.
	 * @param string $meta_key        The meta key. Default 'activity_log'.
	 *
	 * @return int The meta ID on success, 0 on failure.
	 */
	private function get_user_activity_meta_id( $user_activity_id, $meta_key = 'activity_log' ) {
		global $wpdb;

		$user_activity_meta_id = $wpdb->get_var(
			$wpdb->prepare(
				"SELECT meta_id FROM {$wpdb->prefix}masteriyo_user_activitymeta
					WHERE user_activity_id = %d
					AND meta_key = %s",
				absint( $user_activity_id ),
				$meta_key
			)
		);

		return $user_activity_meta_id ? absint( $user_activity_meta_id ) : 0;
	}

	/**
	 * Creates or updates activity meta.
	 *
	 * @since 2.15.0
	 *
	 * @param int $user_activity_id User activity ID.
	 * @param array $data_to_be_updated Data to be updated in the activity meta.
	 * @param string $meta_key The meta key.
	 */
	private function create_or_update_activity_meta( $user_activity_id, $data_to_be_updated, $meta_key = 'activity_log' ) {
		$data_to_be_updated    = $this->sanitize_data_to_be_updated( $data_to_be_updated );
		$user_activity_meta_id = $this->get_user_activity_meta_id( $user_activity_id, $meta_key );

		if ( $user_activity_meta_id ) {
			$this->update_activity_meta( $user_activity_meta_id, $data_to_be_updated );
		} else {
			$this->create_activity_meta( $user_activity_id, $data_to_be_updated, $meta_key );
		}
	}

	/**
	 * Creates activity meta.
	 *
	 * @since 2.15.0
	 *
	 * @param int $user_activity_id User activity ID.
	 * @param array $meta_value Data to be updated in the activity meta.
	 * @param string $meta_key The meta key.
	 */
	private function create_activity_meta( $user_activity_id, $meta_value, $meta_key = 'activity_log' ) {
		global $wpdb;

		$wpdb->insert(
			"{$wpdb->prefix}masteriyo_user_activitymeta",
			array(
				'user_activity_id' => $user_activity_id,
				'meta_key'         => $meta_key,
				'meta_value'       => maybe_serialize( $meta_value ),
			),
			array( '%d', '%s', '%s' )
		);
	}

	/**
	 * Updates activity meta.
	 *
	 * @since 2.15.0
	 *
	 * @param int $user_activity_meta_id User activity meta ID.
	 * @param array $meta_value Data to be updated in the activity meta.
	 */
	private function update_activity_meta( $user_activity_meta_id, $meta_value ) {
		global $wpdb;

		$db_meta_value = $this->get_user_activity_meta( $user_activity_meta_id );

		if ( $db_meta_value ) {
			$meta_value['time_spent']   = absint( $meta_value['time_spent'] ) + absint( $db_meta_value['time_spent'] );
			$meta_value['idle_time']    = absint( $meta_value['idle_time'] ) + absint( $db_meta_value['idle_time'] );
			$meta_value['access_count'] = absint( $meta_value['access_count'] ) + absint( $db_meta_value['access_count'] );

			if ( ! $meta_value['last_entry_time'] ) {
				$meta_value['last_entry_time'] = $db_meta_value['last_entry_time'];
			}
		}

		$wpdb->update(
			"{$wpdb->prefix}masteriyo_user_activitymeta",
			array( 'meta_value' => maybe_serialize( $meta_value ) ),
			array( 'meta_id' => $user_activity_meta_id ),
			array( '%s' ),
			array( '%d' )
		);
	}

	/**
	 * Retrieves the meta value for a given user activity meta ID.
	 *
	 * @since 2.15.0
	 *
	 * @param int $meta_id The user activity meta ID.
	 *
	 * @return mixed|null The meta value on success, null on failure.
	 */
	private function get_user_activity_meta( $meta_id ) {
		global $wpdb;

		$user_activity_meta = $wpdb->get_var(
			$wpdb->prepare(
				"SELECT meta_value FROM {$wpdb->prefix}masteriyo_user_activitymeta WHERE meta_id = %d",
				$meta_id
			)
		);

		return $user_activity_meta ? maybe_unserialize( $user_activity_meta ) : null;
	}

	/**
	 * Sanitizes data to be updated in the activity meta.
	 *
	 * @since 2.15.0
	 *
	 * @param array $data_to_be_updated Data to be updated.
	 *
	 * @return array Sanitized data.
	 */
	private function sanitize_data_to_be_updated( $data_to_be_updated ) {
		return array_map( 'sanitize_text_field', $data_to_be_updated );
	}
}
