<?php
/**
 * The main class for Extra Action.
 */

namespace Simple_Giveaways;

use WP_Error;

/**
 * Class SG_Extra_Action
 */
class SG_Extra_Action {

	/**
	 * Action Icon
	 *
	 * @var string
	 */
	protected $icon = 'fal fa-plus-square';

	/**
     * Action Image
     *
	 * @var string
	 */
    protected $image = '';

	/**
	 * Action Slug
	 *
	 * @var string
	 */
	protected $slug = '';

	/**
	 * Instance ID
	 *
	 * @var integer
	 */
	protected $instance_id = 0;

	/**
	 * Giveaway ID
	 *
	 * @var integer
	 */
	protected $giveaway_id = 0;

	/**
	 * Action Title
	 *
	 * @var string
	 */
	protected $title = '';

	/**
	 * Action Description
	 *
	 * @var string
	 */
	protected $description = '';

	/**
	 * A category under which this action will be shown.
	 *
	 * @var string
	 */
	protected $category = 'general';

	/**
	 * Data for admin area.
	 *
	 * @var array
	 */
	protected $admin_data = array();

	/**
	 * Data given by the subscriber meta data.
	 *
	 * @var array
	 */
	protected $data = null;

	/**
	 * Fields
	 *
	 * @var array
	 */
	protected $fields = array();

	/**
	 * Is this action using the action button.
	 *
	 * @var bool
	 */
	protected $has_action_button = true;

	/**
	 * Is the action auto submitted when the action is clicked?
	 *
	 * @var bool
	 */
	protected $auto_submit = false;

	/**
	 * Is the form complete button disabled at first?
	 *
	 * @var bool
	 */
	protected $submit_disabled = true;

	/**
	 * Subscriber ID.
	 *
	 * @var int
	 */
	protected $subscriber_id = 0;

	/**
	 * Admin Errors
	 *
	 * @var array
	 */
	protected $admin_errors = array();

	/**
	 * @var null|string
	 */
	protected $complete_button_text = null;

	/**
	 * If an action is internal only, it won't be display for the user to select them.
	 *
	 * @var bool
	 */
	protected $internal_only = false;

	/**
	 * Entries given by the action.
	 *
	 * @var int
	 */
	protected $entries = 1;

	/**
	 * Store option templates to load at the footer
	 *
	 * @var array
	 */
	public $option_templates = array();

	/**
	 * If locked, it can't be submitted.
	 *
	 * @var bool
	 */
	protected $locked = false;

	/**
	 * Can this action have custom color.
	 *
	 * @var bool
	 */
	protected $has_custom_colors = true;

	/**
	 * On AJAX complete, should we reload the html?
	 *
	 * @return bool
	 */
	public function reload_html() {
		return true;
	}

	/**
	 * Admin Form
	 */
	public function form_admin( $index = 0, $instance = '' ) {
		$instance_id = isset( $this->admin_data['instance_id'] ) ? $this->admin_data['instance_id'] : $instance;
		$title       = isset( $this->admin_data['title'] ) ? $this->admin_data['title'] : '';
		if ( $instance_id ) {
			$this->validate_admin_data();
		}
		$hasErrors = count( $this->admin_errors ) > 0;
		?>
		<div class="sg-admin-action 
		<?php
		if ( $hasErrors ) {
			echo 'sg-admin-action-with-errors'; }
		?>
		" data-index="<?php echo esc_attr( $index ); ?>">
			<h3 class="sg-admin-action-title"><?php echo esc_html( $this->get_title() ); ?><?php echo $title ? ': ' . esc_html( $title ) : ''; ?></h3>
			<div class="sg-admin-form sg-action-form sg-action-form-<?php echo esc_attr( $this->slug ); ?>">
				<input type="hidden" name="entries_actions[<?php echo esc_attr( $index ); ?>][action]" value="<?php echo esc_attr( $this->slug ); ?>" />
				<input type="hidden" name="entries_actions[<?php echo esc_attr( $index ); ?>][instance_id]" value="<?php echo esc_attr( $instance_id ); ?>" class="sg-action-form-instance" />
				<?php
				if ( $hasErrors ) {
					for ( $i = 0; $i < count( $this->admin_errors ); $i++ ) {
						$error = $this->admin_errors[ $i ];
						echo '<div class="notice error"><p>' . esc_html( $error ) . '</p></div>';
					}
				}
				?>
				<div class="sg-form-field sg-form-field-title">
					<label for="entries_actions_<?php echo esc_attr( $index ); ?>">
						<?php esc_html_e( 'Title', 'giveasap' ); ?>
					</label>
					<div class="sg-action-input-container">
						<input type="text" id="entries_actions_<?php echo esc_attr( $index ); ?>" name="entries_actions[<?php echo esc_attr( $index ); ?>][title]" class="widefat" value="<?php echo esc_attr( $title ); ?>" />
					</div>
				</div>

				<?php
					$fields = $this->get_fields();

				if ( $fields ) {
					foreach ( $fields as $field ) {
						$this->render_field( $index, $field );
					}
				}

					do_action( 'sg_extra_action_admin_form', $index, $this );
					do_action( 'sg_extra_action_admin_form_' . $this->slug, $index, $this );
				?>

				<button type="button" class="button button-secondary small sg-admin-action-remove"><?php esc_html_e( 'Remove', 'giveasap' ); ?></button>
			</div>
		</div>
		<?php
	}

	/**
	 * Get Locked HTML
	 */
	public function get_locked_html() {
		$unlock_message = _n_noop( 'Complete %s action in total to unlock it', 'Complete %s actions in total to unlock it', 'giveasap' );
		$message        = sprintf( translate_nooped_plural( $unlock_message, $this->get_mandatory(), 'giveasap' ), $this->get_mandatory() );

		?>
		<div class="sg-extra-action-locked">
			<span class="fal fa-lock-alt sg-extra-action-locked-icon"></span>
			<div class="sg-extra-action-locked-text">
				<?php

				echo wp_kses_post( apply_filters( 'giveasap_action_get_locked_message', $message, $this ) );

				do_action( 'giveasap_extra_action_locked_html_after_message', $this );
				?>
			</div>
		</div>
		<?php
	}

	/**
	 * Public Form
	 */
	public function form_public() {
		$admin_data = $this->get_admin_data();
		$data       = $this->get_data();

		if ( $this->can_complete() ) {
			$this->submit_disabled = false;
		}

        if ( isset( $admin_data['complete_text'] ) ) {
	        $this->complete_button_text = $admin_data['complete_text'];
        }

		$complete_text_button = null === $this->complete_button_text ? __( 'Complete', 'giveasap' ) : $this->complete_button_text;

		?>
		<div id="sgExtraAction<?php echo esc_attr( $admin_data['instance_id'] ); ?>" class="sg-extra-action sg-extra-action-<?php echo esc_attr( $this->get_slug() ); ?> sg-extra-action-category-<?php echo esc_attr( $this->category ); ?> <?php echo $this->is_locked() ? 'sg-extra-action-locked' : ''; ?>" <?php echo $this->action_attributes(); ?>>
			<button aria-expanded="false" class="sg-extra-action-title">
				<?php
				echo $this->get_icon();
				echo '<span class="action-title">' . esc_html( $admin_data['title'] ) . '</span>';
				if ( $this->is_completed() ) {
					echo $this->complete_mark();
				} else {
					echo $this->action_entries();
				}

				?>
			</button>
			<form method="POST" enctype="multipart/form-data">
				<input type="hidden" name="extra_action" value="<?php echo esc_attr( $this->get_slug() ); ?>" />
				<input type="hidden" name="instance" value="<?php echo esc_attr( $this->get_instance_id() ); ?>" />
				<input type="hidden" name="subscriber_id" value="<?php echo esc_attr( $this->get_subscriber_id() ); ?>" />
				<input type="hidden" name="nonce" value="<?php echo esc_attr( wp_create_nonce( 'giveaway' ) ); ?>" />
				<input type="hidden" name="giveaway_id" value="<?php echo esc_attr( $this->get_giveaway_id() ); ?>" />
				<?php
				if ( $this->is_locked() ) {
					$this->get_locked_html();
				}
				?>
				<div class="sg-extra-action-form-body">
					<?php
					$this->form_public_content();
					if ( $this->has_action_button ) {
						$this->action_button();
					}
					?>
				</div>
				<div class="sg-extra-action-actions">
					<?php if ( ! $this->is_completed() ) { ?>
					<button type="submit" name="sg_extra_action_submit" class="sg-extra-action-button giveasap_button" 
						<?php
						if ( $this->submit_disabled ) {
							echo 'disabled="disabled"'; }
						?>
					>
						<?php echo esc_html( $complete_text_button ); ?>
					</button>
					<?php } ?>
					<button id="sgExtraActionClose<?php echo esc_attr( $admin_data['instance_id'] ); ?>"  type="button" class="giveasap_button giveasap_button-blank sg-extra-action-close"><?php esc_html_e( 'Cancel', 'giveasap' ); ?></button>
				</div>
			</form>
			<?php
			if ( $this->has_custom_colors ) {
				?>
				<style>
					.sg-extra-action#sgExtraAction<?php echo esc_attr( $admin_data['instance_id'] ); ?> .sg-action-icon{background:<?php echo esc_html( $admin_data['background_color'] ); ?>}
					.sg-extra-action#sgExtraAction<?php echo esc_attr( $admin_data['instance_id'] ); ?> .sg-extra-action-title .sg-action-icon .fal,
					.sg-extra-action#sgExtraAction<?php echo esc_attr( $admin_data['instance_id'] ); ?> .sg-extra-action-title .sg-action-icon .fab{color:<?php echo esc_html( $admin_data['icon_color'] ); ?>}
				</style>
				<?php
			}
			?>
		</div>
		<?php
	}

	/**
	 * Get the action button.
	 * This is a general button for any action that can be used.
	 * In most cases, it will enable the complete button to submit it.
	 */
	public function action_button() {
		$data = $this->get_admin_data();

		?>
		<button type="button" data-check-action class="sg_extra_action_button giveasap_button">
			<?php echo esc_html( $data['button_text'] ); ?>
		</button>
		<?php
	}

	/**
	 * The form content of the public part.
	 */
	public function form_public_content() {}

	/**
	 * Complete the action
	 *
	 * @param integer $instance_id
	 * @param integer $subscriber_id
	 * @param array   $data Data posted from the front form.
	 *
	 * @return array|bool|WP_Error
	 */
	public function complete( $instance_id, $subscriber_id, $data = array() ) {
		$subscriber = giveasap_get_subscriber( $subscriber_id );

		if ( ! $subscriber ) {
			return new \WP_Error( 'no-subscriber', __( 'Subscriber not found.', 'giveasap' ) );
		}

		$this->set_subscriber_id( $subscriber_id );
		$this->set_instance_id( $instance_id );
		$subscriber_data = $subscriber->get_data();
		$giveaway_id     = $subscriber_data['post_id'];
		$this->set_giveaway_id( $giveaway_id );

		if ( ! $this->is_action_instance_valid( $giveaway_id ) ) {
			return new \WP_Error( 'no-valid-action', __( 'The Action and the Instance are not matching.', 'giveasap' ) );
		}

		$action_db = $this->get_action_from_db();

		if ( $this->needs_validation() && ( ! $action_db || 'invalid' === $action_db['status'] ) ) {
			return new \WP_Error( 'needs-validation', __( 'The action needs validation first.', 'giveasap' ) );
		}

		$this->set_admin_data_from_giveaway( $giveaway_id );

		if ( $this->get_mandatory() > 0 ) {
			$completed_actions = giveasap_get_public_completed_actions_for_subscriber( $subscriber_id );
			if ( count( $completed_actions ) < $this->get_mandatory() ) {
				$unlock_message = _n_noop( 'You need to complete %s action in total to submit it', 'You need to complete %s actions in total to submit it', 'giveasap' );

				$error = sprintf( translate_nooped_plural( $unlock_message, $this->get_mandatory(), 'giveasap' ), $this->get_mandatory() );

				return new \WP_Error( 'no-mandatory-actions', $error );
			}
		}

		$complete = $this->complete_action( $action_db, $data );

		if ( is_wp_error( $complete ) ) {
			return $complete;
		}

		// We don't have any data of it. Let's create it then.
		if ( $action_db ) {
			if ( 'completed' === $action_db['status'] ) {
				$ret = new \WP_Error( 'action-completed', __( 'Action was already completed', 'giveasap' ) ); // We have already completed it.
			} else {
				$ret = $this->update_status( 'completed', $action_db['id'], $complete );
			}
		} else {
			$ret       = $this->create_action( 'completed', $giveaway_id, $complete );
			$action_db = $this->get_action_from_db();
		}

		$this->set_admin_data( maybe_unserialize( $action_db['data'] ) );

		if ( ! is_wp_error( $ret ) && $ret ) {
			$subscriber->add_entries( $this->get_action_entries() );
			do_action( 'sg_extra_action_completed', $action_db, $this );
		}

		return $ret;
	}

	/**
	 * Render the Field.
	 *
	 * @param $index
	 * @param $field
	 *
	 * @return void
	 */
	public function render_field( $index, $field ) {

		$defaults = array(
			'type'        => 'text',
			'id'          => '',
			'label'       => '',
			'required'    => false,
			'multioption' => false,
			'class'       => array( 'widefat' ),
			'default'     => '',
			'desc'        => '',
		);

		$admin_data = $this->get_admin_data();

		$field      = wp_parse_args( $field, $defaults );
		$label      = '<label for="entries_actions_' . $index . '_' . $field['id'] . '">';
		$label     .= $field['label'] . ( $field['required'] ? '<span class="required">*</span>' : '' );
		$label     .= '</label>';
		$id         = 'entries_actions_' . $index . '_' . $field['id'];
		$name       = 'entries_actions[' . $index . '][' . $field['id'] . ']';
		$value      = isset( $admin_data[ $field['id'] ] ) ? $admin_data[ $field['id'] ] : $field['default'];
		$required   = $field['required'] ? 'required' : '';
		$class      = $field['class'] ? $field['class'] : '';
		$html_field = '<div class="sg-form-field sg-form-field-' . $field['type'] . ' sg-form-field-' . esc_attr( $field['id'] ) . '">%s</div>';

		$fieldClass = '';
		if ( $class ) {
			$fieldClass = 'class="';
			if ( is_array( $class ) ) {
				$fieldClass .= implode( ' ', $class );
			} else {
				$fieldClass .= $class;
			}
			$fieldClass .= '"';
		}

		switch ( $field['type'] ) {
			case 'text':
			case 'url':
			case 'number':
				$input  = '<div class="sg-action-input-container">';
				$input .= '<input ' . $fieldClass . ' type="' . esc_attr( $field['type'] ) . '" value="' . esc_attr( $value ) . '" id="' . esc_attr( $id ) . '" name="' . esc_attr( $name ) . '"/>';
				$input .= '</div>';
				if ( $field['desc'] ) {
					$input .= '<span class="description">' . esc_html( $field['desc'] ) . '</span>';
				}
				printf( $html_field, $label . $input );
				break;
			case 'textarea':
				$input  = '<div class="sg-action-input-container">';
				$input .= '<textarea ' . $fieldClass . ' id="' . esc_attr( $id ) . '" name="' . esc_attr( $name ) . '">' . wp_kses_post( $value ) . '</textarea>';
				$input .= '</div>';
				if ( $field['desc'] ) {
					$input .= '<span class="description">' . esc_html( $field['desc'] ) . '</span>';
				}
				printf( $html_field, $label . $input );
				break;
			case 'checkbox':
				$input  = '<div class="sg-action-input-container">';
				$input .= '<div class="checkbox-container widefat">';
				$input .= '<input ' . checked( $value, '1', false ) . ' ' . $fieldClass . ' type="' . esc_attr( $field['type'] ) . '" value="1" id="' . esc_attr( $id ) . '" name="' . esc_attr( $name ) . '"/>';
				if ( $field['desc'] ) {
					$input .= '<span class="description">' . esc_html( $field['desc'] ) . '</span>';
				}
				$input .= '</div>';
				$input .= '</div>';
				printf( $html_field, $label . $input );
				break;
			case 'multioption':
				$input  = '<div class="sg-action-input-container">';
				$input .= '<div class="sg-multioption-container widefat">';
				$input .= '<div class="sg-multioption-options-container" id="' . esc_attr( $id ) . '">';

				if ( $value && is_array( $value ) ) {
					foreach ( $value as $index => $val ) {
						if ( '' === $val ) {
							continue;
						}
						$attr_name = $name . '[]';
						$attr_id   = $id . '_' . $index;
						$input    .= '<div class="sg-multioption-input" data-index="' . esc_attr( $index ) . '"><input ' . $fieldClass . ' type="text" value="' . esc_attr( $val ) . '" id="' . esc_attr( $attr_id ) . '" name="' . esc_attr( $attr_name ) . '"/><button type="button" class="button button-secondary button-small sg-multioption-delete">X</button></div>';
					}
				}

				$input .= '</div>';

				$input .= '<button type="button" class="button sg-button sg-multioption-button" data-template="multioption-input" data-name="' . esc_attr( $name ) . '" data-id="' . esc_attr( $id ) . '">' . __( 'Add', 'giveasap' ) . '</button>';
				$input .= '</div>';
				$input .= '</div>';
				ob_start();

				?>
				<script type="text/template" id="tmpl-multioption-input">
				   <?php echo '<div class="sg-multioption-input" data-index="{{ data.index }}"><input ' . $fieldClass . ' type="text" value="" id="{{ data.id }}_{{ data.index }}" name="{{ data.name }}[]"/><button type="button" class="button button-secondary button-small sg-multioption-delete">X</button></div>'; ?>
				</script>
				<?php

				$this->option_templates['multioption-input'] = ob_get_clean();

				printf( $html_field, $label . $input );
				break;
			case 'select_search':
				if ( ! $fieldClass ) {
					$fieldClass = 'class="sg-select-search"';
				} else {
					$fieldClass = str_replace( 'class="', 'class="sg-select-search ', $fieldClass );
				}
				$input  = '<div class="sg-action-input-container">';
				$input .= '<div class="sg-select-search-dropdown widefat">';
				$input .= '<select data-action="' . ( isset( $field['action'] ) ? $field['action'] : '' ) . '" ' . $fieldClass . ' name="' . esc_attr( $name ) . '" id="' . esc_attr( $id ) . '" >';
				if ( $value ) {
					if ( is_array( $value ) ) {
						foreach ( $value as $index => $val ) {

							if ( '' === $val ) {
								continue;
							}
							if ( isset( $field['function_for_option'] ) ) {
								$option = call_user_func( $field['function_for_option'], $val );
							} else {
								$option = $val;
							}
							$input .= '<option value="' . $val . '">' . $option . '</option>';
						}
					} else {
						if ( isset( $field['function_for_option'] ) ) {
							$option = call_user_func( $field['function_for_option'], $value );
						} else {
							$option = $value;
						}
						$input .= '<option value="' . $value . '">' . $option . '</option>';
					}
				}
				$input .= '</select>';
				$input .= '</div>';
				$input .= '</div>';
				printf( $html_field, $label . $input );
				break;
			default:
				do_action( 'sg_extra_action_admin_field_' . $field['type'], $index, $field, $admin_data );
				break;
		}
	}

	/**
	 * Display the action attributes.
	 *
	 * @return string
	 */
	public function action_attributes() {
		$atts = $this->get_action_attributes();
		$html = '';
		if ( $atts ) {
			foreach ( $atts as $attribute => $value ) {
				$html .= 'data-' . $attribute . '="' . esc_attr( $value ) . '" ';
			}
		}
		return $html;
	}

	/**
	 * Action Entries HTML
	 *
	 * @return string
	 */
	public function action_entries() {
		$entries = $this->get_action_entries();
		$html    = '<span class="sg-extra-action-entries">' . $entries . '</span>';

		return apply_filters( 'sg_extra_action_entries_html', $html, $entries, $this );
	}

	/**
	 * Returns the complete mark.
	 *
	 * @return string
	 */
	public function complete_mark() {
		$html = '<span class="sg-extra-action-entries"><span class="fal fa-check"></span></span>';
		return apply_filters( 'sg_extra_action_complete_mark_html', $html, $this );
	}

	/**
	 * Perform a specific task for the validation action.
	 *
	 * @param array $action An array ot from DB, in most cases, empty on validation.
	 *
	 * @return array|WP_Error
	 */
	public function validate_action( $action, $data = array() ) {
		return array();
	}

	/**
	 * Perform a specific task for the completing action.
	 *
	 * @param array $action An array ot from DB, in some cases, filled.
	 * @param array $data   An array given from the front end.
	 *
	 * @return array|WP_Error
	 */
	public function complete_action( $action, $data = array() ) {
		return array();
	}

	/**
	 * Validate the instance.
	 *
	 * @param $instance_id
	 * @param $subscriber_id
	 */
	public function validate( $instance_id, $subscriber_id, $data ) {
		$subscriber = giveasap_get_subscriber( $subscriber_id );

		if ( ! $subscriber ) {
			return new \WP_Error( 'no-subscriber', __( 'Subscriber not found.', 'giveasap' ) );
		}

		$this->set_subscriber_id( $subscriber_id );
		$this->set_instance_id( $instance_id );
		$subscriber_data = $subscriber->get_data();
		$giveaway_id     = $subscriber_data['post_id'];
		$this->set_giveaway_id( $giveaway_id );

		if ( ! $this->is_action_instance_valid( $giveaway_id ) ) {
			return new \WP_Error( 'no-valid-action', __( 'The Action and the Instance are not matching.', 'giveasap' ) );
		}

		$this->set_admin_data_from_giveaway( $giveaway_id );

		if ( $this->get_mandatory() > 0 ) {
			$completed_actions = giveasap_get_public_completed_actions_for_subscriber( $subscriber_id );
			if ( count( $completed_actions ) < $this->get_mandatory() ) {
				$unlock_message = _n_noop( 'You need to complete %s action in total to submit it', 'You need to complete %s actions in total to submit it', 'giveasap' );

				$error = sprintf( translate_nooped_plural( $unlock_message, $this->get_mandatory(), 'giveasap' ), $this->get_mandatory() );

				return new \WP_Error( 'no-mandatory-actions', $error );
			}
		}

		$action_db = $this->get_action_from_db();

		$complete = $this->validate_action( $action_db, $data );

		if ( is_wp_error( $complete ) ) {
			return $complete;
		}

		if ( ! $action_db ) {
			$this->create_action( 'valid', $giveaway_id, $complete );
			return true;
		}

		if ( 'invalid' === $action_db['status'] ) {
			return new \WP_Error( 'invalid-action', __( 'Something went wrong with this action before.', 'giveasap' ) );
		}

		return true;
	}

	/**
	 * This function is used when completing the action.
	 *
	 * @return bool
	 */
	public function needs_validation() {
		return true;
	}

	/**
	 * Check if the Action instance is valid for the provided giveaway.
	 *
	 * @param $giveaway_id
	 *
	 * @return bool
	 */
	public function is_action_instance_valid( $giveaway_id ) {
		$actions = giveasap_get_extra_actions( $giveaway_id );

		if ( ! $actions ) {
			return false;
		}

		$instance_id = $this->get_instance_id();

		if ( ! $instance_id ) {
			return false;
		}

		foreach ( $actions as $action ) {
			if ( $this->get_slug() !== $action['action'] ) {
				continue;
			}

			if ( absint( $action['instance_id'] ) === absint( $instance_id ) ) {
				return true;
			}
		}

		return false;
	}

	/**
	 * Has Action Button
	 *
	 * @return bool
	 */
	public function has_action_button() {
		return $this->has_action_button;
	}

	/**
	 * Return if the action can be completed.
	 *
	 * @return bool
	 */
	public function can_complete() {
		$can_complete = true;
		$data         = $this->get_data();

		if ( $this->needs_validation() && ( ! $data || 'invalid' === $data['status'] ) ) {
			$can_complete = false;
		}

		return apply_filters( 'sg_extra_action_can_complete', $can_complete, $this );
	}

	/**
	 * Return if the current action has completed or not.
	 *
	 * @return bool
	 */
	public function is_completed() {
		$data = $this->get_data();
		$ret  = true;

		if ( ! $data ) {
			$ret = false;
		}

		// Invalid status is done through the admin area.
		$is_completed_statuses = apply_filters( 'sg_extra_action_completed_statuses', array( 'completed', 'invalid' ) );

		if ( $ret && ! in_array( $data['status'], $is_completed_statuses ) ) {
			$ret = false;
		}

		return apply_filters( 'sg_extra_action_is_completed', $ret, $this );
	}

	/**
	 * Return if this action is only to be used through code.
	 *
	 * @return bool
	 */
	public function is_internal_only() {
		return $this->internal_only;
	}

	/**
	 * Create the Action.
	 *
	 * @param string $status
	 * @param int    $giveaway_id
	 * @param array  $data
	 */
	public function create_action( $status = 'invalid', $giveaway_id = 0, $data = array() ) {
		global $wpdb;

		if ( ! is_array( $data ) ) {
			$data = (array) $data;
		}

		// Let's build the data that is saved in the db.
		if ( $giveaway_id && $this->get_instance_id() >= 0 ) {
			$actions = giveasap_get_extra_actions( $giveaway_id );
			if ( $actions ) {
				foreach ( $actions as $action ) {
					if ( absint( $this->get_instance_id() ) === absint( $action['instance_id'] ) ) {
						$this->set_admin_data( $action );
						$data = wp_parse_args( $action, $data );
					}
				}
			}
		}

		$ret = $wpdb->insert(
			$wpdb->giveasap_actions,
			array(
				'subscriber_id' => $this->get_subscriber_id(),
				'giveaway_id'   => $giveaway_id,
				'action'        => $this->get_slug(),
				'instance'      => $this->get_instance_id(),
				'status'        => $status,
				'entries'       => $this->get_action_entries(),
				'data'          => maybe_serialize( $data ),
				'datetime'      => current_time( 'mysql' ),
			)
		);

		return false !== $ret ? true : false;
	}

	/**
	 * Update the status
	 *
	 * @param $status
	 * @param $id
	 */
	public function update_status( $status, $id, $data = array() ) {
		global $wpdb;

		$update_data = array(
			'status' => $status,
		);

		$update_format = array(
			'%s',
		);

		if ( $data ) {
			$update_data['data'] = maybe_serialize( $data );
			$update_format[]     = '%s';
		}
		$ret = $wpdb->update(
			$wpdb->giveasap_actions,
			array(
				'status' => $status,
			),
			array(
				'id' => $id,
			),
			array( '%s' ),
			array( '%d' )
		);

		return false !== $ret ? true : false;
	}

	/**
	 * Validate Admin Data.
	 *
	 * @param $admin_data
	 */
	public function validate_admin_data() {

		$admin_data = $this->get_admin_data();

		if ( ! isset( $admin_data['title'] ) || '' === $admin_data['title'] ) {
			$this->admin_errors[] = $this->get_title() . ': ' . __( 'No Extra Action Title. Please enter one.', 'giveasap' );
		}

		$fields = $this->get_fields();

		foreach ( $fields as $field ) {
			if ( isset( $field['required'] ) && $field['required']
				 && ( ! isset( $admin_data[ $field['id'] ] ) || ! $admin_data[ $field['id'] ] ) ) {
				$this->admin_errors[] = $this->get_title() . ': ' . sprintf( __( 'Field %s is required', 'giveasap' ), $field['label'] );
			}

			if ( isset( $field['validate'] ) && $field['validate'] && is_callable( $field['validate'] ) ) {
				$errors = call_user_func_array( $field['validate'], array( $field, $admin_data ) );
				if ( $errors ) {
					if ( is_array( $errors ) ) {
						$this->admin_errors = array_merge( $this->admin_errors, $errors );
					} else {
						$this->admin_errors[] = $errors;
					}
				}
			}
		}
	}

	/**
	 * This is the value that we will show on the Entries table.
	 */
	public function prepare_value_for_entries_table( $row ) {
		if ( $row ) {
			$this->set_subscriber_id( $row['subscriber_id'] );
			$this->set_admin_data( maybe_unserialize( $row['data'] ) );
			$this->set_instance_id( $row['instance'] );
		}
	}

	/**
	 * Return if locked.
	 *
	 * @return bool
	 */
	public function is_locked() {
		return $this->get_locked() === true;
	}

	/******************
	 * Getters
	 ******************/

	/**
	 * Return the entries for the action.
	 *
	 * @return integer
	 */
	public function get_action_entries() {
		return apply_filters( 'sg_extra_action_entries', $this->entries, $this );
	}

	/**
	 * Get action attributes.
	 *
	 * @return array
	 */
	public function get_action_attributes() {
		$data = $this->get_admin_data();
		$atts = array(
			'instance'  => $data['instance_id'],
			'action'    => $this->get_slug(),
			'mandatory' => $this->get_mandatory(),
			'completed' => $this->is_completed() ? 1 : 0,
		);
		return apply_filters( 'sg_extra_action_get_attributes', $atts, $this );
	}

	/**
	 * Get Fields
	 *
	 * @return array
	 */
	public function get_fields() {
		$field_keys = wp_list_pluck( $this->fields, 'id' );
		if ( $this->has_action_button ) {
			if ( ! in_array( 'button_text', $field_keys ) ) {
				$this->fields[] = array(
					'id'    => 'button_text',
					'label' => __( 'Button text', 'giveasap' ),
				);
			}
		}

		if ( ! in_array( 'complete_text', $field_keys ) ) {
			$this->fields[] = array(
				'id'    => 'complete_text',
				'label' => __( 'Complete button (text)', 'giveasap' ),
                'default' => 'Complete',
			);
		}

		$this->fields['mandatory'] = array(
			'id'    => 'mandatory',
			'label' => __( 'Mandatory Actions', 'giveasap' ),
			'type'  => 'number',
			'desc'  => __( 'How many actions have to be completed before this is available? If it needs 2, make sure that 2 are available.', 'giveasap' ),
		);

		if ( $this->has_custom_colors ) {
			$this->fields['background_color'] = array(
				'id'      => 'background_color',
				'label'   => __( 'Background Color', 'giveasap' ),
				'type'    => 'text',
				'class'   => 'sg-color-picker',
				'default' => '#a46497',
			);

			$this->fields['icon_color'] = array(
				'id'      => 'icon_color',
				'label'   => __( 'Icon color', 'giveasap' ),
				'type'    => 'text',
				'class'   => 'sg-color-picker',
				'default' => '#ffffff',
			);
		}

		return apply_filters( 'sg_extra_action_admin_fields', $this->fields, $this );
	}

	/**
	 * Get the admin form.
	 *
	 * @param int|string $index
     * @param int|string $instance Max instance ID.
	 *
	 * @return string
	 */
	public function get_admin_form( $index = 0, $instance = '' ) {
		ob_start();
		$this->form_admin( $index, $instance );
		return ob_get_clean();
	}

	public function get_icon() {
		return '<span class="sg-action-icon"><span class="' . esc_attr( $this->icon ) . '"></span></span>';
	}

	/**
	 * Return the Action Slug
	 *
	 * @return string
	 */
	public function get_slug() {
		return $this->slug;
	}

	/**
	 * Return the instance id.
	 *
	 * @return integer
	 */
	public function get_instance_id() {
		return $this->instance_id;
	}

	/**
	 * Return the giveaway id.
	 *
	 * @return integer
	 */
	public function get_giveaway_id() {
		return $this->giveaway_id;
	}

	/**
	 * Return the title.
	 *
	 * @return string
	 */
	public function get_title() {
		return $this->title;
	}

	/**
	 * Return the description.
	 *
	 * @return string
	 */
	public function get_description() {
		return $this->description;
	}

	/**
	 * Return the Category.
	 *
	 * @return string
	 */
	public function get_category() {
		return $this->category;
	}

	/**
	 * Get data
	 */
	public function get_data() {
		if ( null === $this->data ) {
			$data = $this->get_action_from_db();
			if ( ! $data ) {
				$data = array();
			} elseif ( is_array( $data ) ) {
				$data = array_map( 'maybe_unserialize', $data );
			}
			$this->data = $data;
		}
		return $this->data;
	}

	/**
	 * Get admin data
	 */
	public function get_admin_data() {
		return wp_parse_args(
			$this->admin_data,
			array(
				'title'       => '',
				'instance_id' => '',
				'button_text' => __( 'Do it', 'giveasap' ),
			)
		);
	}

	/**
	 * Get the action from DB.
	 *
	 * @return bool|array
	 */
	public function get_action_from_db() {
		global $wpdb;

		if ( ! $this->get_subscriber_id() ) {
			return false;
		}

		if ( ! $this->get_instance_id() ) {
			return false;
		}

		$action = $wpdb->get_row( $wpdb->prepare( 'SELECT * FROM ' . $wpdb->giveasap_actions . ' WHERE action=%s AND subscriber_id=%d AND instance=%d ORDER BY datetime DESC', $this->get_slug(), $this->get_subscriber_id(), $this->get_instance_id() ), ARRAY_A );

		if ( ! $action ) {
			return false;
		}

		return apply_filters( 'sg_extra_action_from_db', $action, $this );
	}

	/**
	 * Get the subscriber ID.
	 *
	 * @return int
	 */
	public function get_subscriber_id() {
		return $this->subscriber_id;
	}

	/**
	 * This is the value that we will show on the Entries table.
	 */
	public function get_value_for_entries_table( $data ) {
		return '';
	}

	/**
	 * Get Locked
	 *
	 * @return bool
	 */
	public function get_locked() {
		return $this->locked;
	}

	/**
	 * Get Mandatory
	 *
	 * @return int
	 */
	public function get_mandatory() {
		$data      = $this->get_admin_data();
		$mandatory = isset( $data['mandatory'] ) ? absint( $data['mandatory'] ) : 0;
		return apply_filters( 'giveasap_extra_action_get_mandatory', $mandatory, $this );
	}

	/******************
	 * Setters
	 ******************/

	/**
	 * Set the entries.
	 *
	 * @param integer $entries
	 */
	public function set_entries( $entries ) {
		$this->entries = $entries;
	}

	/**
	 * Set the title.
	 *
	 * @param $title
	 */
	public function set_title( $title ) {
		$this->title = $title;
	}

	/**
	 * Set the instance ID.
	 *
	 * @param $id
	 */
	public function set_instance_id( $id ) {
		$this->instance_id = absint( $id );
	}

	/**
	 * Set the Giveaway ID.
	 *
	 * @param $id
	 */
	public function set_giveaway_id( $id ) {
		$this->giveaway_id = $id;
	}

	/**
	 * Set admin data
	 */
	public function set_admin_data( $data ) {
		$this->instance_id = isset( $data['instance_id'] ) ? absint( $data['instance_id'] ) : 0;
		$this->admin_data  = $data;
	}

	/**
	 * @param $giveaway_id
	 */
	public function set_admin_data_from_giveaway( $giveaway_id ) {
		if ( $giveaway_id ) {
			$actions = giveasap_get_extra_actions( $giveaway_id );
			if ( $actions ) {
				foreach ( $actions as $action ) {
					if ( absint( $this->get_instance_id() ) === absint( $action['instance_id'] ) ) {
						$this->set_admin_data( $action );
						return true;
					}
				}
			}
		}
		return false;
	}

	/**
	 * Set the subscriber ID.
	 *
	 * @param $id
	 */
	public function set_subscriber_id( $id ) {
		$this->subscriber_id = absint( $id );
	}

	/**
	 * @param bool $locked
	 */
	public function set_locked( $locked = true ) {
		$this->locked = $locked;
	}
}
