<?php
namespace SabaiApps\Directories\Component\Membership;

use SabaiApps\Directories\Component\AbstractComponent;
use SabaiApps\Directories\Component\Membership\NotificationEvent\OrderCreatedNotificationEvent;
use SabaiApps\Directories\Component\System;
use SabaiApps\Directories\Component\Display;
use SabaiApps\Directories\Context;
use SabaiApps\Directories\Component\Entity;
use SabaiApps\Directories\Application;

class MembershipComponent extends AbstractComponent implements
	System\IMainRouter,
	Display\IButtons,
	System\ISlugs {

	const VERSION = '2.0.6', PACKAGE = 'directories-pro';

	public static function description() {
		return 'Add and manage user profile fields and views.';
	}

	public static function interfaces() {
		return array( 'FrontendSubmit\IRestrictors', 'Notification\IEvents' );
	}

	public function systemMainRoutes( $lang = null ) {
		$routes = array();
		if ( $this->_application->isComponentLoaded( 'Payment' ) ) {
			if ( $apply_slug = $this->hasSlug( 'apply_membership', $lang ) ) {
				$routes[ '/' . $apply_slug ] = array(
					'controller'      => 'ApplyMembership',
					'access_callback' => true,
					'callback_path'   => 'apply_membership',
					'title_callback'  => true,
				);
			}
			if ( $this->_application->isComponentLoaded( 'Dashboard' ) ) {
				$dashboard_slug = '/' . $this->_application->getComponent( 'Dashboard' )->getSlug( 'dashboard', $lang );
				$routes[ $dashboard_slug . '/:user_name/:panel_name/membership' ]                     = array(
					'controller'      => 'Membership',
					'access_callback' => true,
					'title_callback'  => true,
					'callback_path'   => 'membership',
					'priority'        => 3,
				);
				$routes[ $dashboard_slug . '/:user_name/:panel_name/membership/upgrade' ]             = array(
					'controller'      => 'ApplyMembership',
					'access_callback' => true,
					'title_callback'  => true,
					'callback_path'   => 'upgrade',
					'priority'        => 3,
				);
				$routes[ $dashboard_slug . '/:user_name/:panel_name/membership/renew' ]               = array(
					'controller'      => 'ApplyMembership',
					'access_callback' => true,
					'title_callback'  => true,
					'callback_path'   => 'renew',
					'priority'        => 3,
				);
				$routes[ $dashboard_slug . '/:user_name/:panel_name/membership/order_addon' ]         = array(
					'controller'      => 'OrderMembershipAddon',
					'access_callback' => true,
					'title_callback'  => true,
					'callback_path'   => 'order_addon',
					'priority'        => 3,
				);
				$routes[ $dashboard_slug . '/:user_name/:panel_name/membership/cancel_subscription' ] = array(
					'controller'      => 'CancelMembership',
					'access_callback' => true,
					'title_callback'  => true,
					'callback_path'   => 'cancel_subscription',
					'priority'        => 3,
				);
			}
		}

		return $routes;
	}

	public function systemOnAccessMainRoute( Context $context, $path, $accessType, array &$route ) {
		switch ( $path ) {
			case 'membership':
				if ( $accessType === Application::ROUTE_ACCESS_LINK ) {
					if ( ! $entity = $this->_application->Entity_Entity( 'user', $this->_application->getUser()->id ) ) {
						return false;
					}

					$context->entity = $entity;
				}
				return true;
			case 'renew':
			case 'upgrade':
			case 'order_addon':
			case 'cancel_subscription':
				if ( $accessType === Application::ROUTE_ACCESS_LINK ) {
					if ( ! $this->isPaidMembershipEnabled() ) {
						return false;
					}

					return true;
				}
				if ( $this->_application->getUser()->isAdministrator() ) {
					return false;
				}

				$context->action = $path;
				if ( $path === 'cancel_subscription' ) {
					$args = array();
					if ( ! $this->_application->callHelper( 'Entity_IsRoutable', array( $context->entity->getBundleName(), $path, $context->entity, false, &$args ) ) ) {
						return false;
					}
					$context->subscription = $args['subscription'];
					return true;
				}
				return $this->_application->Entity_IsRoutable( $context->entity->getBundleName(), $path, $context->entity, $path !== 'order_addon' );
			case 'apply_membership':
				if ( $accessType === Application::ROUTE_ACCESS_LINK ) {
					if ( ! $this->isPaidMembershipEnabled() ) {
						return false;
					}

					return true;
				}
				if ( $this->_application->getUser()->isAdministrator() ) {
					return false;
				}
				if ( $this->_application->getUser()->isAnonymous() ) {
					$context->setUnauthorizedError( $route['path'] );
					return false;
				}
				if ( ( ! $entity = $this->_application->Entity_Entity( 'user', $this->_application->getUser()->id ) )
					|| $this->_application->Payment_Plan( $entity ) // membership already assigned?
				) {
					if ( $this->_application->isComponentLoaded( 'Dashboard' ) ) {
						$context->setError( null, '/' . $this->_application->getComponent( 'Dashboard' )->getSlug( 'dashboard' ) );
					}
					return false;
				}

				$context->entity = $entity;
				$context->action = 'upgrade'; // required by payment checkout form to render order summary
				return true;
		}
	}

	public function systemMainRouteTitle( Context $context, $path, $titleType, array $route ) {
		switch ( $path ) {
			case 'membership':
				return __( 'Membership', 'directories-pro' );
			case 'upgrade':
				return __( 'Membership', 'directories-pro' ) . ' - ' . __( 'Upgrade / Downgrade', 'directories-pro' );
			case 'renew':
				return __( 'Membership', 'directories-pro' ) . ' - ' . __( 'Renew', 'directories-pro' );
			case 'order_addon':
				return __( 'Membership', 'directories-pro' ) . ' - ' . __( 'Order Add-on', 'directories-pro' );
			case 'apply_membership':
				return $this->getTitle( 'apply_membership' );
		}
	}

	public function onDashboardAccountPanelPagesFilter( &$pages ) {
		if ( ! $this->isPaidMembershipEnabled() ) {
			return;
		}

		$pages['membership'] = array(
			'title'  => __( 'Membership', 'directories-pro' ),
			'icon'   => 'fas fa-user-tag',
			'weight' => 6,
		);
	}

	public function onEntityDisplaysFilter( &$displays, $bundle, $excludeHidden ) {
		if ( $bundle->type === 'user__user' ) {
			$displays['membership_membership'] = array(
				'label'  => __( 'Membership', 'directories-pro' ),
				'weight' => 10,
			);
		}
	}

	public function displayGetButtonNames( Entity\Model\Bundle $bundle ) {
		$ret = array();
		if ( $this->isPaidMembershipEnabled()
			&& $this->_application->isComponentLoaded( 'Dashboard' )
		) {
			if ( $bundle->entitytype_name === 'user'
				&& ! empty( $bundle->info['payment_enable'] )
			) {
				$ret[] = 'membership_renew';
				$ret[] = 'membership_upgrade';
				$ret[] = 'membership_order_addon';
				$ret[] = 'membership_cancel_subscription';
			}
		}
		return $ret;
	}

	public function displayGetButton( $name ) {
		return new DisplayButton\MembershipDashboardDisplayButton( $this->_application, $name );
	}

	public function frontendsubmitGetRestrictorNames() {
		return array( 'membership_membership' );
	}

	public function frontendsubmitGetRestrictor( $name ) {
		return new FrontendSubmitRestrictor\MembershipFrontendSubmitRestrictor( $this->_application, $name );
	}

	public function isPaidMembershipEnabled() {
		return $this->_application->isComponentLoaded( 'Payment' )
			&& $this->_application->getComponent( 'Payment' )->getPaymentComponent( false, true )
			&& ( $user_bundle = $this->_application->getComponent( 'User' )->userDirectoryExists() )
			&& ! empty( $user_bundle->info['payment_enable'] );
	}

	public function systemSlugs() {
		if ( ! $this->isPaidMembershipEnabled() ) {
			return array();
		}

		return array(
			'apply_membership' => array(
				'admin_title'  => __( 'Apply Membership', 'directories-pro' ),
				'title'        => __( 'Apply Membership', 'directories-pro' ),
				'wp_shortcode' => 'drts-membership-apply',
				'required'     => false,
			),
		);
	}

	public function onFrontendsubmitAfterLoginRegisterUrlFilter( &$url, $type, $identity ) {
		if ( $type !== 'register'
			|| ( ! $slug = $this->hasSlug( 'apply_membership' ) )
			|| ! $this->_application->Filter( 'memberships_redirect_apply_membership_page', true, array( $type, $identity ) )
		) {
			return;
		}

		$url = (string) $this->_application->Url( '/' . $slug );
	}

	public function onPaymentExpiredEntitiesHandled( $entityType, $entities, $action ) {
		if ( $entityType !== 'user' ) {
			return;
		}

		// Memberships have expired

		// Handle posts without a payment plan, that have been submitted by expired users. Posts with a payment plan will be handled according to the payment settings.
		if ( $bundle_types = $this->_application->Entity_BundleTypes_byFeatures( array( 'frontendsubmit_enable' ), 'post' ) ) {
			$posts = $this->_application->Entity_Query( 'post' )
				->fieldIsIn( 'bundle_type', $bundle_types )
				->fieldIsIn( 'author', array_keys( $entities ) )
				->fieldIsNull( 'payment_plan', 'plan_id' )
				->fetch();
			if ( count( $posts ) ) {
				$values = array(
					'status' => $this->_application->Entity_Status( 'post', 'draft' ),
				);
				foreach ( $posts as $post ) {
					$this->_application->Entity_Save( $post, $this->_application->Filter( 'membership_expired_post_values', $values, array( $post ) ) );
				}
			}
		}
	}

	public function onPaymentPlanFeaturesFilter( &$features, $bundle, $plan ) {
		if ( ! $plan instanceof \SabaiApps\Directories\Component\Payment\IPlan ) {
			return;
		}

		$plan_id = $plan->paymentPlanId();
		$limit   = $this->_application->getComponent( 'FrontendSubmit' )->getConfig( 'restrict', 'settings', 'membership_membership', $plan_id, 'limit' );
		if ( ! isset( $limit ) ) {
			return;
		}

		foreach ( array_keys( $limit ) as $bundle_name ) {
			if ( ( ! $bundle = $this->_application->Entity_Bundle( $bundle_name ) )
				|| ! empty( $bundle->info['parent'] )
			) {
				continue;
			}

			if ( empty( $limit[ $bundle_name ] ) ) {
				$label = sprintf(
					$this->_application->H( __( '%1$s: %2$s', 'directories-pro' ) ),
					$bundle->getLabel(),
					'<em>' . $this->_application->H( __( 'Unlimited', 'directories-pro' ) ) . '</em>'
				);
			} else {
				$label = sprintf(
					$this->_application->H( $limit[ $bundle_name ] == 1 ? $bundle->getLabel( 'count' ) : $bundle->getLabel( 'count2' ) ),
					'<em>' . $limit[ $bundle_name ] . '</em>'
				);
			}
			$features['membership_submit'][] = array(
				'html' => $label,
				'icon' => $this->_application->Entity_BundleTypeInfo( $bundle, 'icon' ),
			);
		}
	}

	public function notificationGetEventNames() {
		if ( ! $this->isPaidMembershipEnabled() ) {
			return array();
		}

		return array( 'membership_plan_expiring', 'membership_plan_expired', 'membership_subscription_cancelled', 'membership_subscription_expired' );
	}

	public function notificationGetEvent( $name ) {
		switch ( $name ) {
			case 'membership_plan_expiring':
				return new NotificationEvent\PlanExpiringNotificationEvent( $this->_application, $name, 'user__user' );
			case 'membership_plan_expired':
				return new NotificationEvent\PlanExpiredNotificationEvent( $this->_application, $name, 'user__user' );
			case 'membership_subscription_cancelled':
				return new NotificationEvent\SubscriptionCancelledNotificationEvent( $this->_application, $name, 'user__user' );
			case 'membership_subscription_expired':
				return new NotificationEvent\SubscriptionExpiredNotificationEvent( $this->_application, $name, 'user__user' );
		}
	}

	public function onDisplayElementRenderFilter( &$results, $bundle, array $element, $var ) {
		if ( empty( $element['visibility']['membership_check'] ) ) {
			return;
		}

		if ( $this->_application->getUser()->isAdministrator() ) {
			return; // admins do not have membership, so skip
		}

		$results[] = ! empty( $element['visibility']['memberships'] )
			&& ! $this->_application->getUser()->isAnonymous()
			&& $this->_application->getComponent( 'User' )->userDirectoryExists()
			&& ( $entity = $this->_application->Entity_Entity( 'user', $this->_application->getUser()->id ) )
			&& ( $plan_id = $entity->getSingleFieldValue( 'payment_plan', 'plan_id' ) )
			&& in_array( $plan_id, $element['visibility']['memberships'] );
	}

	public function onDisplayElementSettingsFormFilter( &$form, $bundle, $display, $element, $elementData ) {
		if ( $display->type === 'form'
			|| ( ! $user_bundle = $this->_application->getComponent( 'User' )->userDirectoryExists() )
		) {
			return;
		}

		$plans = array();
		foreach ( $this->_application->Payment_Plans( $user_bundle->name, array( 'subscription', 'base' ) ) as $plan_id => $plan ) {
			$plans[ $plan_id ] = $plan->paymentPlanTitle();
		}
		$form['visibility']['membership_check'] = array(
			'#type'          => 'checkbox',
			'#title'         => __( 'Visible to users with selected membership plans only', 'directories-pro' ),
			'#default_value' => ! empty( $elementData['visibility']['membership_check'] ),
			'#horizontal'    => true,
			'#weight'        => 35,
		);
		$form['visibility']['memberships']      = array(
			'#type'          => 'checkboxes',
			'#columns'       => 3,
			'#options'       => $plans,
			'#default_value' => empty( $elementData['visibility']['memberships'] ) ? null : $elementData['visibility']['memberships'],
			'#horizontal'    => true,
			'#states'        => array(
				'visible' => array(
					'input[name="visibility[membership_check]"]' => array(
						'type'  => 'checked',
						'value' => true,
					),
				),
			),
			'#weight'        => 35,
		);
	}

	public function onCorePlatformWordPressInit() {
		if ( is_admin() ) {
			return;
		}

		add_shortcode( 'drts-membership-restrict', array( $this->_application, 'Membership_WordPressShortcode_restrict' ) );
	}
}
