<?php
namespace SabaiApps\Directories\Component\Payment\FieldWidget;

use SabaiApps\Directories\Component\Field;
use SabaiApps\Directories\Component\Entity\Type\IEntity;
use SabaiApps\Directories\Component\Entity\Model\Bundle;
use SabaiApps\Directories\Component\Form;
use SabaiApps\Directories\Component\Payment\IPlan;
use SabaiApps\Directories\Component\Payment\Feature\IAddonFeature;

class PlanFieldWidget extends Field\Widget\AbstractWidget {

	protected function _fieldWidgetInfo() {
		return array(
			'label'       => __( 'Payment Plan', 'directories-pro' ),
			'field_types' => array( $this->_name ),
		);
	}

	public function fieldWidgetForm( Field\IField $field, array $settings, $value = null, ?IEntity $entity = null, array $parents = array(), $language = null ) {
		if ( empty( $field->Bundle->info['payment_enable'] ) ) {
			return;
		}

		if ( isset( $entity ) ) {
			$is_translation     = ( $default_entity_id = $this->_application->getPlatform()->getTranslatedId( $entity->getType(), $entity->getBundleName(), $entity->getId(), $this->_application->getPlatform()->getDefaultLanguage() ) )
				&& $default_entity_id != $entity->getId();
			$is_add_translation = false;
			$has_pending_order  = $entity->isPending()
				&& $this->_application->Payment_Util_hasPendingOrder( $entity, array( 'add', 'submit' ) );
			$is_suspended       = $this->_application->Payment_Util_isSuspended( $entity );
		} else {
			$is_translation     = $has_pending_order = $is_suspended = false;
			$is_add_translation = $this->_application->getPlatform()->isAdminAddTranslation( $_REQUEST );
		}
		if ( ! empty( $value['plan_id'] ) ) {
			$current_component = empty( $value['component'] ) ? 'woocommerce' : $value['component'];
			// Do not allow changing payment plan and show message if base plan.
			if ( $current_component !== $this->_application->getComponent( 'Payment' )->getPaymentComponent( false, true ) ) {
				if ( ( $payment_component = $this->_application->Payment_Components_impl( $current_component ) )
					&& $payment_component->paymentIsEnabled()
					&& $payment_component->paymentGetPlanById( $field->getBundleName(), $value['plan_id'] )
				) {
					$form = array(
						'old_plan_error' => array(
							'#type'   => 'markup',
							'#weight' => 0,
							'#markup' => '<div class="' . DRTS_BS_PREFIX . 'alert ' . DRTS_BS_PREFIX . 'alert-danger">The current payment plan was assigned by a payment component ('
								. $this->_application->H( $current_component ) . ') that is currently inactive.</div>',
						),
					);
					if ( $plan = $payment_component->paymentGetPlanById( $field->getBundleName(), $value['plan_id'] ) ) {
						$form['plan_name'] = array(
							'#type'          => 'markup',
							'#default_value' => $this->_getPlanTitle( $plan ),
							'#weight'        => 1,
							'#group'         => true,
						);
						if ( $plan->paymentPlanType() === 'base' ) {
							$form['old_plan_info'] = array(
								'#type'   => 'markup',
								'#weight' => 0,
								'#markup' => '<div class="' . DRTS_BS_PREFIX . 'alert ' . DRTS_BS_PREFIX . 'alert-info">You can re-assign'
									. ' a new payment plan using the tool from Directories -> System -> Tools.</div>',
							);
						}
					}
					return $form;
				}
				// Payment component or payment plan has been removed
				$value = null;
			}
		}

		if ( ! $this->_application->IsAdministrator()
			|| $is_translation
			|| $is_add_translation
			|| $has_pending_order
			|| $is_suspended
			|| ! empty( $not_valid_component )
		) {
			if ( empty( $value['plan_id'] ) ) {
				return; // do not show for new content
			}

			$current_plan_id = $value['plan_id'];
			if ( ( $current_lang = $this->_application->getPlatform()->getCurrentLanguage() )
				&& ( $payment_component = $this->_application->getComponent( 'Payment' )->getPaymentComponent() )
			) {
				$current_plan_id = $payment_component->paymentGetPlanId( $field->getBundleName(), $current_plan_id, $current_lang );
			}
			if ( ! $plan = $this->_application->Payment_Plan( $field->getBundleName(), $current_plan_id ) ) {
				return;
			}

			$form = array(
				'plan_id'   => array(
					'#type'                 => 'hidden',
					'#default_value'        => $current_plan_id,
					'#render_hidden_inline' => true,
					'#attributes'           => array( 'data-alt-value' => $plan->paymentPlanName() ),
				),
				'plan_name' => array(
					'#type'          => 'markup',
					'#default_value' => $this->_getPlanTitle( $plan ),
					'#weight'        => 1,
					'#group'         => true,
				),
			);

			if ( ! empty( $value['expires_at'] ) ) {
				$expires_at = $this->_application->System_Date_datetime( $value['expires_at'] );
				if ( $value['expires_at'] < time() ) {
					$expires_at .= ' <span class="' . DRTS_BS_PREFIX . 'badge ' . DRTS_BS_PREFIX . 'bg-danger">'
						. $this->_application->H( __( 'Expired', 'directories-pro' ) ) . '</span>';
				}
				$form['expires_at'] = array(
					'#title'  => __( 'Exp. Date', 'directories-pro' ),
					'#type'   => 'item',
					'#markup' => $expires_at,
					'#weight' => 2,
				);
			}
			if ( $is_suspended ) {
				$form['plan_name'] .= ' <span class="' . DRTS_BS_PREFIX . 'badge ' . DRTS_BS_PREFIX . 'bg-warning">'
					. $this->_application->H( __( 'Suspended', 'directories-pro' ) ) . '</span>';
			}

			return $form;
		}

		if ( ( $current_plan_id = empty( $value['plan_id'] )
				? ( isset( $entity ) ? $entity->getSingleFieldValue( 'payment_plan', 'plan_id' ) : null ) // value may be null before submit, fixes validateAddonFeatures not working because of empty plan id key
				: $value['plan_id'] )
			&& ( $current_lang = $this->_application->getPlatform()->getCurrentLanguage() )
			&& ( $payment_component = $this->_application->getComponent( 'Payment' )->getPaymentComponent() )
		) {
			$current_plan_id = $payment_component->paymentGetPlanId( $field->getBundleName(), $current_plan_id, $current_lang );
		}
		$plan_id_field_selector = sprintf( '[name="%s[plan_id]"]', $this->_application->Form_Form_fieldName( $parents ) );
		if ( ! isset( $entity )
			|| ( ! $plan = $this->_application->Payment_Plan( $entity ) )
			|| $plan->paymentPlanType() === 'base'
			|| ( $plan->paymentPlanType() === 'subscription' && ! $this->_hasActiveSubscription( $entity, $plan ) )
		) {
			if ( ! $plans = $this->_application->Payment_Plans( $field->Bundle->name, 'base' ) ) {
				if ( $current_plan_id && ! $plan ) {
					return array(
						'no_plan_error' => array(
							'#type'   => 'markup',
							'#weight' => 0,
							'#markup' => '<div class="' . DRTS_BS_PREFIX . 'alert ' . DRTS_BS_PREFIX . 'alert-danger">' . sprintf( 'The current payment plan (ID: %d) no longer exists. Save to un-assign the payment plan.', $current_plan_id ) . '</div>',
						),
						'plan_id'       => array(
							'#type'          => 'hidden',
							'#default_value' => '',
						),
					);
				}
				return;
			}

			$plan_options = array( 0 => __( '— Select —', 'directories-pro' ) );
			foreach ( $plans as $plan_id => $plan ) {
				if ( ! $plan->paymentPlanIsPurchasable()
					&& $plan_id !== $current_plan_id
				) {
					continue;
				}

				$plan_options[ $plan_id ] = array(
					'#title'      => $plan->paymentPlanTitle(),
					'#attributes' => array( 'data-alt-value' => $plan->paymentPlanName() ),
				);
			}
			$expires_at_title = $this->_application->H( __( 'Exp. Date', 'directories-pro' ) );
			if ( ! empty( $value['expires_at'] )
				&& $value['expires_at'] < time()
			) {
				$expires_at_title .= ' <span class="' . DRTS_BS_PREFIX . 'badge ' . DRTS_BS_PREFIX . 'bg-danger">'
					. $this->_application->H( __( 'Expired', 'directories-pro' ) ) . '</span>';
			}

			$form = array(
				'plan_id'    => array(
					'#type'          => 'select',
					'#options'       => $plan_options,
					'#default_value' => $current_plan_id,
					'#weight'        => 1,
					'#group'         => true,
				),
				'expires_at' => array(
					'#title'           => $expires_at_title,
					'#title_no_escape' => true,
					'#type'            => 'datepicker',
					'#default_value'   => empty( $value['expires_at'] ) ? null : $value['expires_at'],
					'#weight'          => 2,
					'#states'          => array(
						'invisible' => array(
							$plan_id_field_selector => array( 'value' => 0 ),
						),
					),
					'#disable_time'    => true,
				),
				'component'  => array(
					'#type'                 => 'hidden',
					'#default_value'        => ( $current_component = $this->_application->getComponent( 'Payment' )->getPaymentComponent( false, true ) ) ? $current_component : 'woocommerce',
					'#render_hidden_inline' => true,
				),
			);
		} else {
			$form  = array(
				'plan_id'   => array(
					'#type'                 => 'hidden',
					'#default_value'        => $current_plan_id,
					'#render_hidden_inline' => true,
					'#attributes'           => array( 'data-alt-value' => $plan->paymentPlanName() ),
				),
				'component' => array(
					'#type'                 => 'hidden',
					'#default_value'        => $current_component,
					'#render_hidden_inline' => true,
				),
				'plan_name' => array(
					'#type'          => 'markup',
					'#default_value' => $this->_getPlanTitle( $plan ),
					'#weight'        => 1,
					'#group'         => true,
				),
			);
			$plans = array( $current_plan_id => $plan );
		}

		$features_orderable = array();
		foreach ( array_keys( $this->_application->Payment_Features() ) as $feature_name ) {
			if ( ( ! $feature = $this->_application->Payment_Features_impl( $feature_name, true ) )
				|| ! $feature instanceof IAddonFeature
				|| ! $feature->paymentAddonFeatureSupports( $field->Bundle )
			) {
				continue;
			}

			$current_addon_feature_settings = isset( $value['addon_features'][ $feature_name ] ) ? (array) $value['addon_features'][ $feature_name ] : array();

			// Fetch plans that can accept orders for this add-on feature
			foreach ( array_keys( $plans ) as $plan_id ) {
				$plan_features = $this->_application->Payment_Plan_features( $plans[ $plan_id ] );
				if ( ! $feature->paymentAddonFeatureExtraIsOrderable( $plan_features ) ) {
					continue;
				}
				if ( $feature_form = $feature->paymentAddonFeatureExtraSettingsForm(
					$field->Bundle,
					$plan_features,
					$current_addon_feature_settings,
					array_merge( $parents, array( 'addon_features', $plan_id, $feature_name ) )
				) ) {
					$form['addon_features'][ $plan_id ][ $feature_name ] = $feature_form + array(
						'#weight' => ( null !== $weight = $feature->paymentFeatureInfo( 'weight' ) ) ? $weight : 10,
						'#states' => array(
							'visible' => array(
								$plan_id_field_selector => array( 'value' => $plan_id ),
							),
						),
					);
					$features_orderable[ $plan_id ][] = $feature_name;
				}
			}
		}

		$form['#element_validate'] = array( array( array( $this, 'validateAddonFeatures' ), array( $field->Bundle, $features_orderable ) ) );

		if ( isset( $form['addon_features'] ) ) {
			$form['addon_features']['#weight'] = 5;
		}

		return $form;
	}

	public function validateAddonFeatures( Form\Form $form, &$value, $element, Bundle $bundle, array $features ) {
		if ( empty( $value['addon_features'] ) ) {
			return;
		}

		$plan_id = $value['plan_id'];
		if ( empty( $features[ $plan_id ] ) ) {
			$value['addon_features'] = array();
			return;
		}

		$addon_features = array();
		foreach ( $features[ $plan_id ] as $feature_name ) {
			if ( ! $feature = $this->_application->Payment_Features_impl( $feature_name, true ) ) {
				continue;
			}

			$feature_settings = isset( $value['addon_features'][ $plan_id ][ $feature_name ] ) ? $value['addon_features'][ $plan_id ][ $feature_name ] : array();
			if ( ! $feature->paymentAddonFeatureIsEnabled( $bundle, $feature_settings ) ) {
				$addon_features[ $feature_name ] = false; // make sure the feature is removed from the current addon_features value in the database
			} else {
				$addon_features[ $feature_name ] = $feature_settings;
			}
		}
		$value['addon_features'] = $addon_features;
	}

	protected function _getPlanTitle( IPlan $plan ) {
		return '<div style="margin-bottom:1.5em;">'
			. '<span style="font-size:1.2em;" class="' . DRTS_BS_PREFIX . 'badge ' . DRTS_BS_PREFIX . 'bg-secondary">'
			. $this->_application->H( $plan->paymentPlanTitle() ) . '</span></div>';
	}

	protected function _hasActiveSubscription( IEntity $entity, ?IPlan $plan = null ) {
		if ( ! isset( $plan ) ) {
			if ( ( ! $plan = $this->_application->Payment_Plan( $entity ) )
				|| $plan->paymentPlanType() !== 'subscription'
			) {
				return false;
			}
		}

		$payment_component = $this->_application->Payment_Components_impl( $plan->paymentPlanComponent() );
		foreach ( $payment_component->paymentGetSubscriptions( $entity ) as $subscription ) {
			if ( $subscription->paymentSubscriptionIsActive() ) {
				return true;
			}
		}
		return false;
	}
}
