<?php
/**
 * File containing the model \Crowdsignal_Forms\Models\Poll.
 *
 * @package crowdsignal-forms/Models
 * @since 0.9.0
 */

namespace Crowdsignal_Forms\Models;

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

/**
 * Class Poll
 *
 * @since 0.9.0
 * @package Crowdsignal_Forms\Models
 */
class Poll {
	const POLL_ID_BLOCK_ATTRIBUTE   = 'pollId';
	const ANSWER_ID_BLOCK_ATTRIBUTE = 'answerId';

	/**
	 * The poll id.
	 *
	 * @since 0.9.0
	 * @var int
	 */
	private $id = 0;

	/**
	 * The client id.
	 *
	 * @since 0.9.0
	 * @var string
	 */
	private $client_id = '';

	/**
	 * The poll question.
	 *
	 * @since 0.9.0
	 * @var string
	 */
	private $question = '';

	/**
	 * The poll answers.
	 *
	 * @since 0.9.0
	 * @var array
	 */
	private $answers = array();

	/**
	 * The poll settings.
	 *
	 * @since 0.9.0
	 * @var Poll_Settings|null
	 */
	private $settings = null;

	/**
	 * The poll source_link.
	 *
	 * @since 0.9.0
	 * @var string
	 */
	private $source_link = '';

	/**
	 * The poll note.
	 *
	 * @since 0.9.0
	 * @var string
	 */
	private $note = '';

	/**
	 * The poll based block type.
	 *
	 * @since 1.1.0
	 * @var string
	 */
	private $poll_type = '';

	/**
	 * Poll constructor.
	 *
	 * @param int           $id The id.
	 * @param string        $question The question.
	 * @param string        $note The note.
	 * @param array         $answers The answers.
	 * @param Poll_Settings $settings The settings.
	 */
	public function __construct( $id, $question, $note, array $answers, Poll_Settings $settings ) {
		$this->id       = $id;
		$this->question = $question;
		$this->note     = $note;
		$this->answers  = $answers;
		$this->settings = $settings;
	}

	/**
	 * Creates a new Poll object from an array.
	 *
	 * @param array $data The data.
	 * @return Poll
	 * @since 0.9.0
	 */
	public static function from_array( $data ) {
		$id       = isset( $data['id'] ) ? absint( $data['id'] ) : 0;
		$question = isset( $data['question'] ) ? $data['question'] : '';
		$note     = isset( $data['note'] ) ? $data['note'] : '';
		$answers  = array_map(
			function ( $poll_data ) {
				return Poll_Answer::from_array( $poll_data );
			},
			isset( $data['answers'] ) ? $data['answers'] : array()
		);
		$settings = Poll_Settings::from_array( isset( $data['settings'] ) ? $data['settings'] : array() );
		$poll     = new self( $id, $question, $note, $answers, $settings );

		if ( isset( $data['client_id'] ) ) {
			$poll->set_client_id( $data['client_id'] );
		}

		if ( isset( $data['comment_id'] ) ) {
			$poll->set_source_link( \get_comment_link( $data['comment_id'] ) );
		} elseif ( isset( $data['post_id'] ) ) {
			// v2 will carry both edit and view links, leaving this commented for the future
			// $source_link = trim( admin_url( 'post.php?post=' . $data['post_id'] . '&action=edit' ) );
			// $poll->set_source_link( $source_link );.
			$poll->set_source_link( \get_permalink( $data['post_id'] ) );
		} else {
			$poll->set_source_link( \get_site_url() );
		}

		return $poll;
	}

	/**
	 * Processes the block data to convert a poll based block to a poll model.
	 *
	 * @param mixed $block
	 * @return $this
	 * @since 1.1.0
	 */
	public function update_from_block( $block ) {
		$this->set_poll_type( str_replace( 'crowdsignal-forms/', '', $block['blockName'] ) );

		$attrs = $block['attrs'];

		if ( 'crowdsignal-forms/poll' === $block['blockName'] ) {
			return $this->update_from_block_attrs( $attrs, $attrs['answers'] );
		}

		if ( 'crowdsignal-forms/vote' === $block['blockName'] ) {
			$answers = array();

			foreach ( $block['innerBlocks'] as $child ) {
				$child_attrs         = $child['attrs'];
				$child_attrs['text'] = $child_attrs['type'];
				$answers[]           = $child_attrs;
			}

			return $this->update_from_block_attrs( $attrs, $answers );
		}

		if ( 'crowdsignal-forms/applause' === $block['blockName'] ) {
			$answers = array();

			$answers[] = array(
				'answerId' => $attrs['answerId'],
				'text'     => 'clap',
			);

			return $this->update_from_block_attrs( $attrs, $answers );
		}

		return $this;
	}

	/**
	 * Update this poll from the block attrs.
	 *
	 * @param array $attrs The block attrs.
	 * @param array $answers The answer attributes.
	 * @return $this
	 */
	public function update_from_block_attrs( $attrs, $answers ) {
		$this->client_id   = $attrs['pollId'] ? $attrs['pollId'] : '';
		$attribute_answers = isset( $answers ) ? $answers : array();
		$this->question    = isset( $attrs['question'] ) ? $attrs['question'] : '';
		$this->note        = isset( $attrs['note'] ) ? $attrs['note'] : '';

		$block_answers_by_uuid = array();
		foreach ( $attribute_answers as $attribute_answer ) {
			if ( gettype( $attribute_answer ) === 'object' ) {
				// if answer is an object, then it is still a default value, so there are no answer ids set.
				continue;
			}
			$block_answers_by_uuid[ $attribute_answer['answerId'] ] = $attribute_answer;
		}

		foreach ( $this->answers as $i => &$answer ) {
			$answer_block_id = $answer->get_client_id();
			if ( in_array( $answer_block_id, array_keys( $block_answers_by_uuid ), true ) ) {
				$answer->update_from_block( $block_answers_by_uuid[ $answer_block_id ] );
				unset( $block_answers_by_uuid[ $answer_block_id ] );
			} else {
				// todo: delete this answer, it is no longer in the block.
				unset( $this->answers[ $i ] );
			}
		}

		// Trick to reindex the array.
		$this->answers = array_values( $this->answers );

		foreach ( $block_answers_by_uuid as $new_answer_data ) {
			$new_answer = Poll_Answer::from_array( array() );
			$new_answer->update_from_block( $new_answer_data );
			$this->answers[] = $new_answer;
		}

		$this->settings->update_from_block( $attrs );

		return $this;
	}

	/**
	 * Validates the poll is ok for saving.
	 *
	 * @since 0.9.0
	 * @return bool|\WP_Error
	 */
	public function validate() {
		return true;
	}

	/**
	 * Get the id.
	 *
	 * @since 0.9.0
	 * @return int
	 */
	public function get_id() {
		return $this->id;
	}

	/**
	 * Get the question.
	 *
	 * @since 0.9.0
	 * @return int
	 */
	public function get_question() {
		return $this->question;
	}

	/**
	 * Get the answers.
	 *
	 * @since 0.9.0
	 * @return array
	 */
	public function get_answers() {
		return $this->answers;
	}

	/**
	 * Get the source link we set.
	 *
	 * @return string
	 */
	public function get_source_link() {
		return $this->source_link;
	}

	/**
	 * Set the poll block source link.
	 *
	 * @param string $source_link The source link.
	 * @return $this
	 */
	public function set_source_link( $source_link ) {
		$this->source_link = $source_link;
		return $this;
	}

	/**
	 * Get the poll type.
	 *
	 * @return string
	 */
	public function get_poll_type() {
		return $this->poll_type;
	}

	/**
	 * Set the poll type.
	 *
	 * @param string $poll_type The poll type.
	 * @return $this
	 */
	public function set_poll_type( $poll_type ) {
		$this->poll_type = $poll_type;
		return $this;
	}

	/**
	 * Transform the poll into an array for sending to the api or the frontend.
	 *
	 * @since 0.9.0
	 * @param string $context The context which we will be using the array.
	 * @return array
	 */
	public function to_array( $context = 'view' ) {
		$data = array();

		if ( ! empty( $this->id ) ) {
			$data['id'] = $this->id;
		}

		$data['question'] = $this->question;
		$data['note']     = $this->note;

		$data['settings'] = $this->settings->to_array( $context );
		foreach ( $this->get_answers() as $answer ) {
			$data['answers'][] = $answer->to_array();
		}

		if ( ! empty( $this->source_link ) ) {
			$data['source_link'] = $this->get_source_link();
		}

		if ( ! empty( $this->client_id ) ) {
			$data['client_id'] = $this->client_id;
		}

		if ( ! empty( $this->poll_type ) ) {
			$data['poll_type'] = $this->poll_type;
		}

		return $data;
	}

	/**
	 * Set the client id.
	 *
	 * @param string $client_id Unique client id.
	 * @return $this
	 */
	private function set_client_id( $client_id ) {
		$this->client_id = $client_id;
		return $this;
	}
}