<?php
namespace SabaiApps\Directories\Component\Faker\Generator;

use SabaiApps\Directories\Component\Field\IField;
use SabaiApps\Directories\Component\Entity\Model\Bundle;
use SabaiApps\Directories\Component\Entity\FieldType\FeaturedFieldType;
use SabaiApps\Directories\Component\Entity\Type\IEntity;
use SabaiApps\Directories\Request;

class EntityGenerator extends AbstractGenerator {

	protected $_users, $_userCount, $_ids = array(), $_idCount = array();

	protected function _fakerGeneratorInfo() {
		$info = parent::_fakerGeneratorInfo();
		switch ( $this->_name ) {
			case 'entity_title':
				$info += array(
					'default_settings' => array(
						'length' => array(
							'min' => 30,
							'max' => 80,
						),
					),
				);
				break;
			case 'entity_published':
				$info += array(
					'default_settings' => array(
						'range' => array( time() - 86400 * 10, time() - 86400 * 1 ),
					),
				);
				break;
			case 'entity_activity':
				$info += array(
					'default_settings' => array(
						'active_at' => time(),
						'edited_at' => time(),
					),
				);
				break;
			case 'entity_views':
				$info += array(
					'default_settings' => array(
						'range' => array(
							'min' => 0,
							'max' => 1000,
						),
					),
				);
				break;
			case 'entity_author':
				$info += array(
					'default_settings' => array(
						'type'  => 'none',
						'num'   => 10,
						'users' => array( $this->_application->getUser()->id ),
					),
				);
				break;
			case 'entity_parent':
				$info += array(
					'default_settings' => array(
						'type'    => 'random',
						'entries' => null,
						'num'     => 100,
					),
				);
				break;
			case 'entity_featured':
				$info += array(
					'add_probability'  => true,
					'default_settings' => array(
						'probability' => 5,
						'priority'    => array( 1, 5, 9 ),
						'featured_at' => array(
							time() - 86400 * 30,
							time() - 86400 * 1,
						),
						'days'        => array(
							'min' => 10,
							'max' => 30,
						),
					),
				);
				break;
			case 'entity_term_parent':
				$info += array(
					'add_probability'  => true,
					'default_settings' => array(
						'probability' => 30,
						'type'        => 'random',
						'num'         => 20,
					),
				);
				break;
			case 'entity_terms':
			case 'entity_reference':
				$info += array(
					'add_probability'  => true,
					'default_settings' => array(
						'type' => 'random',
						'num'  => 1000,
						'max'  => 3,
					),
				);
				break;
		}

		return $info;
	}

	public function fakerGeneratorSettingsForm( IField $field, array $settings, $mode, array $parents = array() ) {
		switch ( $this->_name ) {
			case 'entity_title':
				$min = $max = null;
				if ( $field->getFieldWidget() === 'entity_title' ) {
					$widget_settings = $field->getFieldWidgetSettings();
					$min             = isset( $widget_settings['min'] ) && $widget_settings['min'] > 5 ? $widget_settings['min'] : null;
					$max             = isset( $widget_settings['max'] ) && $widget_settings['max'] > 5 ? $widget_settings['max'] : null;
				}
				if ( ! empty( $field->Bundle->info['is_taxonomy'] ) ) {
					$settings['length'] = array(
						'min' => 5,
						'max' => 30,
					);
				}
				return array(
					'length' => array(
						'#type'          => 'range',
						'#title'         => __( 'Text length range', 'directories-pro' ),
						'#integer'       => true,
						'#min_value'     => isset( $min ) ? $min : min( array( 5, $settings['length']['min'] ) ),
						'#max_value'     => isset( $max ) ? $max : max( 100, $settings['length']['max'] ),
						'#default_value' => array(
							'min' => isset( $min ) ? max( array( $min, $settings['length']['min'] ) ) : $settings['length']['min'],
							'max' => isset( $max ) ? min( array( $max, $settings['length']['max'] ) ) : $settings['length']['max'],
						),
					),
				);
			case 'entity_published':
				return array(
					'range' => $this->_getTimestampSettingForm( $settings['range'] ),
				);
			case 'entity_activity':
				return array(
					'active_at' => array( '#title' => __( 'Last active date range', 'directories-pro' ) ) + $this->_getTimestampSettingForm( $settings['active_at'] ),
					'edited_at' => array( '#title' => __( 'Last edited date range', 'directories-pro' ) ) + $this->_getTimestampSettingForm( $settings['edited_at'] ),
				);
			case 'entity_views':
				return array(
					'range' => array(
						'#type'          => 'range',
						'#title'         => __( 'View count range', 'directories-pro' ),
						'#integer'       => true,
						'#max_value'     => 1000,
						'#description'   => __( 'Select the range of possible number of view counts for the field.', 'directories-pro' ),
						'#default_value' => $settings['range'],
					),
				);
			case 'entity_author':
				return array(
					'type'  => array(
						'#type'          => 'select',
						'#title'         => $field->getFieldLabel(),
						'#options'       => array(
							'none'   => __( 'No author', 'directories-pro' ),
							'admin'  => __( 'Administrators', 'directories-pro' ),
							'users'  => __( 'Specific users', 'directories-pro' ),
							'newest' => __( 'Newest users', 'directories-pro' ),
						),
						'#default_value' => $settings['type'],
					),
					'users' => array(
						'#type'          => 'user',
						'#multiple'      => true,
						'#default_value' => $settings['users'],
						'#states'        => array(
							'visible' => array(
								sprintf( 'select[name="%s[type]"]', $this->_application->Form_Form_fieldName( $parents ) ) => array( 'value' => 'users' ),
							),
						),
						'#required'      => function ( $form ) use ( $parents ) {
							return $form->getValue( array_merge( $parents, array( 'type' ) ) ) === 'users'; },
					),
					'num'   => array(
						'#type'          => 'slider',
						'#title'         => __( 'Max number of users', 'directories-pro' ),
						'#min_value'     => 1,
						'#max_value'     => 100,
						'#default_value' => $settings['num'],
						'#states'        => array(
							'visible' => array(
								sprintf( 'select[name="%s[type]"]', $this->_application->Form_Form_fieldName( $parents ) ) => array( 'value' => 'newest' ),
							),
						),
						'#required'      => function ( $form ) use ( $parents ) {
							return $form->getValue( array_merge( $parents, array( 'type' ) ) ) === 'newest'; },
					),
				);
			case 'entity_parent':
				$parent_bundle = $this->_getParentBundle( $field );
				return array(
					'type'    => array(
						'#type'          => 'select',
						'#title'         => $parent_bundle->getLabel(),
						'#options'       => array(
							'entries' => __( 'Select manually', 'directories-pro' ),
							'random'  => __( 'Random', 'directories-pro' ),
						),
						'#default_value' => $settings['type'],
					),
					'entries' => array(
						'#type'                     => 'autocomplete',
						'#title'                    => $parent_bundle->getLabel( 'select' ),
						'#default_options_callback' => array( array( $this, '_getDefaultOptions' ), array( $parent_bundle->entitytype_name, $parent_bundle->name ) ),
						'#select2'                  => true,
						'#select2_ajax'             => true,
						'#select2_item_text_key'    => 'title',
						'#select2_ajax_url'         => $this->_application->MainUrl(
							'/_drts/entity/' . $parent_bundle->type . '/query',
							array(
								'bundle' => $parent_bundle->name,
								Request::PARAM_CONTENT_TYPE => 'json',
							),
							'',
							'&'
						),
						'#multiple'                 => true,
						'#default_value'            => $settings['entries'],
						'#states'                   => array(
							'visible' => array(
								sprintf( 'select[name="%s[type]"]', $this->_application->Form_Form_fieldName( $parents ) ) => array( 'value' => 'entries' ),
							),
						),
						'#required'                 => function ( $form ) use ( $parents ) {
							return $form->getValue( array_merge( $parents, array( 'type' ) ) ) === 'entries'; },
					),
					'num'     => array(
						'#type'          => 'slider',
						'#title'         => sprintf( __( 'Max number of random %s to fetch from the database', 'directories-pro' ), strtolower( $parent_bundle->getLabel() ), $parent_bundle->getLabel() ),
						'#min_value'     => 1,
						'#max_value'     => 500,
						'#default_value' => $settings['num'],
						'#states'        => array(
							'visible' => array(
								sprintf( 'select[name="%s[type]"]', $this->_application->Form_Form_fieldName( $parents ) ) => array( 'value' => 'random' ),
							),
						),
						'#required'      => function ( $form ) use ( $parents ) {
							return $form->getValue( array_merge( $parents, array( 'type' ) ) ) === 'random'; },
					),
				);
			case 'entity_featured':
				return array(
					'priority'    => array(
						'#type'          => 'checkboxes',
						'#title'         => __( 'Prioirty', 'directories-pro' ),
						'#options'       => FeaturedFieldType::priorities(),
						'#default_value' => $settings['priority'],
					),
					'featured_at' => $this->_getTimestampSettingForm( $settings['featured_at'], __( 'Featured date', 'directories-pro' ) ),
					'days'        => array(
						'#type'          => 'range',
						'#title'         => __( 'Duration', 'directories-pro' ),
						'#min_value'     => 0,
						'#max_value'     => 100,
						'#default_value' => $settings['days'],
						'#description'   => __( 'Select the range of possible durations for the field.', 'directories-pro' ),
					),
				);
			case 'entity_term_parent':
				$taxonomy_bundle = $field->Bundle;
				return array(
					'type'    => array(
						'#type'          => 'select',
						'#title'         => $taxonomy_bundle->getLabel(),
						'#options'       => array(
							'entries' => __( 'Select manually', 'directories-pro' ),
							'random'  => __( 'Random', 'directories-pro' ),
						),
						'#default_value' => $settings['type'],
					),
					'entries' => array(
						'#type'                     => 'autocomplete',
						'#title'                    => $taxonomy_bundle->getLabel( 'select' ),
						'#default_options_callback' => array( array( $this, '_getDefaultOptions' ), array( $taxonomy_bundle->entitytype_name, $taxonomy_bundle->name ) ),
						'#select2'                  => true,
						'#select2_ajax'             => true,
						'#select2_item_text_key'    => 'title',
						'#select2_ajax_url'         => $this->_application->MainUrl(
							'/_drts/entity/' . $taxonomy_bundle->type . '/query',
							array(
								'bundle' => $taxonomy_bundle->name,
								Request::PARAM_CONTENT_TYPE => 'json',
							),
							'',
							'&'
						),
						'#multiple'                 => true,
						'#states'                   => array(
							'visible' => array(
								sprintf( 'select[name="%s[type]"]', $this->_application->Form_Form_fieldName( $parents ) ) => array( 'value' => 'entries' ),
							),
						),
						'#required'                 => function ( $form ) use ( $parents ) {
							return $form->getValue( array_merge( $parents, array( 'type' ) ) ) === 'entries'; },
					),
					'num'     => array(
						'#type'          => 'slider',
						'#title'         => sprintf(
							__( 'Max number of random %s to fetch from the database', 'directories-pro' ),
							strtolower( $taxonomy_bundle->getLabel() ),
							$taxonomy_bundle->getLabel()
						),
						'#min_value'     => 1,
						'#max_value'     => 100,
						'#default_value' => $settings['num'],
						'#states'        => array(
							'visible' => array(
								sprintf( 'select[name="%s[type]"]', $this->_application->Form_Form_fieldName( $parents ) ) => array( 'value' => 'random' ),
							),
						),
						'#required'      => function ( $form ) use ( $parents ) {
							return $form->getValue( array_merge( $parents, array( 'type' ) ) ) === 'random'; },
					),
				);
			case 'entity_terms':
				if ( ! $taxonomy_bundle = $field->getTaxonomyBundle() ) {
					return false;
				}
				return array(
					'type'    => array(
						'#type'          => 'select',
						'#title'         => $taxonomy_bundle->getLabel(),
						'#options'       => array(
							'entries' => __( 'Select manually', 'directories-pro' ),
							'random'  => __( 'Random', 'directories-pro' ),
						),
						'#default_value' => $settings['type'],
					),
					'entries' => array(
						'#type'                     => 'autocomplete',
						'#title'                    => $taxonomy_bundle->getLabel( 'select' ),
						'#default_options_callback' => array( array( $this, '_getDefaultOptions' ), array( $taxonomy_bundle->entitytype_name, $taxonomy_bundle->name ) ),
						'#select2'                  => true,
						'#select2_ajax'             => true,
						'#select2_item_text_key'    => 'title',
						'#select2_ajax_url'         => $this->_application->MainUrl(
							'/_drts/entity/' . $taxonomy_bundle->type . '/query',
							array(
								'bundle' => $taxonomy_bundle->name,
								Request::PARAM_CONTENT_TYPE => 'json',
							),
							'',
							'&'
						),
						'#multiple'                 => true,
						'#states'                   => array(
							'visible' => array(
								sprintf( 'select[name="%s[type]"]', $this->_application->Form_Form_fieldName( $parents ) ) => array( 'value' => 'entries' ),
							),
						),
						'#required'                 => function ( $form ) use ( $parents ) {
							return $form->getValue( array_merge( $parents, array( 'type' ) ) ) === 'entries'; },
					),
					'num'     => array(
						'#type'          => 'slider',
						'#title'         => sprintf(
							__( 'Max number of random %s to fetch from the database', 'directories-pro' ),
							strtolower( $taxonomy_bundle->getLabel() ),
							$taxonomy_bundle->getLabel()
						),
						'#min_value'     => 1,
						'#max_value'     => 1000,
						'#step'          => 10,
						'#default_value' => $settings['num'],
						'#states'        => array(
							'visible' => array(
								sprintf( 'select[name="%s[type]"]', $this->_application->Form_Form_fieldName( $parents ) ) => array( 'value' => 'random' ),
							),
						),
						'#required'      => function ( $form ) use ( $parents ) {
							return $form->getValue( array_merge( $parents, array( 'type' ) ) ) === 'random'; },
					),
					'max'     => $this->_getMaxNumItemsSettingForm( $field, $settings['max'] ),
				);
			case 'entity_reference':
				$field_settings = $field->getFieldSettings();
				if ( empty( $field_settings['bundle'] )
					|| ( ! $ref_bundle = $this->_application->Entity_Bundle( $field_settings['bundle'] ) )
				) {
					return false;
				}

				return array(
					'type'    => array(
						'#type'          => 'select',
						'#title'         => $ref_bundle->getLabel(),
						'#options'       => array(
							'entries' => __( 'Select manually', 'directories-pro' ),
							'random'  => __( 'Random', 'directories-pro' ),
						),
						'#default_value' => $settings['type'],
					),
					'entries' => array(
						'#type'                     => 'autocomplete',
						'#title'                    => $ref_bundle->getLabel( 'select' ),
						'#default_options_callback' => array( array( $this, '_getDefaultOptions' ), array( $ref_bundle->entitytype_name, $ref_bundle->name ) ),
						'#select2'                  => true,
						'#select2_ajax'             => true,
						'#select2_item_text_key'    => 'title',
						'#select2_ajax_url'         => $this->_application->MainUrl(
							'/_drts/entity/' . $ref_bundle->type . '/query',
							array(
								'bundle' => $ref_bundle->name,
								Request::PARAM_CONTENT_TYPE => 'json',
							),
							'',
							'&'
						),
						'#multiple'                 => true,
						'#states'                   => array(
							'visible' => array(
								sprintf( 'select[name="%s[type]"]', $this->_application->Form_Form_fieldName( $parents ) ) => array( 'value' => 'entries' ),
							),
						),
						'#required'                 => function ( $form ) use ( $parents ) {
							return $form->getValue( array_merge( $parents, array( 'type' ) ) ) === 'entries'; },
					),
					'num'     => array(
						'#type'          => 'slider',
						'#title'         => sprintf(
							__( 'Max number of random %s to fetch from the database', 'directories-pro' ),
							strtolower( $ref_bundle->getLabel() ),
							$ref_bundle->getLabel()
						),
						'#min_value'     => 1,
						'#max_value'     => 1000,
						'#step'          => 10,
						'#default_value' => $settings['num'],
						'#states'        => array(
							'visible' => array(
								sprintf( 'select[name="%s[type]"]', $this->_application->Form_Form_fieldName( $parents ) ) => array( 'value' => 'random' ),
							),
						),
						'#required'      => function ( $form ) use ( $parents ) {
							return $form->getValue( array_merge( $parents, array( 'type' ) ) ) === 'random'; },
					),
					'max'     => $this->_getMaxNumItemsSettingForm( $field, $settings['max'] ),
				);
		}
	}

	public function _getDefaultOptions( $defaultValue, array &$options, $entityType, $bundleName ) {
		foreach ( $this->_application->Entity_Types_impl( $entityType )->entityTypeEntitiesByIds( $defaultValue, $bundleName ) as $entity ) {
			$options[ $entity->getId() ] = $this->_application->Entity_Title( $entity );
		}
	}

	public function fakerGeneratorGenerate( IField $field, array $settings, array &$formStorage, ?IEntity $entity = null ) {
		switch ( $this->_name ) {
			case 'entity_title':
				return str_replace( '.', '', $this->_getFaker()->text( mt_rand( $settings['length']['min'] + 1, $settings['length']['max'] + 1 ) ) );
			case 'entity_published':
				return $this->_generateTimestamp( $settings['range'] );
			case 'entity_activity':
				return array(
					array(
						'active_at' => $this->_generateTimestamp( $settings['active_at'] ),
						'edited_at' => $this->_generateTimestamp( $settings['edited_at'] ),
					),
				);
			case 'entity_views':
				return mt_rand( $settings['range']['min'], $settings['range']['max'] );
			case 'entity_author':
				if ( $settings['type'] === 'none' ) {
					return 0;
				}
				if ( $settings['type'] === 'users' ) {
					return $settings['users'][ mt_rand( 0, count( $settings['users'] ) - 1 ) ];
				}
				if ( ! isset( $this->_users ) ) {
					if ( $settings['type'] === 'admin' ) {
						$this->_users = array_values( $this->_application->getPlatform()->getAdministrators() );
					} else {
						$this->_users = $this->_application
							->getPlatform()
							->getUserIdentityFetcher()
							->fetch( $settings['num'], 0, 'timestamp', 'ASC' );
					}
					$this->_userCount = count( $this->_users );
				}
				return empty( $this->_userCount ) ? null : $this->_users[ mt_rand( 0, $this->_userCount - 1 ) ]->id;
			case 'entity_parent':
				if ( ! isset( $this->_ids[ $this->_name ] ) ) {
					if ( $settings['type'] === 'entries' ) {
						$this->_ids[ $this->_name ] = $settings['entries'];
					} else {
						if ( ( ! $bundle = $this->_getParentBundle( $field ) )
							|| ( ! $entity_type_impl = $this->_application->Entity_Types_impl( $bundle->entitytype_name, true ) )
						) {
							return false;
						}

						$this->_ids[ $this->_name ] = $entity_type_impl->entityTypeRandomEntityIds( $bundle->name, $settings['num'] );
					}
					$this->_idCount[ $this->_name ] = count( $this->_ids[ $this->_name ] );
				}
				return empty( $this->_idCount[ $this->_name ] ) ? null : $this->_ids[ $this->_name ][ mt_rand( 0, $this->_idCount[ $this->_name ] - 1 ) ];
			case 'entity_featured':
				return array(
					array(
						'value'       => $settings['priority'][ array_rand( $settings['priority'] ) ],
						'featured_at' => $featured_at = $this->_generateTimestamp( $settings['featured_at'] ),
						'expires_at'  => empty( $settings['days']['min'] ) && empty( $settings['days']['max'] ) ? 0 : $featured_at + ( 86400 * mt_rand( $settings['days']['min'], $settings['days']['max'] ) ),
					),
				);
			case 'entity_term_parent':
				if ( ! isset( $this->_ids[ $this->_name ] ) ) {
					if ( $settings['type'] === 'entries' ) {
						$this->_ids[ $this->_name ] = $settings['entries'];
					} else {
						if ( ( ! $entity_type_impl = $this->_application->Entity_Types_impl( $field->Bundle->entitytype_name, true ) )
							|| ! $entity_type_impl->entityTypeCount( $field->Bundle->name )
						) {
							return false;
						}

						$this->_ids[ $this->_name ] = $entity_type_impl->entityTypeRandomEntityIds( $field->Bundle->name, $settings['num'] );
					}
					$this->_idCount[ $this->_name ] = count( $this->_ids[ $this->_name ] );
				}
				if ( ! $this->_idCount[ $this->_name ] ) {
					return false;
				}

				return $this->_ids[ $this->_name ][ mt_rand( 0, $this->_idCount[ $this->_name ] - 1 ) ];
			case 'entity_terms':
			case 'entity_reference':
				if ( $this->_name === 'entity_terms' ) {
					if ( ! $bundle = $field->getTaxonomyBundle() ) {
						return false;
					}
				} elseif ( $this->_name === 'entity_reference' ) {
					$field_settings = $field->getFieldSettings();
					if ( empty( $field_settings['bundle'] )
						|| ( ! $bundle = $this->_application->Entity_Bundle( $field_settings['bundle'] ) )
					) {
						return false;
					}
				} else {
					return false;
				}

				if ( ! isset( $this->_ids[ $this->_name ][ $bundle->name ] ) ) {
					if ( $settings['type'] === 'entries' ) {
						$this->_ids[ $this->_name ][ $bundle->name ] = $settings['entries'];
					} else {
						if ( ! $entity_type_impl = $this->_application->Entity_Types_impl( $bundle->entitytype_name, true ) ) {
							return false;
						}

						if ( $this->_name === 'entity_terms' ) {
							if ( ! $entity_type_impl->entityTypeCount( $bundle->name ) ) {
								return false;
							}
						}
						if ( ! $ids = $entity_type_impl->entityTypeRandomEntityIds( $bundle->name, $settings['num'] ) ) {
							return false;
						}

						$this->_ids[ $this->_name ][ $bundle->name ] = $ids;
					}
					$this->_idCount[ $this->_name ][ $bundle->name ] = count( $this->_ids[ $this->_name ][ $bundle->name ] );
				}
				if ( ! $this->_idCount[ $this->_name ][ $bundle->name ] ) {
					return false;
				}

				$ret   = array();
				$count = $this->_getMaxNumItems( $field, $settings['max'] );
				for ( $i = 0; $i < $count; ++$i ) {
					$id         = $this->_ids[ $this->_name ][ $bundle->name ][ mt_rand( 0, $this->_idCount[ $this->_name ][ $bundle->name ] - 1 ) ];
					$ret[ $id ] = $id;
				}
				return array_values( $ret );
		}
	}

	public function fakerGeneratorSupports( Bundle $bundle, IField $field ) {
		switch ( $this->_name ) {
			case 'entity_parent':
				return (bool) $this->_getParentBundle( $field );
			case 'entity_term_parent':
				return ! empty( $bundle->info['is_hierarchical'] );
			case 'entity_terms':
				return ( $bundle = $field->getTaxonomyBundle() )
					&& true !== $this->_application->Entity_BundleTypeInfo( $bundle, 'faker_disable_entity_terms' );
			case 'entity_reference':
				$field_settings = $field->getFieldSettings();
				return ! empty( $field_settings['bundle'] )
					&& $this->_application->Entity_Bundle( $field_settings['bundle'] );
		}
		return true;
	}

	protected function _getParentBundle( $field ) {
		if ( empty( $field->Bundle->info['parent'] )
			|| ( ! $parent_bundle = $field->Bundle->info['parent'] )
		) {
			return false;
		}
		return $this->_application->Entity_Bundle( $parent_bundle );
	}
}
