<?php
namespace SabaiApps\Directories\Component\PaymentGateway\Helper;

use SabaiApps\Directories\Application;
use SabaiApps\Directories\Component\Entity\Type\IEntity;
use SabaiApps\Directories\Component\Payment\IPlan;
use SabaiApps\Directories\Component\PaymentGateway\PaymentSubscription;
use SabaiApps\Directories\Exception;

class SubscriptionHelper {

	public function create( Application $application, $orderOrPlan, ?IEntity $entity = null, $action = '', $gateway = 'stripe' ) {
		$status = 'active';
		if ( $orderOrPlan instanceof IEntity ) {
			$_order  = $orderOrPlan->getSingleFieldValue( 'paymentgateway_order' );
			$_order += array(
				'order_id' => $orderOrPlan->getId(),
				'author'   => $orderOrPlan->getAuthorId(),
			);
			$_order += $_order['data'];
			unset( $_order['data'] );
			$bundle = $application->Entity_Bundle( $orderOrPlan );
		} elseif ( $orderOrPlan instanceof IPlan ) {
			if ( ! isset( $entity ) ) {
				throw new Exception\InvalidArgumentException( 'Entity is required.' );
			}
			$subscription_id = $application->PaymentGateway_Util_gateway( $gateway )->paymentGatewayCreateSubscription( $orderOrPlan );
			$order_data      = $application->PaymentGateway_Order_getData( $orderOrPlan, (array) $application->Payment_Fee_calculate( $entity, $orderOrPlan, $action ) );
			$_order          = array(
				'subscription_id'     => $subscription_id,
				'gateway'             => $gateway,
				'gateway_item_id'     => $subscription_id,
				'parent_entity_id'    => $entity->getId(),
				'parent_entity_type'  => $entity->getType(),
				'next_payment_at'     => null,
				'amount'              => $order_data['amount'],
				'fees'                => $order_data['fees'],
				'currency'            => $order_data['currency'],
				'price'               => $order_data['price'],
				'tax'                 => $order_data['tax'],
				'plan_id'             => $orderOrPlan->paymentPlanId(),
				'plan_name'           => $orderOrPlan->paymentPlanName(),
				'plan_interval'       => $orderOrPlan->paymentPlanInterval(),
				'plan_interval_count' => $orderOrPlan->paymentPlanIntervalCount(),
			);
			$bundle          = $application->Entity_Bundle( $entity );
		} else {
			throw new Exception\InvalidArgumentException();
		}

		$subscription_bundle = $application->Entity_Bundle( 'paymentgateway_subscription', $bundle->group );
		$subscription        = $application->Entity_Save(
			$subscription_bundle,
			array(
				'author'                      => $_order['author'],
				'parent'                      => $_order['parent'],
				'paymentgateway_subscription' => array(
					'status'             => $status,
					'plan_id'            => $_order['plan_id'],
					'gateway'            => $_order['gateway'],
					'gateway_item_id'    => $_order['subscription_id'],
					'parent_entity_id'   => $_order['parent_entity_id'],
					'parent_entity_type' => $_order['parent_entity_type'],
					'next_payment_at'    => $_order['next_payment_at'],
					'data'               => array(
						'status_updated'      => time(),
						'order_id'            => $_order['order_id'],
						'gateway_status'      => $status,
						'amount'              => $_order['amount'],
						'fees'                => $_order['fees'],
						'currency'            => $_order['currency'],
						'price'               => $_order['price'],
						'tax'                 => $_order['tax'],
						'plan_name'           => $_order['plan_name'],
						'plan_interval'       => $_order['plan_interval'],
						'plan_interval_count' => $_order['plan_interval_count'],
					),
					'notes'              => array(
						$application->PaymentGateway_Util_note(
							sprintf(
								__( 'New subscription for item (ID: %1$d) created with status %2$s.', 'directories-pro' ),
								$_order['parent_entity_id'],
								'<i>' . $application->H( $this->statusLabel( $application, $status ) ) . '</i>'
							)
						),
					),
				),
			)
		);

		$application->Payment_Util_triggerSubscriptionEvent( 'created', new PaymentSubscription( $application, $subscription ) );

		return $subscription;
	}

	protected function _getSubscription( Application $application, $subscriptionId ) {
		$subscriptions = $application->Entity_Query( 'post' )->fieldIs( 'paymentgateway_subscription', $subscriptionId, 'gateway_item_id' )->fetch( 1 );
		if ( empty( $subscriptions )
			|| ( ! $subscription = array_shift( $subscriptions ) )
			|| $subscription->getBundleType() !== 'paymentgateway_subscription'
		) {
			throw new Exception\RuntimeException( 'Invalid subscription: ' . $subscriptionId );
		}
		return $subscription;
	}

	/**
	 * @param Application    $application
	 * @param IEntity|string $subscription
	 * @param bool           $isManual
	 * @param bool           $gatewayCancelled
	 * @param array          $options
	 */
	public function cancel( Application $application, $subscription, $isManual = false, $gatewayCancelled = false, array $options = array() ) {
		if ( $gatewayCancelled ) {
			$options['gateway_status'] = 'cancelled';
		}
		$options['extra_args']['paymentgateway_cancel_manual'] = $isManual;
		$subscription = $this->updateStatus( $application, $subscription, 'cancelled', $options );
		$application->Payment_Util_triggerSubscriptionEvent( 'cancelled', new PaymentSubscription( $application, $subscription ) );
	}

	public function expire( Application $application, $subscription, $gatewayCancelled = true, array $options = array() ) {
		if ( $gatewayCancelled ) {
			$options['gateway_status'] = 'cancelled';
		}
		$subscription = $this->updateStatus( $application, $subscription, 'expired', $options );
		$application->Payment_Util_triggerSubscriptionEvent( 'expired', new PaymentSubscription( $application, $subscription ) );
	}

	/**
	 * @param Application    $application
	 * @param IEntity|string $subscription
	 * @param string         $status
	 * @param array          $options
	 */
	public function updateStatus( Application $application, $subscription, $status, array $options = array() ) {
		if ( ! $subscription instanceof IEntity ) {
			$subscription = $this->_getSubscription( $application, $subscription );
		}
		$value = $subscription->getSingleFieldValue( 'paymentgateway_subscription' );
		if ( isset( $value['status'] )
			&& $value['status'] === $status
		) {
			return;
		}

		$statuses = $this->statuses( $application );
		if ( ! isset( $statuses[ $status ] ) ) {
			throw new Exception\RuntimeException( 'Invalid status: ' . $status );
		}

		$options        += array(
			'note'           => null,
			'user_id'        => null,
			'save'           => true,
			'notes'          => null,
			'gateway_status' => null,
			'extra_args'     => array(),
		);
		$old_status      = $value['status'];
		$value['status'] = $status;
		$note            = sprintf(
			__( 'Subscription status changed from %1$s to %2$s.', 'directories-pro' ),
			isset( $statuses[ $old_status ]['label'] ) ? '<i>' . $application->H( $statuses[ $old_status ]['label'] ) . '</i>' : '--',
			'<i>' . $application->H( $statuses[ $status ]['label'] ) . '</i>'
		);
		if ( isset( $options['note'] )
			&& strlen( $options['note'] )
		) {
			$note = sprintf( $options['note'], $note );
		}
		if ( isset( $options['notes'] )
			&& is_array( $options['notes'] )
		) {
			$value['notes'] = $options['notes'];
		}
		$value['notes'][]                = $application->PaymentGateway_Util_note( $note, null, $options['user_id'] );
		$value['data']['status_updated'] = time();
		if ( isset( $options['gateway_status'] ) ) {
			$value['data']['gateway_status'] = $options['gateway_status'];
		}
		if ( ! $options['save'] ) {
			return $value;
		}

		return $application->Entity_Save( $subscription, array( 'paymentgateway_subscription' => $value ), $options['extra_args'] );
	}

	public function updateNextPaymentDate( Application $application, $subscription, $timestamp ) {
		if ( ! $subscription instanceof IEntity ) {
			$subscription = $this->_getSubscription( $application, $subscription );
		}
		$value = $subscription->getSingleFieldValue( 'paymentgateway_subscription' );
		if ( $value['next_payment_at'] !== $timestamp ) {
			$value['next_payment_at'] = $timestamp;
			$value['notes'][]         = $application->PaymentGateway_Util_note( 'Next payment date updated to ' . $application->System_Date_datetime( $timestamp ) );
			$application->Entity_Save( $subscription, array( 'paymentgateway_subscription' => $value ) );
		}
	}

	public function statusLabel( Application $application, $subscriptionOrStatus, $html = false, $badge = false ) {
		if ( $subscriptionOrStatus instanceof IEntity ) {
			if ( ! $status = $subscriptionOrStatus->getSingleFieldValue( 'paymentgateway_subscription', 'status' ) ) {
				return;
			}
		} else {
			$status = $subscriptionOrStatus;
		}

		$statuses = $this->statuses( $application );
		if ( ! isset( $statuses[ $status ] ) ) {
			return $status;
		}

		if ( ! $html ) {
			return $statuses[ $status ]['label'];
		}

		return $application->PaymentGateway_Util_renderLabel( $statuses[ $status ]['label'], $statuses[ $status ]['color'], $badge );
	}

	public function statuses( Application $application ) {
		return array(
			'active'    => array(
				'label' => __( 'Active', 'directories-pro' ),
				'color' => 'success',
			),
			'cancelled' => array(
				'label' => __( 'Cancelled', 'directories-pro' ),
				'color' => 'warning',
			),
			'expired'   => array(
				'label' => __( 'Expired', 'directories-pro' ),
				'color' => 'danger',
			),
		);
	}

	public function renderSummary( Application $application, IEntity $subscription ) {
		if ( ! $_subscripion = $subscription->getSingleFieldValue( 'paymentgateway_subscription' ) ) {
			return;
		}

		$currency    = $_subscripion['data']['currency'];
		$price       = $_subscripion['data']['price'];
		$tax         = $_subscripion['data']['tax'];
		$title       = $_subscripion['data']['plan_name'];
		$description = '';
		return $application->PaymentGateway_Util_renderPaymentSummary( $title, $description, $price, $currency, $tax );
	}

	public function message( Application $application, IEntity $subscription ) {
		return sprintf(
			$application->H( __( 'Subscription %1$s for item %2$s was enabled on %3$s and is currently %4$s.', 'directories-pro' ) ),
			'<mark>' . $application->H( $subscription->getSingleFieldValue( 'paymentgateway_subscription', 'name' ) ) . '</mark>',
			'<mark>' . ( ( $entity = $application->Entity_ParentEntity( $subscription ) ) ? $entity->getTitle() : '--' ) . '</mark>',
			'<mark>' . $application->System_Date( $subscription->getTimestamp(), true ) . '</mark>',
			'<mark>' . $this->statusLabel( $application, $subscription, true ) . '</mark>'
		);
	}
}
