<?php
/**
 * LifterLMS Privacy Exporter
 *
 * @package LifterLMS/Privacy/Classes
 *
 * @since 3.18.0
 * @version 6.0.0
 */

defined( 'ABSPATH' ) || exit;

/**
 * LifterLMS Privacy Exporter class
 *
 * @since 3.18.0
 * @since 3.30.3 Fixed spelling error.
 * @since 3.37.9 Add export group descriptions.
 */
class LLMS_Privacy_Exporters extends LLMS_Privacy {

	/**
	 * Export student achievement data by email address
	 *
	 * @since 3.18.0
	 * @since 3.37.9 Added `$group_description` to the group exporter.
	 *
	 * @param    string $email_address  Email address of the user to retrieve data for.
	 * @param    int    $page           Process page number.
	 * @return   array
	 */
	public static function achievement_data( $email_address, $page ) {

		$data = array();

		$student = self::get_student_by_email( $email_address );
		if ( ! $student ) {
			return self::get_return( $data );
		}

		$achievements = self::get_student_achievements( $student );
		if ( $achievements ) {

			$group_label       = __( 'Achievements', 'lifterlms' );
			$group_description = __( 'Student achievement data.', 'lifterlms' );
			foreach ( $achievements as $achievement ) {

				$data[] = array(
					'group_id'          => 'lifterlms_achievements',
					'group_label'       => $group_label,
					'group_description' => $group_description,
					'item_id'           => sprintf( 'achievement-%d', $achievement->get( 'id' ) ),
					'data'              => self::get_achievement_data( $achievement ),
				);

			}
		}

		return self::get_return( $data );

	}

	/**
	 * Export student certificate data by email address
	 *
	 * @since 3.18.0
	 * @since 3.37.9 Added `$group_description` to the group exporter.
	 *
	 * @param    string $email_address Email address of the user to retrieve data for.
	 * @param    int    $page          Process page number.
	 * @return   array
	 */
	public static function certificate_data( $email_address, $page ) {

		$data = array();

		$student = self::get_student_by_email( $email_address );
		if ( ! $student ) {
			return self::get_return( $data );
		}

		$certs = self::get_student_certificates( $student );
		if ( $certs ) {

			$group_label       = __( 'Certificates', 'lifterlms' );
			$group_description = __( 'Student certificate data.', 'lifterlms' );
			foreach ( $certs as $cert ) {

				$data[] = array(
					'group_id'          => 'lifterlms_certificates',
					'group_label'       => $group_label,
					'group_description' => $group_description,
					'item_id'           => sprintf( 'certificate-%d', $cert->get( 'id' ) ),
					'data'              => self::get_certificate_data( $cert ),
				);

			}
		}

		return self::get_return( $data );

	}

	/**
	 * Get data for a certificate
	 *
	 * @since    3.18.0
	 *
	 * @param    obj $achievement  LLMS_User_Certificate.
	 * @return   array
	 */
	private static function get_achievement_data( $achievement ) {

		$data = array();

		$data[] = array(
			'name'  => __( 'Title', 'lifterlms' ),
			'value' => $achievement->get( 'title' ),
		);

		$data[] = array(
			'name'  => __( 'Description', 'lifterlms' ),
			'value' => $achievement->get( 'content' ),
		);

		$data[] = array(
			'name'  => __( 'Earned Date', 'lifterlms' ),
			'value' => $achievement->get_earned_date( 'Y-m-d H:i:s' ),
		);

		$data[] = array(
			'name'  => __( 'Image', 'lifterlms' ),
			'value' => $achievement->get_image(),
		);

		return $data;

	}



	/**
	 * Get data for a certificate.
	 *
	 * @since 3.18.0
	 * @since 6.0.0 Replaced the use of the deprecated `certificate_title` meta key with the post's title property.
	 *
	 * @param LLMS_User_Certificate $cert Certificate object.
	 * @return array
	 */
	private static function get_certificate_data( $cert ) {

		$data = array();

		$title = $cert->get( 'title' );

		$filename = llms()->certificates()->get_export( $cert->get( 'id' ), true );
		if ( ! is_wp_error( $filename ) ) {
			$title = '<a href="certificates/' . basename( $filename ) . '">' . $title . '</a>';
		}

		$data[] = array(
			'name'  => __( 'Title', 'lifterlms' ),
			'value' => $title,
		);

		$data[] = array(
			'name'  => __( 'Earned Date', 'lifterlms' ),
			'value' => $cert->get_earned_date( 'Y-m-d H:i:s' ),
		);

		return $data;

	}

	/**
	 * Get an array of enrollment data for a course or membership
	 *
	 * @since 3.18.0
	 * @since 3.30.3 Fixed spelling errors.
	 *
	 * @param int $post_id WP Post ID of course or membership.
	 * @param obj $student LLMS_Student.
	 * @param obj $post_type_object WP post type object.
	 * @return array
	 */
	private static function get_enrollment_data( $post_id, $student, $post_type_object ) {

		$data = array();

		$data[] = array(
			// Translators: %s = post type singular name label (Course or Membership).
			'name'  => sprintf( __( '%s Title', 'lifterlms' ), $post_type_object->labels->singular_name ),
			'value' => get_the_title( $post_id ),
		);

		$data[] = array(
			'name'  => __( 'Enrollment Status', 'lifterlms' ),
			'value' => llms_get_enrollment_status_name( $student->get_enrollment_status( $post_id ) ),
		);

		$data[] = array(
			'name'  => __( 'Enrollment Date', 'lifterlms' ),
			'value' => $student->get_enrollment_date( $post_id, 'enrolled', 'Y-m-d H:i:s' ),
		);

		if ( 'course' === $post_type_object->name ) {

			$data[] = array(
				'name'  => __( 'Last Activity', 'lifterlms' ),
				'value' => $student->get_enrollment_date( $post_id, 'updated', 'Y-m-d H:i:s' ),
			);

			$progress = $student->get_progress( $post_id, 'course' );
			if ( is_numeric( $progress ) ) {
				$progress .= '%';
			}
			$data[] = array(
				'name'  => __( 'Progress', 'lifterlms' ),
				'value' => $progress,
			);

			$grade = $student->get_grade( $post_id );
			if ( is_numeric( $grade ) ) {
				$grade .= '%';
			}
			$data[] = array(
				'name'  => __( 'Grade', 'lifterlms' ),
				'value' => $grade,
			);

		}

		return apply_filters( 'llms_privacy_export_enrollment_data', $data, $post_id, $student, $post_type_object );

	}

	/**
	 * Retrieve export data for a single order
	 *
	 * @since 3.18.0
	 *
	 * @param  LLMS_Order $order Order object.
	 * @return array
	 */
	private static function get_order_data( $order ) {

		$data = array();

		$props = self::get_order_data_props( 'export' );

		foreach ( $props as $prop => $name ) {

			$value = apply_filters( 'llms_privacy_export_order_data_prop_value', $order->get( $prop ), $prop, $order );

			if ( $value ) {
				$data[] = array(
					'name'  => $name,
					'value' => $value,
				);
			}
		}

		$transactions = $order->get_transactions(
			array(
				'per_page' => 500,
			)
		);
		if ( $transactions['transactions'] ) {
			$txns = array();
			foreach ( $transactions['transactions'] as $txn ) {
				$txns[] = sprintf( '%1$s &mdash; %2$s (#%3$d)', $txn->get( 'date' ), $txn->get_price( 'amount' ), $txn->get( 'id' ) );
			}
			$data[] = array(
				'name'  => __( 'Transactions', 'lifterlms' ),
				'value' => implode( '<br>', $txns ),
			);
		}

		return apply_filters( 'llms_privacy_export_order_data', $data, $order );
	}

	/**
	 * Get export data for a single quiz attempt
	 *
	 * @since 3.18.0
	 *
	 * @param LLMS_Quiz_Attempt $attempt Quiz attempt object.
	 * @return array
	 */
	private static function get_quiz_attempt_data( $attempt ) {

		$data = array();

		$quiz = $attempt->get_quiz();
		if ( $quiz ) {
			$data[] = array(
				'name'  => __( 'Title', 'lifterlms' ),
				'value' => $quiz->get( 'title' ),
			);
		}

		$data[] = array(
			'name'  => __( 'Attempt ID', 'lifterlms' ),
			'value' => $attempt->get_key(),
		);

		$data[] = array(
			'name'  => __( 'Attempt Number', 'lifterlms' ),
			'value' => $attempt->get( 'attempt' ),
		);

		$data[] = array(
			'name'  => __( 'Status', 'lifterlms' ),
			'value' => $attempt->l10n( 'status' ),
		);

		$grade  = $attempt->get( 'grade' );
		$data[] = array(
			'name'  => __( 'Grade', 'lifterlms' ),
			'value' => is_numeric( $grade ) ? $grade . '%' : '&ndash;',
		);

		return $data;
	}

	/**
	 * Return export data to an exporter
	 *
	 * @since 3.18.0
	 *
	 * @param array $data Array of data.
	 * @return array
	 */
	private static function get_return( $data = array(), $done = true ) {
		return array(
			'data' => $data,
			'done' => $done,
		);
	}

	/**
	 * Get student data to export for a user
	 *
	 * @since 3.18.0
	 *
	 * @param  LLMS_Student $student Student object.
	 * @return array
	 */
	private static function get_student_data( $student ) {

		$data = array();

		$props = self::get_student_data_props();

		foreach ( $props as $prop => $name ) {

			$value = apply_filters( 'llms_privacy_export_student_data_prop_value', $student->get( $prop ), $prop, $student );

			if ( $value ) {
				$data[] = array(
					'name'  => $name,
					'value' => $value,
				);
			}
		}

		return apply_filters( 'llms_privacy_export_student_data', $data, $student );

	}

	/**
	 * Export student course data by email address
	 *
	 * @since 3.18.0
	 *
	 * @param string $email_address Email address of the user to retrieve data for.
	 * @param int    $page          Process page number.
	 * @return array
	 */
	public static function course_data( $email_address, $page ) {
		return self::enrollment_data( $email_address, $page, 'course' );
	}

	/**
	 * General exporter for handling course and membership enrollment data
	 *
	 * @since 3.18.0
	 * @since 3.37.9 Added `$group_description` to the group exporter.
	 *
	 * @param    string $email_address  Requested user's email address
	 * @param    int    $page           process page number
	 * @param    string $post_type      name of the post type
	 * @return   array
	 */
	private static function enrollment_data( $email_address, $page, $post_type ) {

		$data = array();

		$student = self::get_student_by_email( $email_address );
		if ( ! $student ) {
			return self::get_return( $data );
		}

		$enrollments = self::get_student_enrollments( $student, $page, $post_type );
		if ( $enrollments['results'] ) {

			$post_type_obj = get_post_type_object( $post_type );
			$group_id      = 'lifterlms_' . $post_type;

			foreach ( $enrollments['results'] as $post_id ) {

				$data[] = array(
					'group_id'          => $group_id,
					'group_label'       => $post_type_obj->labels->name,
					/* translators: %s: The name of the enrollment post type. */
					'group_description' => sprintf( __( 'Student %s enrollment data.', 'lifterlms' ), $post_type_obj->labels->name ),
					'item_id'           => sprintf( '%1$s-%2$d', $post_type, $post_id ),
					'data'              => self::get_enrollment_data( $post_id, $student, $post_type_obj ),
				);

			}
		}

		return self::get_return( $data, $enrollments['done'] );

	}

	/**
	 * Add files to the zip file for a data export request.
	 *
	 * Adds certificate files into the `/certificates/` directory within the archive.
	 *
	 * @since 3.18.0
	 * @since 6.0.0 Replaced the use of the deprecated `wp_get_user_request_data()` function with `wp_get_user_request()`.
	 *
	 * @param string $archive_pathname     Full path to the zip archive.
	 * @param string $archive_url          Full URI to the zip archive.
	 * @param string $html_report_pathname Full path to the .html file within the archive.
	 * @param int    $request_id           WP Post ID of the export request.
	 * @return void
	 */
	public static function maybe_add_export_files( $archive_pathname, $archive_url, $html_report_pathname, $request_id ) {

		if ( ! class_exists( 'ZipArchive' ) ) {
			return;
		}

		$request = wp_get_user_request( $request_id );
		$student = self::get_student_by_email( $request->email );

		if ( ! $student ) {
			return;
		}

		$certs = self::get_student_certificates( $student );
		if ( ! $certs ) {
			return;
		}

		$zip    = new ZipArchive();
		$delete = array();
		if ( true === $zip->open( $archive_pathname ) ) {
			foreach ( $certs as $cert ) {
				$filepath                        = llms()->certificates()->get_export( $cert->get( 'id' ), true );
				$delete[ $cert->certificate_id ] = $filepath;
				if ( is_wp_error( $filepath ) ) {
					continue;
				}
				$zip->addFile( $filepath, '/certificates/' . basename( $filepath ) );
			}
		}

		$zip->close();

		// cleanup all files
		foreach ( $delete as $id => $path ) {
			wp_delete_file( $path );
			delete_post_meta( $id, '_llms_export_filepath' );
		}

	}

	/**
	 * Export student membership data by email address
	 *
	 * @since    3.18.0
	 *
	 * @param    string $email_address  email address of the user to retrieve data for
	 * @param    int    $page           process page number
	 * @return   array
	 */
	public static function membership_data( $email_address, $page ) {
		return self::enrollment_data( $email_address, $page, 'llms_membership' );
	}

	/**
	 * Export student orders data by email address
	 *
	 * @since 3.18.0
	 * @since 3.37.9 Added `$group_description` to the group exporter.
	 *
	 * @param  string $email_address Email address of the user to retrieve data for.
	 * @param  int    $page          Process page number.
	 * @return array
	 */
	public static function order_data( $email_address, $page ) {

		$data = array();

		$student = self::get_student_by_email( $email_address );
		if ( ! $student ) {
			return self::get_return( $data );
		}

		$orders = self::get_student_orders( $student, $page );

		$group_label       = __( 'Orders', 'lifterlms' );
		$group_description = __( 'Student orders data.', 'lifterlms' );
		foreach ( $orders['orders'] as $order ) {

			$data[] = array(
				'group_id'          => 'lifterlms_orders',
				'group_label'       => $group_label,
				'group_description' => $group_description,
				'item_id'           => sprintf( 'order-%d', $order->get( 'id' ) ),
				'data'              => self::get_order_data( $order ),
			);

		}

		return self::get_return( $data, $orders['done'] );

	}

	/**
	 * Export student data by email address
	 *
	 * @since 3.18.0
	 * @since 3.37.9 Added `$group_description` to the group exporter.
	 *
	 * @param  string $email_address Email address of the user to retrieve data for.
	 * @param  int    $page          Process page number.
	 * @return array
	 */
	public static function student_data( $email_address, $page ) {

		$data = array();

		$student = self::get_student_by_email( $email_address );
		if ( ! $student ) {
			return self::get_return( $data );
		}

		$data[] = array(
			'group_id'          => 'lifterlms_student',
			'group_label'       => __( 'Personal Information', 'lifterlms' ),
			'group_description' => __( 'Student personal information data.', 'lifterlms' ),
			'item_id'           => sprintf( 'student-%d', $student->get( 'id' ) ),
			'data'              => self::get_student_data( $student ),
		);

		return self::get_return( $data );

	}

	/**
	 * Export quiz attempt data by email address
	 *
	 * @since    3.18.0
	 * @since    3.37.9 Added `$group_description` to the group exporter.
	 *
	 * @param    string $email_address Email address of the user to retrieve data for.
	 * @param    int    $page          Process page number.
	 * @return   array
	 */
	public static function quiz_data( $email_address, $page ) {

		$data = array();

		$student = self::get_student_by_email( $email_address );
		if ( ! $student ) {
			return self::get_return( $data );
		}

		$query = self::get_student_quizzes( $student, $page );
		$done  = true;
		if ( $query->has_results() ) {

			$group_label        = __( 'Quiz Attempts', 'lifterlms' );
			$group_descriptions = __( 'Student quiz attempt data', 'lifterlms' );
			foreach ( $query->get_attempts() as $attempt ) {

				$data[] = array(
					'group_id'          => 'lifterlms_quizzes',
					'group_label'       => $group_label,
					'group_description' => $group_description,
					'item_id'           => sprintf( 'order-%d', $attempt->get( 'id' ) ),
					'data'              => self::get_quiz_attempt_data( $attempt ),
				);

			}

			$done = $query->is_last_page();

		}

		return self::get_return( $data, $done );

	}

}
