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

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

class GeneratorsHelper {

	/**
	 * Returns all available field generators
	 *
	 * @param  $application
	 */
	public function help( Application $application, $byFieldType = false, $useCache = true ) {
		$cache_id = $byFieldType ? 'faker_generators_by_field_type' : 'faker_generators';
		if ( ! $useCache
			|| ( ! $ret = $application->getPlatform()->getCache( $cache_id ) )
		) {
			$generators = $generators_by_field_type = array();
			foreach ( $application->InstalledComponentsByInterface( 'Faker\IGenerators' ) as $component_name ) {
				if ( ! $application->isComponentLoaded( $component_name ) ) {
					continue;
				}

				foreach ( $application->getComponent( $component_name )->fakerGetGeneratorNames() as $generator_name ) {
					if ( ! $generator = $application->getComponent( $component_name )->fakerGetGenerator( $generator_name ) ) {
						continue;
					}

					$generators[ $generator_name ] = $component_name;
					foreach ( (array) $generator->fakerGeneratorInfo( 'field_types' ) as $field_type ) {
						$generators_by_field_type[ $field_type ][] = $generator_name;
					}
				}
			}
			$application->getPlatform()->setCache( $generators, 'faker_generators' )
				->setCache( $generators_by_field_type, 'faker_generators_by_field_type' );

			$ret = $byFieldType ? $generators_by_field_type : $generators;
		}

		return $ret;
	}

	private $_impls = array();

	/**
	 * Gets an implementation of Faker\IGenerator interface for a given generator type
	 *
	 * @param Application $application
	 * @param string      $generator
	 * @return SabaiApps\Directories\Component\Faker\Generator\IGenerator
	 */
	public function impl( Application $application, $generator, $returnFalse = false ) {
		if ( ! isset( $this->_impls[ $generator ] ) ) {
			$generators = $this->help( $application );
			// Valid generator type?
			if ( ! isset( $generators[ $generator ] )
				|| ( ! $application->isComponentLoaded( $generators[ $generator ] ) )
			) {
				if ( $returnFalse ) {
					return false;
				}
				throw new Exception\UnexpectedValueException( sprintf( 'Invalid generator: %s', $generator ) );
			}
			$this->_impls[ $generator ] = $application->getComponent( $generators[ $generator ] )->fakerGetGenerator( $generator );
		}

		return $this->_impls[ $generator ];
	}

	public function settingsForm( Application $application, IField $field, $mode, array $parents = array(), $isTopLevel = true ) {
		$generators_by_field_type = $this->help( $application, true );
		if ( empty( $generators_by_field_type[ $field->getFieldType() ] ) ) {
			return;
		}

		if ( ! $bundle = $field->Bundle ) {
			return;
		}

		$form                      = array();
		$last_valid_generator_name = null;
		foreach ( $generators_by_field_type[ $field->getFieldType() ] as $generator_name ) {
			if ( ( ! $generator = $this->impl( $application, $generator_name, true ) )
				|| ! $generator->fakerGeneratorSupports( $bundle, $field )
			) {
				continue;
			}

			if ( ! isset( $form['generator'] ) ) {
				$form['generator']          = array(
					'#type'               => 'radios',
					'#title'              => __( 'Field generator', 'directories-pro' ),
					'#options'            => array(),
					'#horizontal'         => true,
					'#default_value'      => $field->getFieldType(),
					'#default_value_auto' => true,
				);
				$form['generator_settings'] = array();
			}

			$info = $generator->fakerGeneratorInfo();
			$form['generator']['#options'][ $generator_name ] = $info['label'] ?? __( 'Default', 'directories-pro' );
			if ( isset( $info['description'] ) ) {
				$form['generator']['#options_description'][ $generator_name ] = $info['description'];
			}
			$_parents      = array_merge( $parents, array( 'generator_settings', $generator_name ) );
			$settings_form = (array) $generator->fakerGeneratorSettingsForm( $field, $info['default_settings'] ?? array(), $mode, $_parents );
			if ( ! empty( $info['add_probability'] )
				&& $isTopLevel
			) {
				$settings_form = array(
					'probability' => array(
						'#title'         => __( 'Probability of generating value for this field', 'directories-pro' ),
						'#type'          => 'slider',
						'#field_suffix'  => '%',
						'#min_value'     => 0,
						'#max_value'     => 100,
						'#integer'       => true,
						'#default_value' => $info['default_settings']['probability'] ?? 100,
					),
				) + $settings_form;
			}
			if ( $settings_form ) {
				foreach ( array_keys( $settings_form ) as $key ) {
					if ( strpos( $key, '#' ) !== 0 ) {
						$settings_form[ $key ]['#horizontal'] = true;
					}
				}
				$form['generator_settings'][ $generator_name ]                       = $settings_form;
				$form['generator_settings'][ $generator_name ]['#states']['visible'] = array(
					sprintf( '[name="%s[generator]"]', $application->Form_Form_fieldName( $parents ) ) => array( 'value' => $generator_name ),
				);
			}
			$last_valid_generator_name = $generator_name;
		}

		if ( ! empty( $form ) ) {
			if ( ! empty( $form['generator_settings'] ) ) {
				$form['#title']           = $isTopLevel ? $this->fieldLabel( $application, $field ) : $application->H( $field->getFieldLabel() );
				$form['#title_no_escape'] = true;
			}
			if ( count( $form['generator']['#options'] ) === 1 ) {
				$form['generator']['#type']  = 'hidden';
				$form['generator']['#value'] = $last_valid_generator_name;
			}
		}

		return $form;
	}

	public function fieldLabel( Application $application, IField $field ) {
		$label = $application->H( $field->getFieldLabel() ) . ' (' . $field->getFieldName() . ')';
		if ( $field->isCustomField() ) {
			$label = '<span class="' . DRTS_BS_PREFIX . 'badge ' . DRTS_BS_PREFIX . 'bg-secondary">' . $application->H( __( 'Custom field', 'directories-pro' ) ) . '</span> ' . $label;
		}
		return $label;
	}

	public function generate( Application $application, IField $field, array $settings, array &$formStorage, ?IEntity $entity = null ) {
		if ( ! isset( $settings['generator'] )
			|| ( ! $generator = $this->impl( $application, $settings['generator'], true ) )
			|| ! $generator->fakerGeneratorSupports( $field->Bundle, $field )
		) {
			return;
		}

		$_settings = $settings['generator_settings'][ $settings['generator'] ] ?? array();

		if ( $generator->fakerGeneratorInfo( 'add_probability' )
			&& isset( $_settings['probability'] )
			&& mt_rand( 0, 100 ) > $_settings['probability']
		) {
			return;
		}

		return $generator->fakerGeneratorGenerate( $field, $_settings, $formStorage, $entity );
	}
}
