<?php
/**
 * Wishlists function class
 *
 * @since             1.5.0
 * @package           TInvWishlist\Wishlists
 */

// If this file is called directly, abort.
if ( ! defined( 'ABSPATH' ) ) {
	die;
}

/**
 * Wishlists function class
 */
class TInvWL_Wishlist {

	/**
	 * Table name
	 *
	 * @var string
	 */
	private $table;
	/**
	 * Plugin name
	 *
	 * @var string
	 */
	private $_name;
	/**
	 * User id
	 *
	 * @var integer
	 */
	public $user;
	/**
	 * Default share key for wishlist
	 *
	 * @var string
	 */
	public static $default_sharekey;
	/**
	 * Default name wishlist
	 *
	 * @var string
	 */
	private $default_name;
	/**
	 * Default privacy wishlist
	 *
	 * @var string
	 */
	private $default_privacy;
	/**
	 * Allowed privacy
	 *
	 * @var array
	 */
	private $privacy;

	/**
	 * Constructor
	 *
	 * @param string $plugin_name Plugin name.
	 *
	 * @global wpdb $wpdb
	 *
	 */
	function __construct( $plugin_name = TINVWL_PREFIX ) {
		global $wpdb;

		$this->_name           = $plugin_name;
		$this->table           = sprintf( '%s%s_%s', $wpdb->prefix, $this->_name, 'lists' );
		$this->default_name    = apply_filters( 'tinvwl_default_wishlist_title', tinv_get_option( 'general', 'default_title' ) );
		$this->default_privacy = tinv_get_option( 'general', 'default_privacy' );
		$this->privacy         = array( 'public', 'share', 'private' );
		if ( ! in_array( $this->default_privacy, $this->privacy ) ) { // @codingStandardsIgnoreLine WordPress.PHP.StrictInArray.MissingTrueStrict
			$this->default_privacy = 'share';
		}
		$this->user = get_current_user_id();
	}

	/**
	 * Generate unique share key
	 *
	 * @return string
	 * @global wpdb $wpdb
	 */
	function unique_share_key() {
		global $wpdb;

		$share_key = substr( md5( date( 'r' ) . mt_rand( 0, 3000 ) ), 0, 6 );
		$unique    = false;
		while ( $unique === false ) {
			$unique    = ! $wpdb->get_var( $wpdb->prepare( "SELECT `ID` FROM `{$this->table}` WHERE `share_key` = %s", $share_key ) );
			$share_key = substr( md5( date( 'r' ) . mt_rand( 0, 3000 ) ), 0, 6 );
		}

		return $share_key;
	}

	/**
	 * Get/Create default wishlist
	 *
	 * @param integer $user_id Can put 0.
	 *
	 * @return boolean|array
	 */
	function add_user_default( $user_id = 0 ) {
		$user_id = absint( $user_id );
		if ( empty( $user_id ) ) {
			$user_id = $this->user;
		}
		if ( empty( $user_id ) ) {
			return $this->add_sharekey_default();
		}

		if ( ! current_user_can( 'tinvwl_general_settings' ) && ( $user_id != get_current_user_id() ) ) {
			return false;
		}

		if ( $wl = $this->get_by_user_default( $user_id ) ) {
			return array_shift( $wl );
		}

		$wl = $this->add( '', 'default', $this->default_privacy, $user_id );
		if ( is_array( $wl ) && array_key_exists( 'share_key', $wl ) ) {
			$this->set_sharekey( $wl['share_key'] );

			$notifications_meta = TInvWL_Public_Notifications::get_default_meta();
			TInvWL_Public_Notifications::save( $notifications_meta );

			return $wl;
		}

		return false;
	}

	/**
	 * Get/Create default wishlist
	 *
	 * @param string $sharekey Can put empty.
	 *
	 * @return boolean|array
	 */
	function add_sharekey_default( $sharekey = '' ) {
		if ( $wl = $this->get_by_sharekey_default( $sharekey ) ) {
			return array_shift( $wl );
		}

		$wl = $this->add( $this->default_name, 'default', $this->default_privacy, 0 );
		if ( is_array( $wl ) && array_key_exists( 'share_key', $wl ) ) {
			$this->set_sharekey( $wl['share_key'] );

			return $wl;
		}

		return false;
	}

	/**
	 * Add wishlist
	 *
	 * @param mixed $data wishlist name or object.
	 * @param string $type List or default.
	 * @param string $status Public, Share, Private.
	 * @param integer $user_id Can put 0.
	 *
	 * @return boolean
	 * @global wpdb $wpdb
	 *
	 */
	function add( $data, $type = 'list', $status = 'public', $user_id = 0 ) {
		$user_id = absint( $user_id );
		if ( empty( $user_id ) ) {
			$user_id = $this->user;
		}

		$default = array(
			'author'    => $user_id,
			'date'      => current_time( 'Y-m-d H:i:s' ),
			'status'    => $this->default_privacy,
			'share_key' => $this->unique_share_key(),
			'title'     => $this->default_name,
			'type'      => 'list',
		);

		if ( ! is_array( $data ) ) {
			$data = array(
				'title'  => $data,
				'status' => $status,
				'type'   => $type,
			);
		}
		$data = wp_parse_args( $data, $default );
		$data = apply_filters( 'tinvwl_wishlist_add', $data );
		if ( ! in_array( $data['status'], $this->privacy ) ) { // @codingStandardsIgnoreLine WordPress.PHP.StrictInArray.MissingTrueStrict
			$data['status'] = 'public';
		}
		global $wpdb;
		if ( $wpdb->insert( $this->table, $data ) ) { // @codingStandardsIgnoreLine WordPress.VIP.DirectDatabaseQuery.DirectQuery
			$data['ID'] = $wpdb->insert_id;

			/* Run a 3rd party code when a new wishlist created.
			 *
			 * @param array $data A wishlist data.
			 * */
			do_action( 'tinvwl_wishlist_created', $data );

			return $data;
		}

		return false;
	}

	/**
	 * Get default wishlist for user id
	 *
	 * @param integer $user_id Can put 0.
	 *
	 * @return array
	 */
	function get_by_user_default( $user_id = 0 ) {
		$user_id = absint( $user_id );
		if ( empty( $user_id ) ) {
			$user_id = $this->user;
		}

		$data = array(
			'author' => $user_id,
			'type'   => 'default',
			'count'  => 99999,
		);

		if ( ! current_user_can( 'tinvwl_general_settings' ) && ( empty( $this->user ) || ( $data['author'] != $this->user ) ) ) { // WPCS: loose comparison ok.
			$data['status'] = 'public';
		}

		return $this->get( $data );
	}

	/**
	 * Get default wishlist for user id
	 *
	 * @param string $sharekey Can put empty.
	 *
	 * @return array
	 */
	function get_by_sharekey_default( $sharekey = '' ) {
		if ( empty( $sharekey ) ) {
			$sharekey = $this->get_sharekey();
		}
		//validate sharekey
		if ( empty( $sharekey ) || ! is_string( $sharekey ) || ! preg_match( '/[a-f0-9]{6}/i', $sharekey ) ) {
			return array();
		}

		$data = array(
			'share_key' => $sharekey,
			'type'      => 'default',
		);

		return $this->get( $data );
	}

	/**
	 * Get wishlist for user id
	 *
	 * @param integer $user_id Can put 0.
	 * @param array $data Requset.
	 *
	 * @return array
	 */
	function get_by_user( $user_id = 0, $data = array() ) {
		$user_id = absint( $user_id );
		if ( empty( $user_id ) ) {
			$user_id = $this->user;
		}
		$this->add_user_default( $user_id );
		$_data = array(
			'author' => $user_id,
			'count'  => 99999,
		);

		if ( ! current_user_can( 'tinvwl_general_settings' ) && ( empty( $this->user ) || ( $_data['author'] != $this->user ) ) ) { // WPCS: loose comparison ok.
			$_data['status'] = 'public';
		}
		$data = tinv_array_merge( $data, $_data );

		return $this->get( $data );
	}

	/**
	 * Get wishlist by id
	 *
	 * @param integer $id id database wishlist.
	 *
	 * @return array|bool
	 */
	function get_by_id( $id ) {
		$id = absint( $id );
		if ( empty( $id ) ) {
			return false;
		}

		$wishlists = $this->get( array( 'ID' => $id ) );
		$wishlist  = array_shift( $wishlists );

		if ( is_array( $wishlist ) ) {
			$wishlist['is_owner'] = false;
			if ( is_user_logged_in() ) {
				$wishlist['is_owner'] = get_current_user_id() == $wishlist['author']; // WPCS: loose comparison ok.
			} else {
				$wishlist['is_owner'] = $this->get_sharekey() === $wishlist['share_key']; // WPCS: loose comparison ok.
			}
		}

		return $wishlist;
	}

	/**
	 * Get wishlist by share key
	 *
	 * @param string $share_key Share key.
	 *
	 * @return array
	 */
	function get_by_share_key( $share_key ) {
		if ( empty( $share_key ) || ! is_string( $share_key ) || ! preg_match( '/[a-f0-9]{6}/i', $share_key ) ) {
			return array();
		}
		$wishlists = $this->get( array( 'share_key' => $share_key ) );
		$wishlist  = array_shift( $wishlists );
		if ( is_array( $wishlist ) ) {
			$wishlist['is_owner'] = false;
			if ( is_user_logged_in() ) {
				$wishlist['is_owner'] = get_current_user_id() == $wishlist['author']; // WPCS: loose comparison ok.
			} else {
				$wishlist['is_owner'] = $this->get_sharekey() === $wishlist['share_key']; // WPCS: loose comparison ok.
			}
		}

		return $wishlist;
	}

	/**
	 * Get wishlist
	 *
	 * @param array $data Requset.
	 *
	 * @return array
	 * @global wpdb $wpdb
	 *
	 */
	function get( $data = array() ) {
		global $wpdb;

		$default = array(
			'count'     => 10,
			'field'     => null,
			'field_raw' => null,
			'offset'    => 0,
			'order'     => 'ASC',
			'order_by'  => 'title',
			'sql'       => '',
		);

		foreach ( $default as $_k => $_v ) {
			if ( array_key_exists( $_k, $data ) ) {
				$default[ $_k ] = $data[ $_k ];
				unset( $data[ $_k ] );
			}
		}

		//proper sanitizing
		$default['offset'] = absint( $default['offset'] );
		$default['count']  = absint( $default['count'] );
		//the order value is passed directly to the db so it needs to be protected against sql_injections
		$valid_order_values = array( 'ASC', 'DESC' );
		if ( ! in_array( strtoupper( $default['order'] ), $valid_order_values, true ) ) {
			$default['order'] = 'ASC';
		}

		if ( is_array( $default['field'] ) ) {
			$default['field'] = '`' . implode( '`,`', $default['field'] ) . '`';
		} elseif ( is_string( $default['field'] ) ) {
			$default['field'] = array( 'ID', 'type', $default['field'] );
			$default['field'] = '`' . implode( '`,`', $default['field'] ) . '`';
		} else {
			$default['field'] = '*';
		}
		if ( is_array( $default['field_raw'] ) ) {
			$default['field'] = implode( ',', $default['field_raw'] );
		}

		$sql = "SELECT {$default[ 'field' ]} FROM `{$this->table}`";

		$where = '1';
		if ( ! empty( $data ) && is_array( $data ) ) {
			foreach ( $data as $f => $v ) {
				$s = is_array( $v ) ? ' IN ' : '=';
				if ( is_array( $v ) ) {
					foreach ( $v as $_f => $_v ) {
						$v[ $_f ] = $wpdb->prepare( '%s', $_v );
					}
					$v = implode( ',', $v );
					$v = "($v)";
				} else {
					$v = $wpdb->prepare( '%s', $v );
				}
				$data[ $f ] = sprintf( '`%s`%s%s', $f, $s, $v );
			}
			$where = ' WHERE ' . implode( ' AND ', $data );
			$sql   .= $where;
		}
		$sql .= sprintf( ' ORDER BY `%s` %s LIMIT %d,%d;', $default['order_by'], $default['order'], $default['offset'], $default['count'] );

		if ( ! empty( $default['sql'] ) ) {
			$replacer    = $replace = array();
			$replace[0]  = '{table}';
			$replacer[0] = $this->table;
			$replace[1]  = '{where}';
			$replacer[1] = $where;

			foreach ( $default as $key => $value ) {
				$i = count( $replace );

				$replace[ $i ]  = '{' . $key . '}';
				$replacer[ $i ] = $value;
			}

			$sql = str_replace( $replace, $replacer, $default['sql'] );
		}

		$wls = $wpdb->get_results( $sql, ARRAY_A ); // WPCS: db call ok; no-cache ok; unprepared SQL ok.

		if ( empty( $wls ) ) {
			return array();
		}

		foreach ( $wls as $k => $wl ) {
			$wl['ID'] = absint( $wl['ID'] );
			if ( array_key_exists( 'author', $wl ) ) {
				$wl['author'] = absint( $wl['author'] );
			}
			if ( 'default' === $wl['type'] && empty( $wl['title'] ) ) {
				$wl['title'] = $this->default_name;
			}

			if ( is_array( $wl ) ) {
				if ( is_user_logged_in() ) {
					$wl['is_owner'] = get_current_user_id() == $wl['author']; // WPCS: loose comparison ok.
				} else {
					$wl['is_owner'] = $this->get_sharekey() === $wl['share_key']; // WPCS: loose comparison ok.
				}
			}

			$wls[ $k ] = apply_filters( 'tinvwl_wishlist_get', $wl );
		}

		return $wls;
	}

	/**
	 * Update wishlist
	 *
	 * @param integer $id id database wishlist.
	 * @param mixed $data wishlist name or object.
	 * @param string $type List or default.
	 * @param string $status Public, Share, Private.
	 *
	 * @return boolean
	 * @global wpdb $wpdb
	 *
	 */
	function update( $id, $data, $type = 'list', $status = 'public' ) {
		global $wpdb;

		$id = absint( $id );

		if ( ! is_array( $data ) ) {
			$data = array(
				'title'  => $data,
				'status' => $status,
				'type'   => $type,
			);
		}
		$data = filter_var_array( $data, apply_filters( 'tinvwl_wishlist_fields_update', array(
			'title'  => FILTER_SANITIZE_FULL_SPECIAL_CHARS,
			'status' => FILTER_SANITIZE_FULL_SPECIAL_CHARS,
			'type'   => FILTER_SANITIZE_FULL_SPECIAL_CHARS,
			'author' => FILTER_VALIDATE_INT,
		) ) );
		$data = array_filter( $data );
		$data = apply_filters( 'tinvwl_wishlist_update', $data, $id );
		if ( ! array_key_exists( 'title', $data ) ) {
			$wishlist = $this->get_by_id( $id );
			if ( 'default' === $wishlist['type'] ) {
				$data['title'] = '';
			}
		}
		global $wpdb;

		return false !== $wpdb->update( $this->table, $data, array( 'ID' => $id ) );
	}

	/**
	 * Remove wishlist
	 *
	 * @param integer $id id database wishlist.
	 *
	 * @return boolean
	 * @global wpdb $wpdb
	 *
	 */
	public function remove( $id ) {
		$id = absint( $id );
		if ( empty( $id ) ) {
			return false;
		}
		global $wpdb;
		$result = $wpdb->delete( $this->table, array( 'ID' => $id ) );
		if ( false !== $result ) {
			do_action( 'tinvwl_wishlist_removed', $id );
			$wlp = new TInvWL_Product();
			$wlp->remove_product_from_wl( $id );

			return true;
		}

		return false;
	}

	/**
	 * Set share key for default wishlist
	 *
	 * @param string $sharekey Sharekey for default wishlist.
	 *
	 * @return string
	 */
	function set_sharekey( $sharekey = '' ) {
		global $tinvwl_wishlist_sharekey;

		if ( ! empty( $sharekey ) ) {
			self::$default_sharekey = $tinvwl_wishlist_sharekey = $sharekey;
			@setcookie( 'tinv_wishlistkey', self::$default_sharekey, time() + 31 * DAY_IN_SECONDS, COOKIEPATH, COOKIE_DOMAIN );
		}

		return self::$default_sharekey;
	}

	/**
	 * Get variable for default share key
	 *
	 * @return string
	 */
	function get_sharekey() {
		global $tinvwl_wishlist_sharekey;

		if ( empty( self::$default_sharekey ) ) {
			self::$default_sharekey = $tinvwl_wishlist_sharekey = filter_input( INPUT_COOKIE, 'tinv_wishlistkey', FILTER_VALIDATE_REGEXP, array(
				'options' => array(
					'regexp'  => '/^[A-Fa-f0-9]{6}$/',
					'default' => $tinvwl_wishlist_sharekey,
				),
			) );
		}

		return self::$default_sharekey;
	}
}
