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

use SabaiApps\Directories\Application;
use SabaiApps\Directories\Component\Entity\Type\IEntity;
use SabaiApps\Directories\Component\WordPressContent\AdminContent;
use SabaiApps\Directories\Exception;
use SabaiApps\Directories\Component\Field;

class AdminProfile {

	protected $_application, $_js, $_submitted;

	public function __construct( Application $application ) {
		$this->_application = $application;
		if ( ! $this->_application->getComponent( 'User' )->userDirectoryExists() ) {
			return;
		}

		add_action( 'edit_user_profile', array( $this, 'profileFormAction' ) );
		add_action( 'show_user_profile', array( $this, 'profileFormAction' ) );
		add_action( 'user_new_form', array( $this, 'profileFormAction' ) );
		add_action( 'admin_print_styles', array( $this, 'adminPrintStylesAction' ) );

		// Update entity field values when profile edited from WordPress backend
		add_action( 'edit_user_profile_update', array( $this, 'profileUpdateAction' ) );
		add_action( 'personal_options_update', array( $this, 'profileUpdateAction' ) );
		add_action( 'user_profile_update_errors', array( $this, 'profileUpdateErrorsAction' ), 10, 3 );
		add_action( 'edit_user_created_user', array( $this, 'userCreatedAction' ) );

		// Admin users page
		add_filter( 'default_hidden_columns', array( $this, 'defaultHiddenColumnsFilter' ), 10, 2 );
		add_filter( 'manage_users_columns', array( $this, 'manageUsersColumnsFilter' ) );
		add_filter( 'manage_users_custom_column', array( $this, 'manageUsersCustomColumnFilter' ), 10, 3 );
		add_filter( 'pre_get_users', array( $this, 'preGetUsersFilter' ), 10, 1 );
		add_action( 'restrict_manage_users', array( $this, 'restrictManageUsersAction' ), 99 );
	}

	public function profileFormAction( $user ) {
		$form              = $this->_getProfileForm( is_object( $user ) ? $user : null );
		$admin_only_fields = array();
		foreach ( array_keys( $form['drts'] ) as $field_name ) {
			if ( strpos( $field_name, '#' ) !== false
				|| empty( $form['drts'][ $field_name ]['#admin_only'] )
			) {
				continue;
			}

			$admin_only_fields[ $field_name ]      = $form['drts'][ $field_name ]['#title'];
			$form['drts'][ $field_name ]['#title'] = null;
		}
		// Render form and display
		$rendered = $this->_application->Entity_Form_renderDisplay( isset( $_POST['drts'] ) ? $this->_application->Form_Form_build( $form, false, array( 'drts' => $_POST['drts'] ) ) : $form );
		echo '<h2>' . __( 'Additional Details', '-sabia_plugin_name-' ) . '</h2>';
		echo '<div class="drts">' . $rendered['html'] . PHP_EOL . $rendered['form']->getHiddenHtml() . '</div>';
		$this->_js = $rendered['form']->getJsHtml();
		if ( ! empty( $admin_only_fields ) ) {
			echo '<h2>' . __( 'Additional Settings', '-sabia_plugin_name-' ) . '</h2>';
			echo '<div class="drts">';
			foreach ( array_keys( $admin_only_fields ) as $field_name ) {
				$field_title = $this->_application->H( $admin_only_fields[ $field_name ] );
				if ( ! empty( $form['drts'][ $field_name ]['#required'] ) ) {
					$field_title .= $rendered['form']->getRequiredFieldSuffix();
				}
				echo '<div class="drts-form-group ' . DRTS_BS_PREFIX . 'row">';
				echo '<div class="' . DRTS_BS_PREFIX . 'col-sm-3 ' . DRTS_BS_PREFIX . 'col-form-label"><label>' . $field_title . '</label></div>';
				echo '<div class="' . DRTS_BS_PREFIX . 'col-sm-9 -sabai-app_name--form-field-main">' . $rendered['form']->getHtml( $field_name, array( 'drts' ) ) . '</div>';
				echo '</div>';
			}
			echo '</div>';
		}
	}

	public function adminPrintStylesAction() {
		$screen = get_current_screen();
		if ( ! $screen
			|| ! in_array( $screen->id, array( 'users', 'profile', 'user-edit', 'user' ) )
		) {
			return;
		}

		// Add JS/CSS
		$this->_application->getPlatform()
			->loadDefaultAssets()
			->addJs( 'DRTS.init(jQuery(".wp-list-table.users"));', true, -99 );
		if ( $screen->id !== 'users' ) {
			$this->_application->getPlatform()
				->addCssFile( 'wordpressuser-admin-profile.min.css', 'drts-wordpressuser-admin-profile', 'drts', 'directories-pro' );
		} else {
			$this->_application->getPlatform()->addJsInline(
				'jquery-core',
				'document.addEventListener("DOMContentLoaded", function() { 
    jQuery(".wp-list-table.users").addClass("drts");
});'
			);
		}
		add_action( 'admin_footer', array( $this, 'adminFooterAction' ) );
	}

	public function adminFooterAction() {
		// This isn't really required since it will be output by AdminContent, however just in case
		if ( $js = $this->_application->getPlatform()->getJsHtml() ) {
			echo $js;
		}

		if ( isset( $this->_js ) ) {
			echo $this->_js;
		}

		if ( get_current_screen()->id === 'users'
			&& $this->_application->isComponentLoaded( 'User' )
			&& ( $actions_js = AdminContent::adminActionsJs( $this->_application, 'users_usr_usr' ) )
		) {
			echo '<script type="text/javascript">document.addEventListener("DOMContentLoaded", function(event) {' . $actions_js . '});</script>';
		}
	}

	public function profileUpdateAction( $userId ) {
		if ( empty( $userId )
			|| ( ! $entity = $this->_application->Entity_Entity( 'user', $userId ) )
		) {
			return;
		}

		$this->_submit( $entity );
		$this->_save( $entity );
	}

	public function profileUpdateErrorsAction( $errors, $update, $user ) {
		// Need to call submit here if new user since there is no other hook before error check
		if ( empty( $user->ID ) ) {
			$this->_submit();
		}

		if ( ! empty( $this->_submitted['errors'] ) ) {
			foreach ( array_keys( $this->_submitted['errors'] ) as $key ) {
				$error = $this->_submitted['errors'][ $key ];
				$errors->add( $key, '<strong>' . $this->_application->H( $error['label'] ) . '</strong>: ' . $this->_application->H( $error['error'] ) );
			}
		}
	}

	public function userCreatedAction( $userId ) {
		if ( ! $user = get_user_by( 'ID', $userId ) ) {
			return;
		}

		$this->_save( new EntityType\UserEntity( $user ) );
	}

	protected function _getProfileForm( ?\WP_User $user = null, ?array $values = null ) {
		if ( isset( $user )
			&& ! empty( $user->ID )
		) {
			$entity_or_bundle = new EntityType\UserEntity( $user );
			$this->_application->Entity_Field_load( $entity_or_bundle );
			$form_id = 'your-profile';
		} else {
			$entity_or_bundle = 'users_usr_usr';
			$form_id          = 'createuser';
		}
		$form = $this->_application->Entity_Form(
			$entity_or_bundle,
			array(
				'values'                => $values,
				'values_check_build_id' => false,
				'is_admin'              => true,
				'wrap'                  => 'drts',
				'pre_render_display'    => true,
				'horizontal'            => true,
			)
		);
		// Remove fields that are already displayed by WordPress
		unset( $form['drts']['user_name'], $form['drts']['user_email'], $form['drts']['user_url'], $form['drts']['user_description'] );

		$form['#id']                    = $form_id; // Form ID which is assigned by WordPress
		$form['#method']                = 'post'; // This is required to generate form build ID hidden field
		$form['#token']                 = false;
		$form['#build_id']              = false;
		$form['#required_field_suffix'] = ' <span class="description">(required)</span>';

		return $form;
	}

	protected function _submit( ?IEntity $entity = null ) {
		if ( isset( $this->_submitted ) ) {
			return;
		}

		$this->_submitted = array(
			'errors'     => array(),
			'values'     => array(),
			'extra_args' => array(),
		);

		// Update values from drts form
		$submit_values = empty( $_POST['drts'] ) ? array() : stripslashes_deep( $_POST['drts'] );

		// Invisible field names need to be unwrapped
		if ( ! empty( $submit_values['_drts_form_invisible_fields'] )
			&& ( $invisible_field_names = explode( ',', $submit_values['_drts_form_invisible_fields'] ) )
		) {
			foreach ( array_keys( $invisible_field_names ) as $key ) {
				$invisible_field_names[ $key ] = preg_replace(
					'/^drts\[([^\]]+)\](.*)$/',
					'$1$2',
					$invisible_field_names[ $key ]
				);
			}
			$submit_values['_drts_form_invisible_fields'] = implode( ',', $invisible_field_names );
		}

		// Build form
		if ( ! isset( $entity )
			|| ( ! $user = $entity->user() )
		) {
			$user = null;
		}
		$form = $this->_application->Form_Form_build( $this->_getProfileForm( $user, array( 'drts' => $submit_values ) ) );

		$form->submit( array( 'drts' => $submit_values ) );
		if ( ! $form->isSubmitSuccess() ) {
			if ( $form->hasError() ) {
				$errors = $form->getError();
				foreach ( array_keys( $errors ) as $name ) {
					$this->_submitted['errors'][ $name ] = array(
						'label' => ( $label = $form->getLabel( $name ) ) ? $label : null,
						'error' => $errors[ $name ],
					);
				}
			}
		} else {
			$values = $form->values['drts'];

			// Update values from WordPress default user form
			if ( isset( $_POST['description'] ) ) {
				$values['user_description'] = $_POST['description'];
			}
			$values['user_name'] = isset( $entity ) ? $entity->getSingleFieldValue( 'user_name' ) : array();
			foreach ( array( 'first_name', 'last_name', 'display_name' ) as $key ) {
				if ( isset( $_POST[ $key ] ) ) {
					$values['user_name'][ $key ] = $_POST[ $key ];
				}
			}
			// Get display name from nick name or user name if empty
			if ( ! isset( $values['user_name']['display_name'] )
				|| ! strlen( $values['user_name']['display_name'] )
			) {
				foreach ( array( 'nickname', 'user_login' ) as $key ) {
					if ( isset( $_POST[ $key ] ) ) {
						$values['user_name']['display_name'] = $_POST[ $key ];
						break;
					}
				}
			}

			$this->_submitted['values'] = $values;

			// Set max number of items for each field
			$this->_submitted['extra_args']['entity_field_max_num_items'] = $form->settings['#entity_field_max_num_items'];
		}
	}

	protected function _save( $entityOrBundle ) {
		if ( empty( $this->_submitted['values'] ) ) {
			return;
		}

		try {
			$this->_application->Entity_Save( $entityOrBundle, $this->_submitted['values'], $this->_submitted['extra_args'] );
		} catch ( Exception\IException $e ) {
			$this->_application->logError( $e );
		}
	}

	public function manageUsersColumnsFilter( $columns ) {
		if ( $user_bundle = $this->_application->getComponent( 'User' )->userDirectoryExists() ) {
			foreach ( $this->_application->Entity_Field( $user_bundle ) as $field ) {
				if ( ( ! $field_type = $this->_application->Field_Type( $field->getFieldType(), true ) )
					|| ! $field_type instanceof Field\Type\IColumnable
					|| ( ! $info = $field_type->fieldColumnableInfo( $field ) )
				) {
					continue;
				}

				foreach ( $info as $column_key => $column_info ) {
					$label = ucwords( $this->_application->H( $column_info['label'] ) );
					if ( isset( $column_info['icon'] ) ) {
						$label = '<i class="drts-' . $this->_application->H( $column_info['icon'] ) . '" title="' . $label . '"></i> '
							. '<span style="display:none;">' . $label . '</span>'; // need this to be visible in screen options
					}
					$column_name = 'drts_field_' . $field->getFieldName();
					if ( strlen( $column_key ) ) {
						$column_name .= '-' . $column_key;
					}
					$columns[ $column_name ] = $label;
				}
			}

			// Move columns to last
			foreach ( array( 'posts' ) as $column_name ) {
				if ( ! isset( $columns[ $column_name ] ) ) {
					continue;
				}

				$column_label = $columns[ $column_name ];
				unset( $columns[ $column_name ] );
				$columns[ $column_name ] = $column_label;
			}
		}

		return $columns;
	}

	public function manageUsersCustomColumnFilter( $value, $column, $userId ) {
		if ( ( ! $user_bundle = $this->_application->getComponent( 'User' )->userDirectoryExists() )
			|| strpos( $column, 'drts_field_' ) !== 0
		) {
			return $value;
		}

		$field_name       = substr( $column, strlen( 'drts_field_' ) );
		$field_column_key = '';
		if ( strpos( $field_name, '-' ) ) {
			if ( ! $field_name = explode( '-', $field_name ) ) {
				return $value;
			}

			if ( isset( $field_name[1] ) ) {
				$field_column_key = $field_name[1];
			}
			$field_name = $field_name[0];
		}
		if ( ( $field = $this->_application->Entity_Field( $user_bundle, $field_name ) )
			&& ( $field_type = $this->_application->Field_Type( $field->getFieldType(), true ) )
			&& $field_type instanceof Field\Type\IColumnable
			&& ( $entity = $this->_application->Entity_Entity( 'user', $userId ) )
			&& ( null !== $column_content = $field_type->fieldColumnableColumn( $field, $entity, $field_column_key ) )
			&& strlen( $column_content )
		) {
			$value = '<div class="drts">' . $column_content . '</div>';
		}
		return $value;
	}

	public function preGetUsersFilter( $query ) {
		if ( $user_bundle = $this->_application->getComponent( 'User' )->userDirectoryExists() ) {
			foreach ( $this->_application->Entity_Field( $user_bundle ) as $field ) {
				$name = 'drts_' . $field->getFieldName();
				if ( empty( $_GET[ $name ] )
					|| ( ! $field_type = $this->_application->Field_Type( $field->getFieldType(), true ) )
					|| ! $field_type instanceof Field\Type\IRestrictable
					|| ( ! $options = $field_type->fieldRestrictableOptions( $field ) )
				) {
					continue;
				}

				foreach ( $options as $option_name => $_options ) {
					if ( empty( $_GET[ $name ][ $option_name ] )
						|| ! array_key_exists( $_GET[ $name ][ $option_name ], $_options )
						|| ( false === $restrict = $field_type->fieldRestrictableRestrict( $field, $_GET[ $name ][ $option_name ], $option_name ) )
					) {
						continue;
					}

					settype( $restrict, 'array' );
					$restrict  += array(
						'column'  => 'value',
						'value'   => $_GET[ $name ][ $option_name ],
						'compare' => '=',
					);
					$meta_query = $query->get( 'meta_query' );
					if ( ! is_array( $meta_query ) ) {
						$meta_query = array();
					}
					$meta_query[] = array(
						'key'     => '_' . $name . '__' . $restrict['column'],
						'value'   => $restrict['value'],
						'compare' => $restrict['compare'],
					);
					$query->set( 'meta_query', $meta_query );
				}
			}
		}
		return $query;
	}

	public function restrictManageUsersAction( $which ) {
		if ( $which === 'bottom'
			|| ( ! $user_bundle = $this->_application->getComponent( 'User' )->userDirectoryExists() )
		) {
			return;
		}

		foreach ( $this->_application->Entity_Field( $user_bundle ) as $field ) {
			if ( ( ! $field_type = $this->_application->Field_Type( $field->getFieldType(), true ) )
				|| ! $field_type instanceof Field\Type\IRestrictable
				|| ! $this->_application->Filter( 'wordpress_admin_users_filter_field', true, array( $field ) )
				|| ( ! $options = $field_type->fieldRestrictableOptions( $field ) )
			) {
				continue;
			}

			foreach ( $options as $option_name => $_options ) {
				$name     = 'drts_' . $field->getFieldName();
				$selected = isset( $_GET[ $name ][ $option_name ] ) ? $_GET[ $name ][ $option_name ] : '';
				if ( ! isset( $_options[''] ) ) {
					$_options = array( '' => sprintf( __( 'Select %s', 'directories-pro' ), $field->getFieldLabel() ) ) + $_options;
				} elseif ( is_array( $_options[''] ) ) {
						$_options[''] = sprintf( __( 'Select %s', 'directories-pro' ), isset( $_options['']['label'] ) ? $_options['']['label'] : $field->getFieldLabel() );
				}

				$is_first    = ! isset( $is_first );
				$margin_left = $is_first ? 'margin-left:16px;' : '';
				$html        = array( '<select name="' . $name . '[' . $option_name . ']" style="float:none;' . $margin_left . '">' );
				foreach ( array_keys( $_options ) as $option_key ) {
					$_selected = $selected && $selected == $option_key ? ' selected="selected"' : '';
					$html[]    = '<option value="' . $this->_application->H( $option_key ) . '"' . $_selected . '>' . $this->_application->H( $_options[ $option_key ] ) . '</option>';
				}
				$html[] = '</select>';
				echo implode( PHP_EOL, $html );
			}
		}
		submit_button( __( 'Filter', 'directories-pro' ), null, $which, false );
	}

	public function defaultHiddenColumnsFilter( $hidden, $screen ) {
		if ( $screen->id !== 'users' ) {
			return $hidden;
		}

		if ( $user_bundle = $this->_application->getComponent( 'User' )->userDirectoryExists() ) {
			foreach ( $this->_application->Entity_Field( $user_bundle ) as $field ) {
				if ( ( ! $field_type = $this->_application->Field_Type( $field->getFieldType(), true ) )
					|| ! $field_type instanceof Field\Type\IColumnable
					|| ( ! $info = $field_type->fieldColumnableInfo( $field ) )
				) {
					continue;
				}

				foreach ( $info as $column_key => $column_info ) {
					if ( ! empty( $column_info['hidden'] )
						|| $field->isCustomField()
					) {
						$column_name = 'drts_field_' . $field->getFieldName();
						if ( strlen( $column_key ) ) {
							$column_name .= '-' . $column_key;
						}
						$hidden[] = $column_name;
					}
				}
			}
		}
		return $hidden;
	}
}
