<?php
/**
 * LifterLMS Quiz Question Model
 *
 * @package LifterLMS/Models/Classes
 *
 * @since 3.16.0
 * @version 7.4.1
 */

defined( 'ABSPATH' ) || exit;

/**
 * LLMS_Question_Choice model class
 *
 * @since 3.16.0
 */
class LLMS_Question_Choice {

	protected $prefix = '_llms_choice_';

	private $id = null;

	private $data = array();

	private $question    = null;
	private $question_id = null;

	/**
	 * Constructor
	 *
	 * @param    int          $question_id  WP Post ID of the choice's parent LLMS_Question
	 * @param    array|string $data_or_id   array of choice data or the choice ID string
	 * @since    3.16.0
	 * @version  3.16.0
	 */
	public function __construct( $question_id, $data_or_id = array() ) {

		// Ensure the question is valid.
		if ( $this->set_question( $question_id ) ) {

			// If an ID is passed in, load the question data from post meta.
			if ( ! is_array( $data_or_id ) ) {
				$data_or_id = str_replace( $this->prefix, '', $data_or_id );
				$data_or_id = get_post_meta( $this->question_id, $this->prefix . $data_or_id, true );
			}

			// Hydrate with postmeta data or array of data passed in.
			if ( is_array( $data_or_id ) && isset( $data_or_id['id'] ) ) {
				$this->hydrate( $data_or_id );
			}
		}

	}

	/**
	 * Creates a new question
	 *
	 * @param    array $data  question data array
	 * @return   self
	 * @since    3.16.0
	 * @version  3.16.0
	 */
	public function create( $data ) {

		$this->id = uniqid();
		return $this->update( $data )->save();

	}

	/**
	 * Delete a choice
	 *
	 * @return   boolean
	 * @since    3.16.0
	 * @version  3.16.0
	 */
	public function delete() {
		return delete_post_meta( $this->question_id, $this->prefix . $this->id );
	}

	/**
	 * Determine if the choice that's been requested actually exists
	 *
	 * @return   boolean
	 * @since    3.16.0
	 * @version  3.16.0
	 */
	public function exists() {
		return ( $this->id );
	}

	/**
	 * Retrieve a piece of choice data by key
	 *
	 * @param    string $key      name of the data to be retrieved
	 * @param    mixed  $default  default value if key isn't set
	 * @return   mixed
	 * @since    3.16.0
	 * @version  3.16.0
	 */
	public function get( $key, $default = '' ) {

		if ( isset( $this->data[ $key ] ) ) {
			return $this->data[ $key ];
		}

		return $default;

	}

	/**
	 * Generic choice getter which automatically uses correct functions based on choice type
	 *
	 * @return   string
	 * @since    3.16.0
	 * @version  3.16.0
	 */
	public function get_choice() {
		if ( 'image' === $this->get( 'choice_type' ) ) {
			return $this->get_image();
		}
		return $this->get( 'choice' );
	}

	/**
	 * Retrieve an image for picture choices
	 *
	 * @return   [type]
	 * @since    3.16.0
	 * @version  3.16.0
	 */
	public function get_image() {
		if ( 'image' !== $this->get( 'choice_type' ) ) {
			return '';
		}
		$img = $this->get( 'choice' );
		if ( is_array( $img ) && isset( $img['id'] ) ) {
			return wp_get_attachment_image( $img['id'], 'full' );
		}
		return '';
	}

	/**
	 * Retrieve all of the choice data as an array
	 *
	 * @return   array
	 * @since    3.16.0
	 * @version  3.16.0
	 */
	public function get_data() {
		return $this->data;
	}

	/**
	 * Retrieve an instance of an LLMS_Question for questions parent
	 *
	 * @return   obj
	 * @since    3.16.0
	 * @version  3.16.0
	 */
	public function get_question() {
		return $this->question;
	}

	/**
	 * Retrieve the question ID for the given choice
	 *
	 * @return   int
	 * @since    3.16.0
	 * @version  3.16.0
	 */
	public function get_question_id() {
		return $this->question_id;
	}

	/**
	 * Setup the id and data variables
	 *
	 * @param    array $data  array of question data
	 * @return   void
	 * @since    3.16.0
	 * @version  3.16.0
	 */
	private function hydrate( $data ) {
		$this->id         = $data['id'];
		$this->data['id'] = $this->id;
		$this->update( $data );
	}

	/**
	 * Determine if the choice is correct
	 *
	 * @return   bool
	 * @since    3.16.0
	 * @version  3.16.0
	 */
	public function is_correct() {
		return filter_var( $this->get( 'correct' ), FILTER_VALIDATE_BOOLEAN );
	}

	/**
	 * Save $this->data to the postmeta table
	 *
	 * @return   void
	 * @since    3.16.0
	 * @version  3.16.0
	 */
	public function save() {

		$this->data['id'] = $this->id; // Always ensure the ID is set when saving data.
		$update           = update_post_meta( $this->question_id, $this->prefix . $this->id, $this->data );

		return ( $update );

	}

	/**
	 * Set a piece of data by key.
	 *
	 * @since 3.16.0
	 * @since 7.4.1 Check `$type['choices']` is an array before trying to access it as such.
	 *
	 * @param string $key Name of the key to set.
	 * @param mixed  $val Value to set.
	 * @return self
	 */
	public function set( $key, $val ) {

		// Don't set the ID.
		if ( 'id' === $key ) {
			return $this;
		}

		switch ( $key ) {

			case 'choice_type':
				if ( ! in_array( $val, array( 'text', 'image' ) ) ) {
					$val = 'text';
				}
				break;

			case 'correct':
				$val = filter_var( $val, FILTER_VALIDATE_BOOLEAN );
				break;

			case 'marker':
				$type = $this->get_question()->get_question_type();
				if ( is_array( $type['choices'] ?? false ) ) {
					$markers = $type['choices']['markers'];
					if ( ! in_array( $val, $markers ) ) {
						$val = $markers[0];
					}
				}
				break;

			case 'choice':
			default:
				if ( is_array( $val ) ) {
					$val = array_map( 'sanitize_text_field', $val );
				} else {
					$val = wp_kses_post( $val );
				}
				break;

		}

		$this->data[ $key ] = $val;
		return $this;

	}

	/**
	 * Sets question-related data from constructor
	 *
	 * @param    int $id  WP Post ID of the question's parent question
	 * @return   boolean
	 * @since    3.16.0
	 * @version  3.16.0
	 */
	public function set_question( $id ) {
		$question = llms_get_post( $id );
		if ( $question && is_a( $question, 'LLMS_Question' ) ) {
			$this->question    = $question;
			$this->question_id = $id;
			return true;
		}

		return false;

	}

	/**
	 * Update multiple data by key=>val pairs
	 *
	 * @param    array $data  array of data to set
	 * @return   self
	 * @since    3.16.0
	 * @version  3.16.0
	 */
	public function update( $data = array() ) {

		foreach ( $data as $key => $val ) {
			$this->set( $key, $val );
		}
		return $this;

	}

}
