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

use SabaiApps\Directories\Component\AbstractComponent;
use SabaiApps\Directories\Component\Directory;
use SabaiApps\Directories\Component\Entity;
use SabaiApps\Directories\Component\System;
use SabaiApps\Directories\Component\Field;
use SabaiApps\Directories\Context;
use SabaiApps\Directories\Component\Display;

class UserComponent extends AbstractComponent implements
	Directory\ITypes,
	System\IMainRouter,
	Field\ITypes,
	Field\IWidgets,
	Field\IRenderers,
	Display\IElements,
	Field\IFilters,
	System\IWidgets,
	Display\IStatistics {

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

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

	public static function events() {
		return array(
			// Make sure the callback is called after FrontendSubmit component
			'directoryadminsettingsformfilter' => 99,
			// Make sure the callback is called after Voting component
			'entitypermissionsfilter'          => 99,
		);
	}

	public function directoryGetTypeNames() {
		return array( 'user' );
	}

	public function directoryGetType( $name ) {
		switch ( $name ) {
			case 'user':
				return new DirectoryType\UserDirectoryType( $this->_application, $name );
		}
	}

	public function systemMainRoutes( $lang = null ) {
		$routes = array();
		if ( $this->_application->isComponentLoaded( 'Dashboard' ) ) {
			$dashboard_slug = '/' . $this->_application->getComponent( 'Dashboard' )->getSlug( 'dashboard', $lang );
			$routes[ $dashboard_slug . '/:user_name/:panel_name/edit_profile' ] = array(
				'controller'     => 'EditProfile',
				'title_callback' => true,
				'callback_path'  => 'edit_profile',
				'priority'       => 3,
			);
		}

		return $routes;
	}

	public function systemOnAccessMainRoute( Context $context, $path, $accessType, array &$route ) {}

	public function systemMainRouteTitle( Context $context, $path, $titleType, array $route ) {
		switch ( $path ) {
			case 'edit_profile':
				return __( 'Edit profile', 'directories-pro' );
		}
	}

	public function fieldGetTypeNames() {
		return array( 'user_name', 'user_email', 'user_url', 'user_post_count' );
	}

	public function fieldGetType( $name ) {
		switch ( $name ) {
			case 'user_name':
				return new FieldType\NameFieldType( $this->_application, $name );
			case 'user_email':
				return new FieldType\EmailFieldType( $this->_application, $name );
			case 'user_url':
				return new FieldType\UrlFieldType( $this->_application, $name );
			case 'user_post_count':
				return new FieldType\PostCountFieldType( $this->_application, $name );
		}
	}

	public function fieldGetWidgetNames() {
		return array( 'user_name' );
	}

	public function fieldGetWidget( $name ) {
		switch ( $name ) {
			case 'user_name':
				return new FieldWidget\NameFieldWidget( $this->_application, $name );
		}
	}

	public function fieldGetRendererNames() {
		return array( 'user_name', 'user_author_card' );
	}

	public function fieldGetRenderer( $name ) {
		switch ( $name ) {
			case 'user_name':
				return new FieldRenderer\NameFieldRenderer( $this->_application, $name );
			case 'user_author_card':
				return new FieldRenderer\AuthorCardFieldRenderer( $this->_application, $name );
		}
	}

	public function fieldGetFilterNames() {
		return array( 'user_name' );
	}

	public function fieldGetFilter( $name ) {
		switch ( $name ) {
			case 'user_name':
				return new FieldFilter\NameFieldFilter( $this->_application, $name );
		}
	}

	public function displayGetElementNames( Entity\Model\Bundle $bundle ) {
		return array( 'user_posts' );
	}

	public function displayGetElement( $name ) {
		switch ( $name ) {
			case 'user_posts':
				return new DisplayElement\UserPostsDisplayElement( $this->_application, $name );
		}
	}

	public function userDirectoryExists() {
		try {
			$user_bundle = $this->_application->Entity_Bundle( 'users_usr_usr', '', true );
		} catch ( \Exception $e ) {
			return false;
		}
		return $user_bundle;
	}

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

		$pages['edit_profile'] = array(
			'title'  => _x( 'Edit profile', 'directories-pro' ),
			'icon'   => 'fas fa-user-edit',
			'weight' => 5,
		);
	}

	public function onCoreUserLinkAttrFilter( &$attr, $identity ) {
		if ( $identity->isAnonymous()
			|| ( ! $entity = $this->_application->Entity_Entity( 'user', $identity->id ) )
		) {
			return;
		}

		$attr['href']   = $this->_application->Entity_PermalinkUrl( $entity );
		$attr['target'] = '_self';
		unset( $attr['rel'] );
	}

	public function onFieldFieldSettingsFormFilter( &$form, $field, $settings, $display, $parents, $tab, $isEdit, $submitValues ) {
		if ( empty( $field->Bundle->info['is_user'] ) ) {
			return;
		}

		if ( isset( $form['_is_personal_data'] ) ) {
			$form['_is_personal_data']         = array(
				'#type'          => 'hidden',
				'#default_value' => true,
			);
			$form['_personal_data_identifier'] = array(
				'#type'          => 'hidden',
				'#default_value' => 'author',
			);
		}
	}

	public function onDisplayElementSettingsFormFilter( &$form, $bundle, $display, $element, $elementData ) {
		if ( $display->type !== 'form'
			|| $bundle->entitytype_name !== 'user'
			|| ! $this->_application->isComponentLoaded( 'FrontendSubmit' )
			|| $elementData['settings']['field_name'] === 'user_email'
		) {
			return;
		}

		$form['visibility']['user_register_show'] = array(
			'#type'          => 'checkbox',
			'#title'         => __( 'Show in user registration form', 'directories-pro' ),
			'#default_value' => ! empty( $elementData['visibility']['user_register_show'] ),
			'#horizontal'    => true,
			'#weight'        => 100,
		);
	}

	public function onFieldFieldDataFilter( &$fieldData, $bundle, &$data ) {
		$fieldData['data']['_user_register_show'] = ! empty( $data['visibility']['user_register_show'] );
	}

	public function onFrontendsubmitRegisterFormFilter( &$form ) {
		if ( ! $fields = $this->_application->Entity_Field( 'users_usr_usr' ) ) {
			return;
		}

		foreach ( array_keys( $fields ) as $field_name ) {
			$field = $fields[ $field_name ];
			if ( ! $field->getFieldData( '_user_register_show' ) ) {
				unset( $fields[ $field_name ] );
			}
		}
		if ( empty( $fields ) ) {
			return;
		}

		$form += $this->_application->Entity_Form(
			'users_usr_usr',
			array(
				'values'            => null,
				'horizontal'        => false,
				'fields_only'       => array_keys( $fields ),
				'field_name_prefix' => '_user_field_',
			)
		);
	}

	public function onFrontendsubmitRegister( $identity, $values ) {
		$user_field_values = array();
		foreach ( array_keys( $values ) as $field_name ) {
			if ( strpos( $field_name, '_user_field_' ) !== 0 ) {
				continue;
			}

			$user_field_name                       = substr( $field_name, strlen( '_user_field_' ) );
			$user_field_values[ $user_field_name ] = $values[ $field_name ];
		}
		if ( ! $entity = $this->_application->Entity_Entity( 'user', $identity->id ) ) {
			return;
		}

		if ( ! empty( $user_field_values ) ) {
			$this->_application->Entity_Save( $entity, $user_field_values );
		}
	}

	public function systemGetWidgetNames() {
		return array( 'user_users' );
	}

	public function systemGetWidget( $name ) {
		return new SystemWidget\UsersSystemWidget( $this->_application, $name );
	}

	public function onEntityPermissionsFilter( &$permissions, $bundle ) {
		if ( empty( $bundle->info['is_user'] ) ) {
			return;
		}

		$permissions['entity_read']['title']       = _x( 'View Profile', 'permission name', 'directories-pro' );
		$permissions['voting_rating']['title']     = _x( 'Rate Profile', 'permission name', 'directories-pro' );
		$permissions['voting_rating_own']['title'] = _x( 'Rate own Profile', 'permission name', 'directories-pro' );
	}

	public function onEntityCreateBundlesSuccess( $bundles ) {
		foreach ( $bundles as $bundle ) {
			if ( $bundle->entitytype_name !== 'user' ) {
				continue;
			}

			// Add post count field
			$this->_application->getComponent( 'Entity' )->createEntityField(
				$bundle,
				'user_post_count',
				array(
					'type'     => 'user_post_count',
					'settings' => array(),
					'weight'   => 99,
				)
			);
		}
	}

	public function onEntityUpdateBundlesSuccess( $bundles ) {
		$this->onEntityCreateBundlesSuccess( $bundles );
	}

	public function onSystemToolSettingsFormFilter( &$form, $tool, $parents, $currentStep ) {
		if ( $tool !== 'entity_recount'
			|| ! $this->userDirectoryExists()
		) {
			return;
		}

		$form['type']['#options']['user'] = __( 'Recount user posts', 'directories-pro' );
		$form['type']['#default_value'][] = 'user';
	}

	public function onSystemToolTasksFilter( &$tasks, $tool, $settings, $currentStep, &$storage, &$logs ) {
		if ( $tool !== 'entity_recount'
			|| ! in_array( 'user', $settings['type'] )
			|| ! $this->getPublicPostBundles()
		) {
			return;
		}

		$tasks['user'] = $this->_application->Entity_Query( 'user', 'users_usr_usr' )->count();
	}

	protected function _countUserPosts( array $userIds, $bundleName, $status, $lang = null ) {
		return $this->_application->Entity_Query( 'post', $bundleName )
			->fieldIsIn( 'author', $userIds )
			->fieldIs( 'status', $status )
			->groupByField( 'author' )
			->count( $lang );
	}

	public function onSystemToolTaskResultFilter( &$result, $tool, $task, $settings, $iteration, $total, $currentStep, &$storage, &$logs ) {
		if ( $tool !== 'entity_recount'
			|| $result !== false
			|| $task !== 'user'
		) {
			return;
		}

		$paginator = $this->_application->Entity_Query( 'user', 'users_usr_usr' )
			->paginate( 10 )
			->setCurrentPage( $iteration );
		if ( $users = $paginator->getElements() ) {
			$counts           = $user_post_counts = array();
			$user_ids         = array_keys( $users );
			$published_status = $this->_application->Entity_Status( 'post', 'publish' );
			$langs            = $this->_application->getPlatform()->getLanguages();
			foreach ( $this->getPublicPostBundles() as $bundle_name => $bundle_type ) {
				if ( empty( $langs )
					|| ! $this->_application->getPlatform()->isTranslatable( 'post', $bundle_name )
				) {
					$counts[''][ $bundle_type ][ $bundle_name ] = $this->_countUserPosts( $user_ids, $bundle_name, $published_status );
				} else {
					foreach ( $langs as $lang ) {
						$counts[ $lang ][ $bundle_type ][ $bundle_name ] = $this->_countUserPosts( $user_ids, $bundle_name, $published_status, $lang );
					}
				}
			}
			foreach ( array_keys( $counts ) as $lang ) {
				foreach ( array_keys( $counts[ $lang ] ) as $bundle_type ) {
					foreach ( array_keys( $counts[ $lang ][ $bundle_type ] ) as $bundle_name ) {
						foreach ( array_keys( $counts[ $lang ][ $bundle_type ][ $bundle_name ] ) as $user_id ) {
							if ( ! empty( $counts[ $lang ][ $bundle_type ][ $bundle_name ][ $user_id ] ) ) {
								$user_post_counts[ (int) $user_id ][] = array(
									'value'            => $counts[ $lang ][ $bundle_type ][ $bundle_name ][ $user_id ],
									'post_bundle_type' => $bundle_type,
									'post_bundle_name' => $bundle_name,
									'lang'             => $lang,
								);
							}
						}
					}
				}
			}
			foreach ( array_keys( $users ) as $user_id ) {
				try {
					$this->_application->Entity_Save(
						$users[ $user_id ],
						array( 'user_post_count' => isset( $user_post_counts[ $user_id ] ) ? $user_post_counts[ $user_id ] : false )
					);
				} catch ( Exception\IException $e ) {
					$logs['error'][] = $e->getMessage();
				}
			}
		}

		$offset            = $paginator->getElementOffset();
		$logs['success'][] = sprintf(
			'Recount posts finished for user (%d - %d)',
			$offset + 1,
			$offset + $paginator->getElementLimit()
		);

		$result = $paginator->getElementLimit();
	}

	public function getPublicPostBundles() {
		$ret = array();
		foreach ( $this->_application->Entity_Bundles() as $bundle ) {
			if ( $bundle->entitytype_name !== 'post'
				|| empty( $bundle->info['public'] )
			) {
				continue;
			}

			$ret[ $bundle->name ] = $bundle->type;
		}

		return $ret;
	}

	public function displayGetStatisticNames( Entity\Model\Bundle $bundle ) {
		if ( $bundle->type !== 'user__user' ) {
			return array();
		}

		$ret = array();
		foreach ( array_keys( $this->getPublicPostBundles() ) as $bundle_name ) {
			$ret[] = 'user_post_' . $bundle_name;
		}
		return $ret;
	}

	public function displayGetStatistic( $name ) {
		return new DisplayStatistic\PostCountDisplayStatistic( $this->_application, $name );
	}

	public function onSystemWidgetSettingsFormFilter( &$form, $name, $settings ) {
		if ( $name !== 'user_users'
			|| ( ! $bundles = $this->getPublicPostBundles() )
		) {
			return;
		}

		$weight = 40;
		foreach ( array_keys( $bundles ) as $bundle_name ) {
			if ( ! $bundle = $this->_application->Entity_Bundle( $bundle_name ) ) {
				continue;
			}

			$setting          = 'user_show_post_count-' . $bundle->type . '-' . $bundle_name;
			$form[ $setting ] = array(
				'#title'         => __( 'Show post count', 'directories-pro' ) . ' (' . $bundle->getGroupLabel() . ' - ' . $bundle->getLabel() . ')',
				'#type'          => 'checkbox',
				'#default_value' => ! empty( $settings[ $setting ] ),
				'#weight'        => ++$weight,
			);
		}
	}

	public function onEntitySystemWidgetEntityContentFilter( &$content, $name, $entity, $settings ) {
		if ( $name !== 'user_users' ) {
			return;
		}

		$lang = (string) $this->_application->getPlatform()->getCurrentLanguage();
		foreach ( array_keys( $settings ) as $setting_name ) {
			if ( strpos( $setting_name, 'user_show_post_count-' ) !== 0 ) {
				continue;
			}

			$setting_name_parts = explode( '-', $setting_name );
			if ( count( $setting_name_parts ) !== 3 ) {
				continue;
			}

			$post_counts = $entity->getSingleFieldValue( 'user_post_count' );
			if ( empty( $post_counts[ $setting_name_parts[1] ][ $setting_name_parts[2] ][ $lang ] )
				|| ( ! $bundle = $this->_application->Entity_Bundle( $setting_name_parts[2] ) )
			) {
				continue;
			}

			$count = $post_counts[ $setting_name_parts[1] ][ $setting_name_parts[2] ][ $lang ];
			$label = sprintf( _n( $bundle->getLabel( 'count' ), $bundle->getLabel( 'count2' ), $count, 'directories-pro' ), $count );
			if ( $icon = $this->_application->Entity_BundleTypeInfo( $bundle, 'icon' ) ) {
				$label = '<i class="fa-fw ' . $icon . '"></i> ' . $label;
			}
			$content['meta'][] = $label;
		}
	}

	public function onDashboardPanelsLoaded( $panels ) {
		if ( isset( $panels['account']['links']['edit_profile'] ) ) {
			$this->_application->Entity_Form_loadAssets( 'users_usr_usr' );
		}
	}

	public function onCorePlatformWordpressInit() {
		if ( ! $this->userDirectoryExists() ) {
			return;
		}

		if ( is_admin() ) {
			// Add link to public profile page
			add_filter(
				'user_row_actions',
				function ( $actions, $wpuser ) {
					if ( $entity = $this->_application->Entity_Entity( 'user', $wpuser->ID ) ) {
						$actions['drts_user_view_profile'] = '<a href="' . $this->_application->Entity_PermalinkUrl( $entity ) . '">'
						. $this->_application->H( ucwords( __( 'View public profile', 'directories-pro' ) ) )
						. '</a>';
					}
					return $actions;
				},
				10,
				2
			);
			add_action(
				'admin_bar_menu',
				function ( $adminbar ) {
					if ( in_array( get_current_screen()->base, array( 'user-edit', 'profile' ) )
					&& ( $entity = $this->_application->Entity_Entity( 'user', empty( $_GET['user_id'] ) ? get_current_user_id() : $_GET['user_id'] ) )
					) {
						$adminbar->add_menu(
							array(
								'id'    => 'drts-user-view-profile',
								'title' => ucwords( __( 'View public profile', 'directories-pro' ) ),
								'href'  => $this->_application->Entity_PermalinkUrl( $entity ),
							)
						);
					}
				},
				90
			);
		} else {
			if ( $this->_application->getComponent( 'Dashboard' ) ) {
				// Change edit profile url to frontend dashboard edit profile page
				add_filter(
					'edit_profile_url',
					function ( $url, $userId, $scheme ) {
						if ( $userId === $this->_application->getUser()->id ) {
							$url = $this->_application->Dashboard_Panels_panelUrl( 'account', 'edit_profile', '/edit_profile' );
						}
						return $url;
					},
					10,
					3
				);
			}
			// Add link to edit user in admin bar
			add_action(
				'admin_bar_menu',
				function ( $adminbar ) {
					if ( isset( $GLOBALS['drts_entity'] )
					&& $GLOBALS['drts_entity']->getType() === 'user'
					&& ( $bundle = $this->userDirectoryExists() ) // this should not fail
					) {
						$adminbar->add_menu(
							array(
								'id'    => 'drts-user-edit-profile',
								'title' => sprintf( __( 'Edit %s', 'directories-pro' ), $bundle->getLabel( 'singular' ) ),
								'href'  => admin_url( 'user-edit.php?user_id=' . $GLOBALS['drts_entity']->getId() ),
							)
						);
					}
				},
				90
			);
		}
	}

	public function onFrontendsubmitSubmittableBundlesFilter( &$bundles, $bundleType ) {
		if ( $bundleType === 'user__user'
			&& ! $this->_application->getUser()->isAnonymous()
		) {
			$bundles = array();
		}
	}

	public function onFrontendsubmitRedirectGuestCheckFilter( &$ret, $bundleType ) {
		// Do not require guest redirection check if adding user
		if ( $bundleType === 'user__user' ) {
			$ret = false;
		}
	}

	public function onDashboardAccountNotificationEventsFilter( &$filters, $settings ) {
		$filters[] = 'user_profile_edited';
	}
}
