<?php
/**
 * Subscription model.
 *
 * @since 2.6.10
 *
 * @package Masteriyo\Models
 */

namespace Masteriyo\Pro\Models;

use Masteriyo\PostType\PostType;
use Masteriyo\Models\Order\Order;
use Masteriyo\Enums\CourseBillingPeriod;
use Masteriyo\Pro\Enums\SubscriptionStatus;

defined( 'ABSPATH' ) || exit;

/**
 * Subscription model.
 *
 * @since 2.6.10
 */
class Subscription extends Order {

	/**
	 * Related order object.
	 *
	 * @since 2.6.10
	 *
	 * @var \Masteriyo\Models\Order\Order
	 */
	protected $order = null;

	/**
	 * Post type.
	 *
	 * @since 2.6.10
	 *
	 * @var string
	 */
	public $post_type = PostType::SUBSCRIPTION;

	/**
	 * Object type.
	 *
	 * @since 2.6.10
	 *
	 * @var string
	 */
	public $object_type = 'subscription';

	/**
	 * Cache groups.
	 *
	 * @since 2.6.10
	 *
	 * @var string
	 */
	public $cache_group = 'subscriptions';

	/**
	 * Extra data.
	 *
	 * @since 2.6.10
	 *
	 * @var array
	 */
	protected $extra_data = array(
		// Extra data with getters/setters
		'recurring_amount'        => 0,
		'billing_period'          => '',
		'billing_interval'        => 1,
		'billing_expire_after'    => 0,
		'suspension_count'        => 0,
		'requires_manual_renewal' => true,
		'cancelled_email_sent'    => false,
		'trial_period'            => '',
		'switch_data'             => array(),
		'subscription_id'         => '',

		// Extra data that requires manual getting/setting because we don't define getters/setters for it
		'schedule_trial_end'      => null,
		'schedule_next_payment'   => null,
		'schedule_cancelled'      => null,
		'schedule_end'            => null,
		'schedule_payment_retry'  => null,
		'schedule_start'          => null,
	);

	/*
	|--------------------------------------------------------------------------
	| Non-CRUD methods
	|--------------------------------------------------------------------------
	|
	*/

	/**
	 * Get rest formatted total.
	 *
	 * @since 2.6.10
	 * @param string $context What the value is for. Valid values are view and edit.
	 * @return string
	 */
	public function get_rest_formatted_total( $context = 'view' ) {
		$total  = parent::get_rest_formatted_total( $context );
		$total .= CourseBillingPeriod::separator() . CourseBillingPeriod::label( $this->get_billing_period( $context ) );

		return $total;
	}

	/**
	 * Get rest formatted total.
	 *
	 * @since 2.6.10
	 * @param string $context What the value is for. Valid values are view and edit.
	 * @return string
	 */
	public function get_rest_formatted_recurring_amount( $context = 'view' ) {
		$total  = $this->get_recurring_amount( $context );
		$total .= CourseBillingPeriod::separator() . CourseBillingPeriod::label( $this->get_billing_period( $context ) );

		return $total;
	}

	/**
	 * Return an instance of the subscription model.
	 *
	 * @since 2.6.10
	 *
	 * @return \Masteriyo\Pro\Models\Subscription
	 */
	public static function instance() {
		return masteriyo( 'subscription' );
	}

	/*
	|--------------------------------------------------------------------------
	| Getters
	|--------------------------------------------------------------------------
	*/

	/**
	 * Get recurring amount.
	 *
	 * @since 2.6.10
	 *
	 * @param  string $context What the value is for. Valid values are view and edit.
	 *
	 * @return string
	 */
	public function get_recurring_amount( $context = 'view' ) {
		return $this->get_prop( 'recurring_amount', $context );
	}

	/**
	 * Get billing period.
	 *
	 * @since 2.6.10
	 *
	 * @param  string $context What the value is for. Valid values are view and edit.
	 *
	 * @return string
	 */
	public function get_billing_period( $context = 'view' ) {
		return $this->get_prop( 'billing_period', $context );
	}

	/**
	 * Get billing interval.
	 *
	 * @since 2.6.10
	 *
	 * @param  string $context What the value is for. Valid values are view and edit.
	 *
	 * @return int
	 */
	public function get_billing_interval( $context = 'view' ) {
		return $this->get_prop( 'billing_interval', $context );
	}

	/**
	 * Get billing expire after.
	 *
	 * @since 2.6.10
	 *
	 * @param  string $context What the value is for. Valid values are view and edit.
	 *
	 * @return int
	 */
	public function get_billing_expire_after( $context = 'view' ) {
		return $this->get_prop( 'billing_expire_after', $context );
	}

	/**
	 * Get trial period.
	 *
	 * @since 2.6.10
	 *
	 * @param  string $context What the value is for. Valid values are view and edit.
	 *
	 * @return string
	 */
	public function get_trial_period( $context = 'view' ) {
		return $this->get_prop( 'trial_period', $context );
	}

	/**
	 * Get suspension count.
	 *
	 * @since 2.6.10
	 *
	 * @param  string $context What the value is for. Valid values are view and edit.
	 *
	 * @return int
	 */
	public function get_suspension_count( $context = 'view' ) {
		return $this->get_prop( 'suspension_count', $context );
	}

	/**
	 * Checks if the subscription requires manual renewal payments.
	 *
	 * @since 2.6.10
	 *
	 * @param  string $context What the value is for. Valid values are view and edit.
	 *
	 * @return bool
	 */
	public function get_requires_manual_renewal( $context = 'view' ) {
		return $this->get_prop( 'requires_manual_renewal', $context );
	}

	/**
	 * Get the switch data.
	 *
	 * @since 2.6.10
	 *
	 * @param  string $context What the value is for. Valid values are view and edit.
	 *
	 * @return string
	 */
	public function get_switch_data( $context = 'view' ) {
		return $this->get_prop( 'switch_data', $context );
	}

	/**
	 * Get the flag about whether the cancelled email has been sent or not.
	 *
	 * @since 2.6.10
	 *
	 * @param  string $context What the value is for. Valid values are view and edit.
	 *
	 * @return string
	 */
	public function get_cancelled_email_sent( $context = 'view' ) {
		return $this->get_prop( 'cancelled_email_sent', $context );
	}

	/**
	 * Get schedule trial end date.
	 *
	 * @since 2.6.10
	 *
	 * @param  string $context What the value is for. Valid values are view and edit.
	 */
	public function get_schedule_trial_end( $context = 'view' ) {
		return $this->get_prop( 'schedule_trial_end', $context );
	}

	/**
	 * Get schedule next payment
	 *
	 * @since 2.6.10
	 *
	 * @param  string $context What the value is for. Valid values are view and edit.
	 */
	public function get_schedule_next_payment( $context = 'view' ) {
		return $this->get_prop( 'schedule_next_payment', $context );
	}

	/**
	 * Get schedule cancelled.
	 *
	 * @since 2.6.10
	 * @param  string $context What the value is for. Valid values are view and edit.
	 */
	public function get_schedule_cancelled( $context = 'view' ) {
		return $this->get_prop( 'schedule_cancelled', $context );
	}

	/**
	 * Get schedule end
	 *
	 * @since 2.6.10
	 * @param  string $context What the value is for. Valid values are view and edit.
	 */
	public function get_schedule_end( $context = 'view' ) {
		return $this->get_prop( 'schedule_end', $context );
	}

	/**
	 * Get schedule payment retry.
	 *
	 * @since 2.6.10
	 * @param  string $context What the value is for. Valid values are view and edit.
	 */
	public function get_schedule_payment_retry( $context = 'view' ) {
		return $this->get_prop( 'schedule_payment_retry', $context );
	}

	/**
	 * Get schedule start
	 *
	 * @since 2.6.10
	 * @param  string $context What the value is for. Valid values are view and edit.
	 */
	public function get_schedule_start( $context = 'view' ) {
		return $this->get_prop( 'schedule_start', $context );
	}

	/**
	 * Get subscription id.
	 *
	 * @since 2.6.10
	 * @param  string $context What the value is for. Valid values are view and edit.
	 */
	public function get_subscription_id( $context = 'view' ) {
		return $this->get_prop( 'subscription_id', $context );
	}

	/*
	|--------------------------------------------------------------------------
	| Setters
	|--------------------------------------------------------------------------
	*/

	/**
	 * Set recurring amount.
	 *
	 * @since 2.6.10
	 * @param double $value
	 */
	public function set_recurring_amount( $value ) {
		$this->set_prop( 'recurring_amount', $value );
	}

	/**
	 * Set billing period.
	 *
	 * @since 2.6.10
	 * @param string $value
	 */
	public function set_billing_period( $value ) {
		$this->set_prop( 'billing_period', $value );
	}

	/**
	 * Set billing interval.
	 *
	 * @since 2.6.10
	 * @param int $value
	 */
	public function set_billing_interval( $value ) {
		$this->set_prop( 'billing_interval', absint( $value ) );
	}

	/**
	 * Set billing expire_after.
	 *
	 * @since 2.6.10
	 * @param int $expire_after Subscription billing expire after in months.
	 */
	public function set_billing_expire_after( $expire_after ) {
		$this->set_prop( 'billing_expire_after', absint( $expire_after ) );
	}

	/**
	 * Set trial period.
	 *
	 * @param string $value
	 * @since 2.6.10
	 */
	public function set_trial_period( $value ) {
		$this->set_prop( 'trial_period', $value );
	}

	/**
	 * Set suspension count.
	 *
	 * @since 2.6.10
	 * @param int $value
	 */
	public function set_suspension_count( $value ) {
		$this->set_prop( 'suspension_count', absint( $value ) );
	}

	/**
	 * Set schedule start date.
	 *
	 * This function should not be used. It only exists to support setting the start date on subscription creation without
	 * having to call update_dates() which results in a save.
	 *
	 * The more aptly named set_schedule_start() cannot exist because then WC core thinks the _schedule_start meta is an
	 * internal meta key and throws errors.
	 *
	 * @param string $schedule_start
	 */
	public function set_start_date( $schedule_start ) {
		$this->set_prop( 'schedule_start', $schedule_start );
	}

	/**
	 * Set the manual renewal flag on the subscription.
	 *
	 * The manual renewal flag is stored in database as string 'true' or 'false' when set, and empty string when not set
	 * (which means it doesn't require manual renewal), but we want to consistently use it via get/set as a boolean,
	 * for sanity's sake.
	 *
	 * @since 2.6.10
	 * @param bool $value
	 */
	public function set_requires_manual_renewal( $value ) {
		$this->set_prop( 'requires_manual_renewal', masteriyo_string_to_bool( $value ) );
	}

	/**
	 * Set the switch data on the subscription.
	 *
	 * @since 2.6.10
	 */
	public function set_switch_data( $value ) {
		$this->set_prop( 'switch_data', $value );
	}

	/**
	 * Set the flag about whether the cancelled email has been sent or not.
	 *
	 * @since 2.6.10
	 */
	public function set_cancelled_email_sent( $value ) {
		$this->set_prop( 'cancelled_email_sent', $value );
	}

	/**
	 * Set schedule trial end.
	 *
	 * @since 2.6.10
	 *
	 * @param string|integer|null $date UTC timestamp, or ISO 8601 DateTime. If the DateTime string has no timezone or offset, WordPress site timezone will be assumed. Null if their is no date.
	 */
	public function set_schedule_trial_end( $date ) {
		$this->set_date_prop( 'schedule_trial_end', $date );
	}

	/**
	 * Set schedule next payment
	 *
	 * @since 2.6.10
	 *
	 * @param string|integer|null $date UTC timestamp, or ISO 8601 DateTime. If the DateTime string has no timezone or offset, WordPress site timezone will be assumed. Null if their is no date.
	 */
	public function set_schedule_next_payment( $date ) {
		$this->set_date_prop( 'schedule_next_payment', $date );
	}

	/**
	 * Set schedule cancelled
	 *
	 * @since 2.6.10
	 *
	 * @param string|integer|null $date UTC timestamp, or ISO 8601 DateTime. If the DateTime string has no timezone or offset, WordPress site timezone will be assumed. Null if their is no date.
	 */
	public function set_schedule_cancelled( $date ) {
		$this->set_date_prop( 'schedule_cancelled', $date );
	}

	/**
	 * Set schedule end.
	 *
	 * @since 2.6.10
	 *
	 * @param string|integer|null $date UTC timestamp, or ISO 8601 DateTime. If the DateTime string has no timezone or offset, WordPress site timezone will be assumed. Null if their is no date.
	 */
	public function set_schedule_end( $date ) {
		$this->set_date_prop( 'schedule_end', $date );
	}

	/**
	 * Set schedule payment retry.
	 *
	 * @since 2.6.10
	 *
	 * @param string|integer|null $date UTC timestamp, or ISO 8601 DateTime. If the DateTime string has no timezone or offset, WordPress site timezone will be assumed. Null if their is no date.
	 */
	public function set_schedule_payment_retry( $date ) {
		$this->set_date_prop( 'schedule_payment_retry', $date );
	}

	/**
	 * Set schedule start.
	 *
	 * @since 2.6.10
	 *
	 * @param string|integer|null $date UTC timestamp, or ISO 8601 DateTime. If the DateTime string has no timezone or offset, WordPress site timezone will be assumed. Null if their is no date.
	 */
	public function set_schedule_start( $date ) {
		$this->set_date_prop( 'schedule_start', $date );
	}

	/**
	 * Set subscription id.
	 *
	 * @since 2.6.10
	 *
	 * @param string $subscription_id Payment methods subscription/profile id.
	 */
	public function set_subscription_id( $subscription_id ) {
		$this->set_prop( 'subscription_id', $subscription_id );
	}

	/**
	 * Get all valid statuses for this order
	 *
	 * @since 2.6.10
	 * @return array Internal status keys e.g. 'masteriyo-processing'
	 */
	protected function get_valid_statuses() {
		return SubscriptionStatus::all();
	}

	/**
	 * Return billing expire after text.
	 *
	 * @since 2.6.10
	 *
	 * @param string $context
	 *
	 * @return string
	 */
	public function billing_expire_after_text( $context = 'view' ) {
		$expire_after = $this->get_billing_expire_after( $context );
		$text         = _x( 'Never', 'Subscription billing expire after (months) ', 'learning-management-system' );

		if ( 0 < $expire_after ) {
			$text = sprintf( 'After %d months', $expire_after );
		}

		/**
		 * Filters subscription billing expire after text.
		 *
		 * @since 2.6.10
		 *
		 * @param string $text Subscription billing expire after text.
		 * @param \Masteriyo\Pro\Models\Subscription $subscription Subscription object.
		 */
		return apply_filters( 'masteriyo_subscription_billing_expire_after_text', $text, $this );
	}

	/**
	 * Return billing interval text.
	 *
	 * @since 2.6.10
	 *
	 * @param string $context
	 *
	 * @return string
	 */
	public function billing_interval_text( $context = 'view' ) {
		$billing_period_text = $this->billing_period_text( $context );
		$billing_interval    = $this->get_billing_interval( $context );

		if ( 1 === $billing_interval ) {
			$billing_interval = '1st';
		} elseif ( 2 === $billing_interval ) {
			$billing_interval = '2nd';
		} elseif ( 3 === $billing_interval ) {
			$billing_interval = '3rd';
		} else {
			$billing_interval = "{$billing_interval}th";
		}

		$text = sprintf(
			/* translators: %d: Subscription billing interval, %s: Subscription billing period */
			_x( 'Every %1$s %2$s', 'Stripe billing interval text like( Every 1st/2nd/3rd/4th month).', 'learning-management-system' ),
			$billing_interval,
			$billing_period_text
		);

			/**
		 * Filters subscription billing interval text.
		 *
		 * @since 2.6.10
		 *
		 * @param string $text Subscription billing interval text.
		 * @param \Masteriyo\Pro\Models\Subscription $subscription Subscription object.
		 */
		return apply_filters( 'masteriyo_subscription_billing_interval_text', $text, $this );
	}

	/**
	 * Return billing period text.
	 *
	 * @since 2.6.10
	 *
	 * @param string $context
	 * @return string
	 */
	public function billing_period_text( $context = 'view' ) {
		$billing_period = $this->get_billing_period( $context );

		$maps = array(
			'year'  => _x( 'Year', 'Subscription billing period text', 'learning-management-system' ),
			'month' => _x( 'Month', 'Subscription billing period text', 'learning-management-system' ),
			'week'  => _x( 'Week', 'Subscription billing period text', 'learning-management-system' ),
			'day'   => _x( 'Day', 'Subscription billing period text', 'learning-management-system' ),
		);

		$text = isset( $maps[ $billing_period ] ) ? $maps[ $billing_period ] : _x( 'N/A', 'Subscription billing period text', 'learning-management-system' );

		/**
		 * Filters billing period text.
		 *
		 * @since 2.6.10
		 *
		 * @param string $text Billing period text.
		 * @param \Masteriyo\Pro\Models\Subscription $this Subscription object.
		 */
		return apply_filters( 'masteriyo_subscription_billing_period_text', $text, $this );
	}
}
