<?php

// Exit if accessed directly
if ( ! defined( 'ABSPATH' ) ) exit;


function datebook_get_subscription_plan( $id_or_post ) {
	return new DateBook_Subscription_Plan( $id_or_post );
}


function datebook_get_subscription_plans_group_parent_id( $subscription_plan_id ) {

	$ancestors_ids = get_post_ancestors( $subscription_plan_id );

	if( !empty( $ancestors_ids ) )
		$top_parent_id = $ancestors_ids[ count( $ancestors_ids ) - 1 ];
	else
		$top_parent_id = $subscription_plan_id;

	return $top_parent_id;

}


function datebook_get_subscription_plans_group( $subscription_plan_id, $only_active = true, $ascending = false ) {

	$top_parent_id = datebook_get_subscription_plans_group_parent_id( $subscription_plan_id );

	// Add top most parent
	$subscription_plan_posts[] = get_post( $top_parent_id );

	// Add all the children in the group
	while( ( $subscription_plan_downgrade = get_posts( array('post_type' => 'datebook-subscribe', 'numberposts' => -1, 'post_parent' => $top_parent_id, 'order' => 'DESC', 'orderby' => 'parent', 'post_status' => 'any' ) ) ) != null ) {

		$top_parent_id = $subscription_plan_downgrade[0]->ID;
		$subscription_plan_posts[] = $subscription_plan_downgrade[0];

	}

	$subscription_plans = array();

	if( !empty( $subscription_plan_posts ) ) {
		foreach( $subscription_plan_posts as $subscription_plan_post ) {
			$subscription_plan = datebook_get_subscription_plan( $subscription_plan_post );

			if( $only_active && !$subscription_plan->is_active() )
				continue;

			$subscription_plans[] = $subscription_plan;
		}
	}

	if( $ascending == true )
		$subscription_plans = array_reverse( $subscription_plans );

	return $subscription_plans;

}



/**
  * Returns all subscription plans into an array of objects
  *
  * @param $only_active   - true to return only active subscription plans, false to return all
  *
  * @return array
  *
  */
function datebook_get_subscription_plans( $only_active = true, $include = array() ) {

	$subscription_plans = array();
	$subscription_plan_post_ids = array();

	if( empty( $include ) ) {

		$subscription_plan_posts = get_posts( array('post_type' => 'datebook-subscription', 'numberposts' => -1, 'post_status' => 'any' ) );

		$page_hierarchy_posts = get_page_hierarchy( $subscription_plan_posts );

		foreach($page_hierarchy_posts as $post_id => $post_name){
			$subscription_plan_post_ids[] = $post_id;
		}

	} else {

		$subscription_plan_posts = get_posts( array('post_type' => 'datebook-subscription', 'numberposts' => -1, 'include' => $include, 'orderby' => 'post__in', 'post_status' => 'any' ) );

		$subscription_plan_post_ids = $subscription_plan_posts;

	}

	// Return if we don't have any plans by now
	if( empty( $subscription_plan_post_ids ) ) return $subscription_plans;

	foreach( $subscription_plan_post_ids as $subscription_plan_post_id ) {

		$subscription_plan = datebook_get_subscription_plan( $subscription_plan_post_id );

		if( $only_active && !$subscription_plan->is_active() ) continue;

		$subscription_plans[] = $subscription_plan;

	}

	return apply_filters( 'datebook_get_subscription_plans', $subscription_plans, $only_active );

}


/**
 * Returns an array with member subscriptions based on the given arguments
 *
 * @param array $args
 *
 * @return array
 *
 */
function datebook_get_member_subscriptions( $args = array() ) {

	global $wpdb;
	
	$table_name = strtolower($wpdb->prefix) . 'datebook_member_subscriptions';

	$defaults = array(
        'order'         		      => 'DESC',
        'orderby'       		      => 'id',
        'number'        		      => 1000,
        'offset'        	   	      => '',
        'status'        	   	      => '',
		'payment_gateway'			  => '',
        'user_id'       	   	      => '',
        'subscription_plan_id' 	      => '',
        'start_date'                  => '',
        'start_date_after'            => '',
        'start_date_before'           => '',
        'expiration_date'             => '',
        'expiration_date_after'       => '',
        'expiration_date_before'      => '',
        'billing_next_payment'        => '',
        'billing_next_payment_after'  => '',
        'billing_next_payment_before' => ''
    );

	$args = wp_parse_args( $args, $defaults );

	// Start query string
    $query_string = "SELECT * ";

    $query_from   = "FROM {$table_name} ";
    $query_where  = "WHERE 1=%d ";


    // Filter by user id
    if( !empty( $args['user_id'] ) ) {

        $user_id      = absint( $args['user_id'] );
        $query_where .= " AND user_id LIKE '{$user_id}'";

    }

    // Filter by status
    if( !empty( $args['status'] ) ) {

        $status       = sanitize_text_field( $args['status'] );
        $query_where .= " AND status LIKE '{$status}'";

    }

    // Filter by payment_gateway
    if( !empty( $args['payment_gateway'] ) ) {

        $payment_gateway = sanitize_text_field( $args['payment_gateway'] );
        $query_where .= " AND payment_gateway LIKE '{$payment_gateway}'";

    }

    // Filter by start date
    if( ! empty( $args['start_date'] ) ) {

        $query_where .= " AND start_date LIKE '%%{$args['start_date']}%%'";

    }

    // Filter by start date after
    if( ! empty( $args['start_date_after'] ) ) {

        $query_where .= " AND start_date > '{$args['start_date_after']}'";

    }

    // Filter by start date before
    if( ! empty( $args['start_date_before'] ) ) {

        $query_where .= " AND start_date < '{$args['start_date_before']}'";

    }

    // Filter by expiration date
    if( ! empty( $args['expiration_date'] ) ) {

        $query_where .= " AND expiration_date LIKE '%%{$args['expiration_date']}%%'";

    }

    // Filter by expiration date after
    if( ! empty( $args['expiration_date_after'] ) ) {

        $query_where .= " AND expiration_date > '{$args['expiration_date_after']}'";

    }

    // Filter by expiration date before
    if( ! empty( $args['expiration_date_before'] ) ) {

        $query_where .= " AND expiration_date < '{$args['expiration_date_before']}'";

    }

    // Filter by billing next payment date
    if( ! empty( $args['billing_next_payment'] ) ) {

        $query_where .= " AND billing_next_payment LIKE '%%{$args['billing_next_payment']}%%'";

    }

    // Filter by billing next date payment after
    if( ! empty( $args['billing_next_payment_after'] ) ) {

        $query_where .= " AND billing_next_payment > '{$args['billing_next_payment_after']}'";

    }

    // Filter by billing next payment date before
    if( ! empty( $args['billing_next_payment_before'] ) ) {

        $query_where .= " AND billing_next_payment < '{$args['billing_next_payment_before']}'";

    }

    // Query order by
    $query_order_by = '';

    if ( ! empty($args['orderby']) ) {

        $query_order_by = " ORDER BY " . trim( $args['orderby'] ) . ' ';

    }

    // Query order
    $query_order = $args['order'] . ' ';

    // Query limit
    $query_limit = '';

    if( ! empty( $args['number'] ) ) {

        $query_limit = 'LIMIT ' . (int)trim( $args['number'] ) . ' ';

    }

    // Query offset
    $query_offset = '';

    if( ! empty( $args['offset'] ) ) {

        $query_offset = 'OFFSET ' . (int)trim( $args['offset'] ) . ' ';

    }


    $query_string .= $query_from . $query_where . $query_order_by . $query_order . $query_limit . $query_offset;

	$data_array = $wpdb->get_results( $wpdb->prepare( $query_string, 1 ), ARRAY_A );

	$subscriptions = array();

	foreach( $data_array as $key => $data ) {

		$subscriptions[$key] = new DateBook_Member_Subscription( $data );
		
 	}

 	/**
     * Filter member subscriptions just before returning them
     *
     * @param array $subscriptions - the array of returned member subscriptions from the db
     * @param array $args     	   - the arguments used to query the member subscriptions from the db
     *
     */
    $subscriptions = apply_filters( 'datebook_get_member_subscriptions', $subscriptions, $args );

	return $subscriptions;

}


/**
 * Returns a member subscription object from the database by the given id
 * or null if no subscription is found
 * 
 * @param int $member_subscription_id
 *
 * @return mixed
 *
 */
function datebook_get_member_subscription( $member_subscription_id = 0 ) {

	global $wpdb;
	
	$table_name = strtolower($wpdb->prefix) . 'datebook_member_subscriptions';

	$result = $wpdb->get_row( "SELECT * FROM {$table_name} WHERE id = {$member_subscription_id}", ARRAY_A );

	if(!is_null($result)){
		$result = new DateBook_Member_Subscription($result);
	}

	return $result;

}


/**
 * Function that returns all available member subscription statuses
 *
 * @return array
 *
 */
function datebook_get_member_subscription_statuses() {

    $statuses = array(
        'active'    => esc_html__( 'Active', 'datebook' ),
        'canceled'  => esc_html__( 'Canceled', 'datebook' ),
        'expired'   => esc_html__( 'Expired', 'datebook' ),
        'pending'   => esc_html__( 'Pending', 'datebook' )
    );

    /**
     * Filter to add/remove member subscription statuses
     *
     * @param array $statuses
     *
     */
    $statuses = apply_filters( 'datebook_member_subscription_statuses', $statuses );
    
    return $statuses;

}


/**
 * Returns the metadata for a given member subscription
 *
 * @param int    $member_subscription_id
 * @param string $meta_key
 * @param bool   $single
 *
 * @return mixed - single metadata value | array of values
 *
 */
function datebook_get_member_subscription_meta( $member_subscription_id = 0, $meta_key = '', $single = false ) {

    return get_metadata( 'member_subscription', $member_subscription_id, $meta_key, $single );

}


/**
 * Adds the metadata for a member subscription
 *
 * @param int    $member_subscription_id
 * @param string $meta_key
 * @param string $meta_value
 * @param bool   $unique
 *
 * @return mixed - int | false
 *
 */
function datebook_add_member_subscription_meta( $member_subscription_id = 0, $meta_key = '', $meta_value = '', $unique = false ) {

    return add_metadata( 'member_subscription', $member_subscription_id, $meta_key, $meta_value, $unique );

}


/**
 * Updates the metadata for a member subscription
 *
 * @param int    $member_subscription_id
 * @param string $meta_key
 * @param string $meta_value
 * @param string $prev_value
 *
 * @return bool
 *
 */
function datebook_update_member_subscription_meta( $member_subscription_id = 0, $meta_key = '', $meta_value = '', $prev_value = '' ) {

    return update_metadata( 'member_subscription', $member_subscription_id, $meta_key, $meta_value, $prev_value );

}


/**
 * Deletes the metadata for a member subscription
 *
 * @param int    $member_subscription_id
 * @param string $meta_key
 * @param string $meta_value
 * @param string $delete_all - If true, delete matching metadata entries for all member subscriptions, ignoring 
 *                             the specified member_subscription_id. Otherwise, only delete matching metadata 
 *                             entries for the specified member_subscription_id.
 *
 */
function datebook_delete_member_subscription_meta( $member_subscription_id = 0, $meta_key = '', $meta_value = '', $delete_all = false ) {

    return delete_metadata( 'member_subscription', $member_subscription_id, $meta_key, $meta_value, $delete_all );

}


/**
 * Cancels all member subscriptions for a user when the user is deleted
 *
 * @param int $user_id
 *
 */
function datebook_member_delete_user_subscription_cancel( $user_id = 0 ) {

    if( empty( $user_id ) )
        return;

    $member_subscriptions = datebook_get_member_subscriptions( array( 'user_id' => (int)$user_id ) );

    if( empty( $member_subscriptions ) )
        return;

    foreach( $member_subscriptions as $member_subscription ) {

        if( $member_subscription->status == 'active' || $member_subscription->status == 'completed' ) {

            $member_subscription->update( array( 'status' => 'canceled' ) );

        }

    }

}


/**
 * Function triggered by the cron job that checks for any expired subscriptions.
 *
 * Note 1: This function has been refactored due to slow performance. It would take all members and then
 *         for each one of the subscription it would check to see if it was expired and if so, set the status
 *         to expired.
 * Note 2: The function now gets all active subscriptions without using the DateBook_Member_Class class and checks to see
 *         if they have passed their expiration time and if so, sets the status to expire. Due to the fact that
 *         the DateBook_Member_Class class is not used, the "datebook_member_update_subscription" had to be added here also to
 *         deal with further actions set on the hook 
 *
 * @return void
 *
 */
function datebook_member_check_expired_subscriptions() {

    global $wpdb;
	
	$table_name = strtolower($wpdb->prefix) . 'datebook_member_subscriptions';

    $subscriptions = $wpdb->get_results( "SELECT * FROM {$table_name} WHERE ( status = 'active' OR status = 'canceled' ) AND expiration_date > '0000-00-00 00:00:00' AND expiration_date < DATE_SUB( NOW(), INTERVAL 12 HOUR )", ARRAY_A );

    if( empty( $subscriptions ) )
        return;

    foreach( $subscriptions as $subscription ) {
        
        $update_result = $wpdb->update( $table_name, array( 'status' => 'expired' ), array( 'user_id' => $subscription['user_id'], 'subscription_plan_id' => $subscription['subscription_plan_id'] ) );

        // Can return 0 if no data was changed
        if( $update_result !== false )
            $update_result = true;

        if( $update_result ) {

            /**
             * Fires right after the Member Subscription db entry was updated
             *
             * This action is the same as the one in the "update" method in DateBook_Member_Subscription class
             *
             * @param int   $id            - the id of the subscription that has been updated
             * @param array $data          - the array of values to be updated for the subscription
             * @param array $old_data      - the array of values representing the subscription before the update
             *
             */
            do_action( 'datebook_member_subscription_update', $subscription['id'], array( 'status' => 'expired' ), $subscription );


            /**
             * Action to do something after a subscription update.
             *
             * This action is the same as the one in the "update" method in DateBook_Member_Subscription class
             *
             * @param int   $id            - the id of the subscription that has been updated
             * @param array $data          - the array of values to be updated for the subscription
             * @param array $old_data      - the array of values representing the subscription before the update
             *
             */
            do_action( 'datebook_member_subscription_updated', $subscription['id'], array( 'status' => 'expired' ), $subscription );

        }

        /**
         * Deprecated action to do something after a subscription update.
         *
         * @deprecated
         *
         * This action is the same as the one in the "update_subscription" method in DateBook_Member_Class class
         *
         */
        do_action( 'datebook_member_update_subscription', $update_result, $subscription['user_id'], $subscription['subscription_plan_id'], $subscription['start_date'], $subscription['expiration_date'], 'expired' );

    }

}



class DateBook_Member_Subscription {
	
	public $id = 0;

	public $user_id = 0;

	public $subscription_plan_id = 0;

	public $plan_price_period;

	public $start_date;

	public $expiration_date;

	public $status;

	public $payment_profile_id;

	public $payment_gateway;

	public $billing_amount;

	public $billing_duration;

	public $billing_duration_unit;

	public $billing_cycles;

	public $billing_next_payment;

	public $billing_last_payment;

	public $trial_end;


	/**
	 * Construct
	 *
	 * @param array $data - the subscription data
	 *
	 */
	public function __construct( $data = array() ) {

		$this->set_instance( $data );

	}


	/**
	 * Sets the values of the object properties to the provided data
	 *
	 * @param array $data - the subscription data
	 *
	 */
	public function set_instance( $data = array() ) {

		// Grab all properties and populate them
        foreach( get_object_vars( $this ) as $property => $value ) {

            if( isset( $data[$property] ) ) {

            	// Empty dates overwrite
            	if( $data[$property] == '0000-00-00 00:00:00' )
            		$data[$property] = '';

                $this->$property = $data[$property];

            }

        }

	}


	/**
	 * Clears the instance data
	 *
	 */
	public function clear_instance() {

		foreach( get_class_vars( __CLASS__ ) as $property => $value ) {
			$this->$property = $value;
		}
		

	}


	/**
	 * Inserts a new member subscription into the database
	 *
	 * @param array $data - the array of data for the member subscription
	 *
	 * @return mixed int|false
	 *
	 */
	public function insert( $data = array() ) {

		global $wpdb;
		
		$table_name = strtolower($wpdb->prefix) . 'datebook_member_subscriptions';

		// Clean the data array
        $data = $this->sanitize_data( $data );

        // Insert member subscription
		$insert_result = $wpdb->insert( $table_name, $data );

		if( $insert_result ) {

            // Populate current object
            $this->id = $wpdb->insert_id;
            $this->set_instance( $data );


            return $this->id;

        }

        return false;

	}


	/**
	 * Updates an existing member subscription with the new provided data
	 *
	 * @param array $data - the new datas to be updated for the member subscription
	 *
	 * @return bool
	 *
	 */
	public function update( $data = array() ) {

		global $wpdb;
		
		$table_name = strtolower($wpdb->prefix) . 'datebook_member_subscriptions';

		// Clean the data array
		$data = $this->sanitize_data( $data );

		// We don't want the id to be updated
		if( isset( $data['id'] ) )
			unset( $data['id'] );

		// Update the member subscription
		$update_result = $wpdb->update( $table_name, $data, array( 'id' => $this->id ) );

		// Can return 0 if no rows are affected
        if( $update_result !== false )
            $update_result = true;

        
		if( $update_result ) {

			/**
			 * Fires right after the Member Subscription db entry was updated
			 *
			 * @param int 	$id 		   - the id of the subscription that has been updated
			 * @param array $data 		   - the array of values to be updated for the subscription
			 * @param array $old_data 	   - the array of values representing the subscription before the update
			 *
			 */
			do_action( 'datebook_member_subscription_update', $this->id, $data, $this->to_array() );


			/**
			 * Fires right after the Member Subscription db entry was updated
			 *
			 * @param int 	$id 		   - the id of the subscription that has been updated
			 * @param array $data 		   - the array of values to be updated for the subscription
			 * @param array $old_data 	   - the array of values representing the subscription before the update
			 *
			 */
			do_action( 'datebook_member_subscription_updated', $this->id, $data, $this->to_array() );


			// Update the current instance with the new data values
			$this->set_instance( $data );

		}

		/**
		 * Compatibility hook from class DateBook_Member_Class in update_subscription() method
		 *
		 * @param bool   $update_result			- the result of the database update
		 * @param int  	 $user_id				- the id of the user attached to this subscription
		 * @param int  	 $subscription_plan_id  - the id of the subscription plan attached to this subscription
		 * @param string $start_date   			- the start date of the subscription
		 * @param string $expiration_date 		- the expiration date of the subscription
		 * @param string $status 				- the status of the subscription
		 *
		 */
		do_action( 'datebook_member_update_subscription', $update_result, $this->user_id, $this->subscription_plan_id, $this->start_date, $this->expiration_date, $this->status );

		return $update_result;

	}
	

	/**
	 * Removes the current member subscription from the database
	 *
	 * @return bool
	 *
	 */
	public function remove() {

		global $wpdb;
		
		$table_name = strtolower($wpdb->prefix) . 'datebook_member_subscriptions';

		$delete_result = $wpdb->delete( $table_name, array( 'id' => $this->id ) );

		// Can return 0 if no rows are affected
        if( $delete_result !== false )
            $delete_result = true;

        if( $delete_result ) {

			$table_member_subscriptionmeta = strtolower($wpdb->prefix) . 'datebook_member_subscriptionmeta';

        	/**
        	 * Remove all meta data
        	 *
        	 */
        	$meta_delete_result = $wpdb->delete( $table_member_subscriptionmeta, array( 'member_subscription_id' => $this->id ) );

        	/**
	         * Fires right after a member subscription has been deleted
	         *
	         * @param int   $id   	  - the id of the member subscription that has just been deleted from the db
	         * @param array $old_data - the data the subscription had at the moment of deletion
	         *
	         */
	        do_action( 'datebook_member_subscription_delete', $this->id, $this->to_array() );


	        /**
		     * Fires right after a member subscription has been deleted
		     *
		     * @param int   $id   	  - the id of the member subscription that has just been deleted from the db
		     * @param array $old_data - the data the subscription had at the moment of deletion
		     *
		     */
		    do_action( 'datebook_member_subscription_deleted', $this->id, $this->to_array() );


	        // Clear the current object instance
	        $this->clear_instance();

        }

        return $delete_result;

	}


	/**
	 * Verifies if the current subscription is auto renewing
	 * What this means is that it either has a subscription equivalent in one of the payment gateways
	 * or that it has a renewal schedule set in the database for it
	 *
	 * @return bool
	 *
	 */
	public function is_auto_renewing() {

		if( $this->status == 'expired' || $this->status == 'canceled' )
			return false;

		if( ! empty( $this->payment_profile_id ) )
			return true;
		
		if( ( ! empty( $this->billing_duration ) && ! empty( $this->billing_duration_unit ) ) )
			return true;

		return false;

	}


    /**
     * Checks to see if the current subscription is in its trial period or not
     *
     * @return bool
     *
     */
    public function is_trial_period() {

        if( empty( $this->trial_end ) )
            return false;

        if( strtotime( $this->trial_end ) < time() )
            return false;

        return true;

    }


	/**
	 * Eliminate all values from the provided data array that are not a part of the object
	 *
	 * @param array $data
	 *
	 * @return array
	 *
	 */
	private function sanitize_data( $data = array() ) {

		$object_vars = array_keys( get_object_vars( $this ) );

        foreach( $data as $key => $val ) {

            if( !in_array( $key, $object_vars ) )
                unset( $data[$key] );

        }

        return $data;

	}


	/**
	 * Returns the array representation of the current object instance
	 *
	 */
	public function to_array() {

		return get_object_vars( $this );

	}

}



/**
 * Member class stores and handles user data that is specific only for members
 *
 */
class DateBook_Member_Class {

    /**
     * User ID
     *
     * @access public
     * @var int
     */
    public $user_id;

    /**
     * User name
     *
     * @access public
     * @var string
     */
    public $username;

    /**
     * User email
     *
     * @access public
     * @var string
     */
    public $email;

    /**
     * Member subscriptions data
     *
     * @access public
     * @var array
     */
    public $subscriptions;


    /*
     * Constructor
     *
     */
    public function __construct( $user_id = 0 ) {

        add_action( 'datebook_member_before_remove_subscription', array( $this, 'before_remove_subscription_status_cancel' ), 10, 2 );

        $this->init( $user_id );

    }


    /*
     * Initialize method where we set the member data
     *
     * @param $user_id  - id of the user
     *
     */
    public function init( $user_id ) {

        $user_data = get_userdata( $user_id );

        $this->user_id = $user_id;

        if( !$user_data )
            return null;

        // Set member data
        $this->username = $user_data->user_login;
        $this->email = $user_data->user_email;

        // Set subscriptions
        $this->subscriptions = $this->get_subscriptions();

    }


    /*
     * Method that returns the properties as an associative array
     *
     * @return array
     *
     */
    public function to_array() {

        return get_object_vars( $this );

    }


    /*
     * Method that returns an array with the class properties
     *
     * @return array
     *
     */
    public static function get_properties() {

        $properties = array();

        $class_vars = get_class_vars( __CLASS__ );

        foreach( $class_vars as $class_var => $class_var_value ) {

            $properties[] = $class_var;

        }

        return $properties;

    }


    /*
     * Return from the database the subscriptions associated with a user
     *
     * @return array
     *
     */
    public function get_subscriptions() {

        global $wpdb;
		
		$table_name = strtolower($wpdb->prefix) . 'datebook_member_subscriptions';

        $subscriptions = $wpdb->get_results( "SELECT id, subscription_plan_id, plan_price_period, start_date, expiration_date, status, payment_profile_id, payment_gateway FROM {$table_name} WHERE user_id = $this->user_id", ARRAY_A );

        return $subscriptions;

    }


    /*
     * Return an array that contains data about a member subscription
     *
     * @param $subscription_plan_id
     *
     * @return array
     *
     */
    public function get_subscription( $subscription_plan_id ) {

        if( false !== ( $key = array_search( $subscription_plan_id, $this->get_subscriptions_ids() ) ) )
            return $this->subscriptions[$key];
        else
            return array();

    }


    /*
     * Returns the number of subscriptions that a member has
     *
     * @return int
     *
     */
    public function get_subscriptions_count() {

		$count_subscriptions = is_array($this->subscriptions) && !empty($this->subscriptions) ? count( $this->subscriptions ) : 0;

        return $count_subscriptions;

    }


    /*
     * Returns an array with the ids of the subscription plans associated with the member
     *
     * @return array
     *
     */
    public function get_subscriptions_ids() {

        $subscription_ids = array();

        foreach( $this->subscriptions as $subscription )
            $subscription_ids[] = $subscription['subscription_plan_id'];

        return $subscription_ids;

    }


    /*
     * Before removing a subscription from a member, if it is active we're
     * going to first change its status to canceled
     *
     */
    public function before_remove_subscription_status_cancel( $user_id, $subscription_plan_id ) {

        $member_subscription = $this->get_subscription( $subscription_plan_id );

        if( $member_subscription['status'] == 'active' ) {

            $this->update_subscription( $subscription_plan_id, $member_subscription['start_date'], $member_subscription['expiration_date'], 'canceled' );

        }

    }


    /*
     * Removes from the database a member's subscription
     *
     * @param $subscription_plan_id
     *
     * @return bool
     *
     */
    public function remove_subscription( $subscription_plan_id ) {

        global $wpdb;

        do_action( 'datebook_member_before_remove_subscription', $this->user_id, $subscription_plan_id );
		
		$table_member_subscriptions = strtolower($wpdb->prefix) . 'datebook_member_subscriptions';

        $delete_result = $wpdb->delete( $table_member_subscriptions, array( 'user_id' => $this->user_id, 'subscription_plan_id' => $subscription_plan_id ) );

        do_action( 'datebook_member_remove_subscription', $delete_result, $this->user_id, $subscription_plan_id );

        return $delete_result;

    }


    /*
     * Updates in the database the member's subscription data
     *
     * @param $subscription_plan_id
     * @param $start_date
     * @param $expiration_date
     * @param $status
     *
     * @return bool
     *
     */
    public function update_subscription( $subscription_plan_id, $start_date, $expiration_date, $status = 'pending' ) {

        global $wpdb;

        // Automatically update status to correct state ('expired' or 'active') in case $start_date or $expiration_date have been modified
        if ( ( $status != 'canceled') && ($status != 'pending') ) {
            if ( ( $status == 'active' ) && ( ( strtotime($start_date) > strtotime($expiration_date) ) || ( time() > strtotime($expiration_date) ) ) ) $status = 'expired';
            if ( ( $status == 'expired') &&  ( time() < strtotime($expiration_date) ) ) $status = 'active';
        }
		
		$table_member_subscriptions = strtolower($wpdb->prefix) . 'datebook_member_subscriptions';

        $update_result = $wpdb->update( $table_member_subscriptions, array( 'start_date' => $start_date, 'expiration_date' => $expiration_date, 'status' => $status ), array( 'user_id' => $this->user_id, 'subscription_plan_id' => $subscription_plan_id ) );

        // Can return 0 if no data was changed
        if( $update_result !== false )
            $update_result = true;

        do_action( 'datebook_member_update_subscription', $update_result, $this->user_id, $subscription_plan_id, $start_date, $expiration_date, $status );

        return $update_result;

    }


    /*
     * Updates in the database the subscription plan id for a member's subscription
     *
     * @param $old_subscription_plan_id
     * @param $new_subscription_plan_id
     *
     * @return bool
     *
     */
    public function replace_subscription( $old_subscription_plan_id, $new_subscription_plan_id ) {

        global $wpdb;
		
		$table_member_subscriptions = strtolower($wpdb->prefix) . 'datebook_member_subscriptions';

        $update_result = $wpdb->update( $table_member_subscriptions, array( 'subscription_plan_id' => $new_subscription_plan_id ), array( 'user_id' => $this->user_id, 'subscription_plan_id' => $old_subscription_plan_id ) );

        // Can return 0 if no data was changed
        if( $update_result !== false )
            $update_result = true;

        do_action( 'datebook_member_replace_subscription', $update_result, $this->user_id, $new_subscription_plan_id, $old_subscription_plan_id );

        return $update_result;

    }


    /*
     * Add a new subscription in the database for this member
     *
     * @param $subscription_plan_id
     * @param $start_date
     * @param $expiration_date
     * @param $status
     *
     * @return bool
     *
     */
    public function add_subscription( $subscription_plan_id, $start_date, $expiration_date, $status ) {

        global $wpdb;

        // If the start date is set after the expiration date, change status to 'expired'
        if ( ( strtotime($start_date) > strtotime($expiration_date) ) && ( $status == 'active' ) ) $status = 'expired';
		
		$table_member_subscriptions = strtolower($wpdb->prefix) . 'datebook_member_subscriptions';

        $insert_result = $wpdb->insert( $table_member_subscriptions, array( 'user_id' => $this->user_id, 'subscription_plan_id' => $subscription_plan_id, 'start_date' => $start_date, 'expiration_date' => $expiration_date, 'status' => $status ) );

        do_action( 'datebook_member_add_subscription', $insert_result, $this->user_id, $subscription_plan_id, $start_date, $expiration_date, $status );

        return $insert_result;

    }


    /*
     * Method that checks if the user has a subscription plan id associated
     *
     * @return bool     - true if finds a subscription plan id, false if it doesn't
     *
     */
    public function is_member() {

        if( empty( $this->subscriptions ) )
            return false;
        else
            return true;

    }
}







/**
 * Wrapper function to return a member object
 *
 * @param $user_id  - The id of the user we wish to return
 *
 * @return DateBook_Member
 *
 */
function datebook_get_member( $user_id ) {

    return new DateBook_Member_Class( $user_id );
    
}


/**
 * Check whether a logged in user is an active member (has active subscriptions)
 *
 * @param $user_id  - The id of the user we wish to return
 *
 * @return boolean
 *
 */
function datebook_is_active_member( $user_id ){

    $member = datebook_get_member( $user_id );

    if ( is_object($member) && !empty($member) ) {

        // get member subscriptions
        $subscription_plans = $member->subscriptions;

        //check for active subscriptions
        if (!empty($subscription_plans)) {

            foreach ($subscription_plans as $subscription_plan) {
                if ($subscription_plan['status'] == 'active')
                    return true;
            }

        }
    }

    // if member has no active subscriptions, return false
    return false;
}


/**
 * Queries the database for user ids that also match the member_subscriptions table
 * and returns an array with member objects
 *
 * @param array $args   - arguments to modify the query and return different results
 * @param bool  $count
 *
 * @param mixed array | int - array with member objects or the count of the members if $count is true
 *
 */
function datebook_get_members( $args = array(), $count = false ) {

    global $wpdb;
	
	$table_name = strtolower($wpdb->prefix) . 'datebook_member_subscriptions';

    $defaults = array(
        'order'                      => 'DESC',
        'orderby'                    => 'ID',
        'offset'                     => '',
        'number'                     => '',
        'subscription_plan_id'       => '',
        'member_subscription_status' => '',
        'search'                     => ''
    );

    $args = apply_filters( 'datebook_get_members_args', wp_parse_args( $args, $defaults ), $args, $defaults );

    // Start query string
    if( ! $count )
        $query_string   = "SELECT DISTINCT users.ID ";
    else
        $query_string   = "SELECT COUNT( DISTINCT users.ID ) ";

    // Query string sections
    $query_from         = "FROM {$wpdb->users} users ";
    $query_inner_join   = "INNER JOIN {$table_name} member_subscriptions ON users.ID = member_subscriptions.user_id ";
    $query_inner_join  .= "INNER JOIN {$wpdb->usermeta} usermeta ON users.ID = usermeta.user_id ";
    $query_where        = "WHERE 1=%d ";

    if( ! empty( $args['member_subscription_status'] ) )
        $query_where    = $query_where . " AND member_subscriptions.status = '" . sanitize_text_field( $args['member_subscription_status'] ) . "' ";

    if( ! empty( $args['subscription_plan_id'] ) )
        $query_where    = $query_where . " AND member_subscriptions.subscription_plan_id = " . (int)$args['subscription_plan_id'] . " ";

    // Add search query
    if( ! empty( $args['search'] ) ) {
        $search_term    = sanitize_text_field( $args['search'] );
        $query_where    = $query_where . " AND  " . "  (users.user_email LIKE '%%%s%%' OR users.user_nicename LIKE '%%%s%%' OR usermeta.meta_value LIKE '%%%s%%')  ". " ";
    }

    $query_oder_by      = "ORDER BY users." . sanitize_text_field( $args['orderby'] ) . ' ';

    $query_order        = strtoupper( sanitize_text_field( $args['order'] ) ) . ' ';

    $query_limit        = '';
    if( $args['number'] )
        $query_limit    = 'LIMIT ' . (int)trim( $args['number'] ) . ' ';

    $query_offset       = '';
    if( $args['offset'] )
        $query_offset   = 'OFFSET ' . (int)trim( $args['offset'] ) . ' ';

    // Concatenate query string
    if( ! $count )
        $query_string .= $query_from . $query_inner_join . $query_where . $query_oder_by . $query_order . $query_limit . $query_offset;
    else
        $query_string .= $query_from . $query_inner_join . $query_where . $query_oder_by . $query_order;

    // Return results
    if( ! $count ) {

        if ( ! empty( $search_term ) )
            $results = $wpdb->get_results( $wpdb->prepare( $query_string, 1, $wpdb->esc_like( $search_term ), $wpdb->esc_like( $search_term ), $wpdb->esc_like( $search_term ) ), ARRAY_A );
        else
            $results = $wpdb->get_results( $wpdb->prepare( $query_string, 1 ), ARRAY_A );

    } else {

        if ( ! empty( $search_term ) )
            $results = (int)$wpdb->get_var( $wpdb->prepare( $query_string, 1, $wpdb->esc_like( $search_term ), $wpdb->esc_like( $search_term ), $wpdb->esc_like( $search_term ) ) );
        else
            $results = (int)$wpdb->get_var( $wpdb->prepare( $query_string, 1 ) );

    }

    // Get members for each ID passed
    if( ! $count ) {
        
        $members = array();
    
        if ( ! empty( $results ) ) {
            foreach ($results as $user_data) {
                $member = new DateBook_Member_Class( $user_data['ID'] );

                $members[] = $member;
            }
        }

    // Members are represented by their count
    } else {

        $members = $results;

    }

    return apply_filters( 'datebook_get_members', $members, $args, $count );

}


/**
 * Function that saves the user last login time in usermeta table; 
 * We use this to send email reminders after a certain time has passed since last login
 *
 * @param string $login
 *
 */
function datebook_save_user_last_login( $login = '' ) {

    if( empty( $login ) )
        return;

    $user = get_user_by( 'login', $login );
    $now  = date('Y-m-d H:i:s');

    update_user_meta( $user->ID, 'last_login', $now );

}
add_action( 'wp_login', 'datebook_save_user_last_login', 9 );


/*
 * Adds the value of the payment_profile_id received from the payment gateway in the database to a
 * users subscription information
 *
 */
if( !function_exists('datebook_member_add_payment_profile_id') ) {

    function datebook_member_add_payment_profile_id( $user_id = 0, $subscription_plan_id = 0, $payment_profile_id = '' ) {

        if( empty($user_id) || empty($subscription_plan_id) || empty($payment_profile_id) )
            return false;

        global $wpdb;
		
		$table_member_subscriptions = strtolower($wpdb->prefix) . 'datebook_member_subscriptions';

        $result = $wpdb->update( $table_member_subscriptions, array( 'payment_profile_id' => $payment_profile_id ), array( 'user_id' => $user_id, 'subscription_plan_id' => $subscription_plan_id ) );

        if( $result === false )
            return false;
        else
            return true;
    }

}


/**
 * Returns the value of the payment_profile_id of a member subscription if it exists
 *
 * @param int $user_id
 * @param int $subscription_plan_id
 *
 * @return mixed string | null
 *
 */
if( !function_exists('datebook_member_get_payment_profile_id') ) {
    function datebook_member_get_payment_profile_id( $user_id = 0, $subscription_plan_id = 0 ) {

        if( empty($user_id) || empty($subscription_plan_id) )
            return NULL;

        global $wpdb;
		
		$table_name = strtolower($wpdb->prefix) . 'datebook_member_subscriptions';

        $result = $wpdb->get_var( "SELECT payment_profile_id FROM {$table_name} WHERE user_id = {$user_id} AND subscription_plan_id = {$subscription_plan_id}" );

        // In case we do not find it, it could be located in the api failed canceling
        // errors
        if( is_null($result) ) {

            $api_failed_attempts = get_option( 'datebook_api_failed_attempts', array() );

            if( isset( $api_failed_attempts[$user_id][$subscription_plan_id]['payment_profile_id'] ) )
                $result = $api_failed_attempts[$user_id][$subscription_plan_id]['payment_profile_id'];

        }

        return $result;

    }
}


/**
 * Function that retrieves the unique user key from the database. If we don't have one we generate one and add it to the database
 *
 * @param string $requested_user_login the user login
 *
 */
function datebook_retrieve_activation_key( $requested_user_login ){
    global $wpdb;

    $key = $wpdb->get_var( $wpdb->prepare( "SELECT user_activation_key FROM $wpdb->users WHERE user_login = %s", $requested_user_login ) );

    if ( empty( $key ) ) {

        // Generate something random for a key...
        $key = wp_generate_password( 20, false );
        do_action('datebook_retrieve_password_key', $requested_user_login, $key);

        // Now insert the new md5 key into the db
        $wpdb->update($wpdb->users, array('user_activation_key' => $key), array('user_login' => $requested_user_login));
    }

    return $key;
}


/**
 * Function triggered by the cron job that removes the user activation key (used for password reset) from the db, (make it expire) every 20 hours (72000 seconds).
 *
 */
function datebook_remove_expired_activation_key(){
    $activation_keys = get_option( 'datebook_recover_password_activation_keys', array());

    if ( !empty($activation_keys) ) { //option exists

        foreach ($activation_keys as $id => $activation_key) {

            if ( ( $activation_key['time'] + 72000 ) < time() ) {
                update_user_meta($id, 'user_activation_key', '' ); // remove expired activation key from db
                unset($activation_keys[$id]);
                update_option('datebook_recover_password_activation_keys', $activation_keys); // delete activation key from option
            }

        }

    }
}