<?php
namespace SabaiApps\Directories\Component\Directory\Controller\Admin;

use SabaiApps\Directories\Context;
use SabaiApps\Directories\Component\Form;
use SabaiApps\Directories\Component\Directory\Model\Directory;
use SabaiApps\Directories\Exception;

class AddDirectory extends Form\Controller {

	protected function _doGetFormSettings( Context $context, array &$formStorage ) {
		$context->addTemplate( 'system_progress' );
		$this->_ajaxSubmit       = true;
		$this->_ajaxOnSubmit     = $this->System_Progress_formSubmitJs( 'directory_add' );
		$this->_ajaxLoadingImage = false;
		$this->_submitButtons[]  = array(
			'#btn_label'  => __( 'Add Directory', 'directories-pro' ),
			'#btn_color'  => 'success',
			'#attributes' => array( 'data-modal-title' => 'false' ),
			'#btn_size'   => 'lg',
		);
		$form                    = array(
			'#enable_storage' => true,
			'general'         => array(
				'#weight' => 2,
				'#tree'   => false,
				'method'  => array(
					'#weight'              => -1,
					'#title'               => 'Choose a method',
					'#type'                => 'radios',
					'#options'             => array(
						'configure' => __( 'Configure a new directory', 'directories-pro' ),
						'import'    => __( 'Import from a JSON file', 'directories-pro' ),
					),
					'#options_description' => array(
						'configure' => __( 'Enter configuration details of your directory.', 'directories-pro' ),
						'import'    => __( 'Load configuration details from an exported JSON file.', 'directories-pro' ),
					),
					'#default_value'       => 'configure',
					'#horizontal'          => true,
					'#required'            => true,
				),
				'label'   => array(
					'#type'        => 'textfield',
					'#title'       => __( 'Directory label', 'directories-pro' ),
					'#description' => __( 'Enter a label used for administration purpose only.', 'directories-pro' ),
					'#max_length'  => 255,
					'#horizontal'  => true,
					'#weight'      => 2,
					'#states'      => array(
						'visible'     => array(
							'[name="method"]' => array( 'value' => 'configure' ),
						),
						'placeholder' => array(
							'[name="type"]' => array(
								'type'  => 'selected',
								'value' => true,
							),
						),
					),
					'#data'        => array(),
					'#required'    => array( __CLASS__, '_isDirectoryInfoRequired' ),
				),
				'name'    => array(
					'#type'                => 'textfield',
					'#title'               => __( 'Directory name', 'directories-pro' ),
					'#description'         => ( $message = __( 'Enter a machine readable name which may not be changed later. Only lowercase alphanumeric characters and underscores are allowed and may not start or end with an underscore.', 'directories-pro' ) ),
					'#max_length'          => 12,
					'#regex'               => '/^[a-z0-9]([a-z0-9_]*[a-z0-9])?$/',
					'#regex_error_message' => $message,
					'#horizontal'          => true,
					'#states'              => array(
						'visible' => array(
							'[name="method"]' => array( 'value' => 'configure' ),
						),
						'slugify' => array(
							'input[name="label"]' => array(
								'type'  => 'filled',
								'value' => true,
							),
						),
					),
					'#element_validate'    => array( array( $this, '_validateName' ) ),
					'#weight'              => 3,
					'#required'            => array( __CLASS__, '_isDirectoryInfoRequired' ),
				),
				'icon'    => array(
					'#type'          => 'iconpicker',
					'#title'         => __( 'Directory icon', 'directories-pro' ),
					'#horizontal'    => true,
					'#default_value' => 'fas fa-map-location-dot',
					'#weight'        => 10,
					'#required'      => array( __CLASS__, '_isDirectoryInfoRequired' ),
					'#states'        => array(
						'visible' => array(
							'[name="method"]' => array( 'value' => 'configure' ),
						),
					),
				),
				'type'    => array(
					'#title'         => __( 'Directory type', 'directories-pro' ),
					'#description'   => __( 'Select the type of directory. This may not be changed later.', 'directories-pro' ),
					'#default_value' => 'directory',
					'#type'          => 'select',
					'#horizontal'    => true,
					'#options'       => array(),
					'#weight'        => 1,
					'#empty_value'   => '',
					'#required'      => array( __CLASS__, '_isDirectoryInfoRequired' ),
					'#states'        => array(
						'visible' => array(
							'[name="method"]' => array( 'value' => 'configure' ),
						),
					),
				),
				'import'  => array(
					'#tree'        => false,
					'#states'      => array(
						'visible' => array(
							'[name="method"]' => array( 'value' => 'import' ),
						),
					),
					'json'         => array(
						'#type'               => 'file',
						'#title'              => __( 'JSON configuration file', 'directories-pro' ),
						'#upload_dir'         => $this->getComponent( 'System' )->getTmpDir(),
						'#allowed_extensions' => array( 'json' ),
						'#required'           => array( __CLASS__, '_isJsonRequired' ),
						'#multiple'           => false,
						'#horizontal'         => true,
					),
					'custom_label' => array(
						'#type'        => 'textfield',
						'#title'       => __( 'Directory label', 'directories-pro' ),
						'#description' => __( 'Enter a new value. Leave it blank to use the value from the JSON file.', 'directories-pro' ),
						'#max_length'  => 255,
						'#horizontal'  => true,
					),
					'custom_name'  => array(
						'#type'        => 'textfield',
						'#title'       => __( 'Directory name', 'directories-pro' ),
						'#description' => __( 'Enter a machine-readable name for the directory. Leave blank to use the name in the JSON file. Changing this is not recommended if the directory contains fieldsets or is referenced by other directories.', 'directories-pro' ),
						'#max_length'  => 12,
						'#regex'       => '/^[a-z0-9_]+$/',
						'#horizontal'  => true,
					),
					'custom_icon'  => array(
						'#type'          => 'iconpicker',
						'#title'         => __( 'Directory icon', 'directories-pro' ),
						'#horizontal'    => true,
						'#default_value' => null,
						'#description'   => __( 'Enter a new value. Leave it blank to use the value from the JSON file.', 'directories-pro' ),
					),
				),
			),
			'settings'        => array(
				'#tree'   => true,
				'#weight' => 20,
				'#states' => array(
					'visible' => array(
						'[name="method"]' => array( 'value' => 'configure' ),
					),
				),
			),
		);
		foreach ( array_keys( $this->Directory_Types() ) as $directory_type_name ) {
			if ( ! $directory_type = $this->Directory_Types_impl( $directory_type_name, true ) ) {
				continue;
			}

			$form['general']['type']['#options'][ $directory_type_name ] = $directory_type->directoryInfo( 'label' );

			// Disable selecting this directory type if only one allowed and one already exists.
			if ( $directory_type->directoryInfo( 'multiple' ) === false
				&& $this->getModel( 'Directory', 'Directory' )->type_is( $directory_type_name )->count()
			) {
				$form['general']['type']['#options_disabled'][] = $directory_type_name;
			}

			$form['general']['label']['#attributes'][ 'data-placeholder-' . str_replace( '_', '-', $directory_type_name ) ] = $directory_type->directoryInfo( 'label' );

			// Add directory type specific settings
			$form['settings'][ $directory_type_name ]            = $this->Directory_Types_settingsForm(
				$directory_type_name,
				array(),
				array( 'settings', $directory_type_name ),
				$this->_getSubimttedValues( $context, $formStorage )
			);
			$form['settings'][ $directory_type_name ]['#states'] = array(
				'visible' => array(
					'[name="type"]' => array( 'value' => $directory_type_name ),
				),
			);

			// Do not allow setting directory name if has a system name
			if ( $directory_type->directoryInfo( 'name' ) ) {
				$form['name']['#states']['invisible_or']['[name="type"]']['value'][] = $directory_type_name;
			}
		}
		// Hide directory type selection if only single option available
		if ( count( $form['general']['type']['#options'] ) === 1 ) {
			$form['general']['type']['#type']          = 'hidden';
			$form['general']['type']['#default_value'] = current( array_keys( $form['general']['type']['#options'] ) );
		}

		return $form;
	}

	public function _validateName( Form\Form $form, &$value, $element ) {
		if ( $form->values['method'] !== 'configure' ) {
			return;
		}

		$value = trim( trim( $value ), '_' );
		if ( ! strlen( $value ) ) {
			$form->setError( __( 'Directory name may not be empty.', 'directories-pro' ), $element );
			return;
		}
		if ( in_array( strtolower( $value ), array( 'drts', 'add', 'settings', 'system' ) )
			|| $this->getModel( 'Directory', 'Directory' )->name_is( $value )->count()
			|| ! $this->Filter( 'directory_validate_name', true, array( $value ) )
		) {
			$form->setError( __( 'The name may not be used or is already taken.', 'directories-pro' ), $element );
		}
	}

	public function submitForm( Form\Form $form, Context $context ) {
		$values = $form->values;

		$progress = $this->System_Progress( 'directory_add' )->start( 3, __( 'Adding directory (%1$d/%2$d) ... %3$s', 'directories-pro' ) );

		$extra_data = null;
		if ( ! $directory = $this->_createDirectory( $form, $extra_data ) ) {
			$progress->done();
			return;
		}

		$progress->set( 'Directory initialized' );

		$bundles_created = array();
		if ( $bundles = $this->_getBundles( $form, $directory ) ) {
			foreach ( $this->Entity_Admin_createEntityBundles( $bundles, $directory->name ) as $bundle ) {
				$bundles_created[ $bundle->type ] = $bundle;
			}
			unset( $bundles );
		}

		$progress->set( 'Content types initialized' );

		$this->getPlatform()->deleteCache( 'wordpress_admin_menu_post_type_icons' );

		$this->Action( 'directory_admin_directory_added', array( $directory, $values, $bundles_created, $extra_data ) );

		// Run upgrade process to notify directory slugs have been updated
		$this->System_Component_upgradeAll( array_keys( $this->System_Slugs() ) );

		// Reload all routes
		$this->getComponent( 'System' )->reloadAllRoutes();

		$progress->set( 'Components reloaded' );

		// Clear available widgets cache
		$this->getPlatform()->deleteCache( 'system_widgets' );

		$progress->done();

		$context->setSuccess( '/directories/' . $directory->name )
			->addFlash( __( 'Directory created.', 'directories-pro' ) );
	}

	protected function _createDirectory( Form\Form $form, &$extra_data ) {
		switch ( $form->values['method'] ) {
			case 'configure':
				return $this->_configureDirectory( $form, $extra_data );
			case 'import':
				return $this->_importDirectory( $form, $extra_data );
			default:
				throw new Exception\RuntimeException( 'Invalid method requested for adding a directory.' );
		}
	}

	protected function _configureDirectory( Form\Form $form, &$extra_data ) {
		$values = $form->values;
		if ( ! $directory_impl = $this->Directory_Types_impl( $values['type'], true ) ) {
			return;
		}

		// Abort if directory type allows one directory only and one already exists.
		if ( $directory_impl->directoryInfo( 'multiple' ) === false
			&& $this->getModel( 'Directory', 'Directory' )->type_is( $values['type'] )->count()
		) {
			return;
		}

		$settings        = empty( $values['settings'][ $values['type'] ] ) ? array() : $values['settings'][ $values['type'] ];
		$directory       = $this->getModel( 'Directory', 'Directory' )->create();
		$directory->name = ( $name = $directory_impl->directoryInfo( 'name' ) ) ? $name : $values['name'];
		$directory->type = $values['type'];
		$directory->data = array(
			'label'    => $values['label'],
			'icon'     => $values['icon'],
			'settings' => $settings,
		);
		$directory->markNew()->commit();

		return $directory;
	}

	protected function _importDirectory( Form\Form $form, &$extra_data ) {
		$values = $form->values;
		if ( empty( $values['json']['saved_file_path'] )
			|| false === ( $json = file_get_contents( $values['json']['saved_file_path'] ) )
		) {
			$form->setError( __( 'JSON configuration file was not uploaded.', 'directories-pro' ) );
			return;
		}
		if ( ! $decoded = json_decode( $json, true ) ) {
			$form->setError( __( 'Invalid JSON data', 'directories-pro' ), 'json' );
			return;
		}

		// Check directory type
		if ( ! $directory_type = $this->Directory_Types_impl( $decoded['type'], true ) ) {
			$form->setError( __( 'Invalid directory type', 'directories-pro' ) );
			return;
		}
		// Multiple directories allowed?
		if ( $directory_type->directoryInfo( 'multiple' ) === false
			&& $this->getModel( 'Directory', 'Directory' )->type_is( $decoded['type'] )->count()
		) {
			$form->setError( sprintf( __( 'You may not create multiple directories with this directory type: %s', 'directories-pro' ), $decoded['type'] ) );
			return;
		}

		if ( isset( $values['custom_name'] ) && strlen( $values['custom_name'] ) ) {
			$decoded['name'] = $values['custom_name'];
			$element         = 'custom_name';
		} else {
			$element = 'json';
		}
		$this->_validateName( $form, $decoded['name'], $element );
		if ( $form->hasError() ) {
			return;
		}

		foreach ( array( 'label', 'icon' ) as $key ) {
			if ( isset( $values[ 'custom_' . $key ] )
				&& strlen( $values[ 'custom_' . $key ] )
			) {
				$decoded[ $key ] = $values[ 'custom_' . $key ];
			}
		}
		$form->values['data'] = $decoded;
		$directory            = $this->getModel( 'Directory', 'Directory' )->create();
		$directory->name      = $decoded['name'];
		$directory->type      = $decoded['type'];
		$directory->data      = $decoded['data'];
		$extra_data           = $decoded;

		return $directory->markNew()->commit();
	}

	protected function _getBundles( Form\Form $form, Directory $directory ) {
		// Create bundles for each content type in the directory
		$bundles        = array();
		$directory_type = $this->Directory_Types_impl( $directory->type );
		$settings       = $directory->data['settings'];
		foreach ( $directory_type->directoryInfo( 'content_types' ) as $content_type ) {
			if ( ! $content_type_info = $this->_application->Filter(
				'directory_content_type_info',
				$directory_type->directoryContentTypeInfo( $content_type ),
				array( $content_type, isset( $settings[ $content_type ] ) ? $settings[ $content_type ] : array() )
			) ) {
				continue;
			}

			// Convert content type info to bundle type info
			$bundles[ $directory->type . '__' . $content_type ] = $this->Directory_Types_entityBundleTypeInfo( $directory->type, $content_type, $content_type_info );
		}
		if ( $form->values['method'] === 'import' ) {
			$data     = $form->values['data'];
			$_bundles = array();
			foreach ( array_keys( $data['bundles'] ) as $bundle_type ) {
				if ( empty( $data['bundles'][ $bundle_type ]['info'] ) ) {
					continue;
				}

				$_bundles[ $bundle_type ]  = $data['bundles'][ $bundle_type ]['info'];
				$_bundles[ $bundle_type ] += $data['bundles'][ $bundle_type ];
				if ( isset( $bundles[ $bundle_type ] ) ) {
					$_bundles[ $bundle_type ] += $bundles[ $bundle_type ];
				}
			}
			$bundles = $this->Filter( 'directory_admin_directory_import_bundles', $_bundles, array( $directory ) );
		}
		return $bundles;
	}

	public static function _isDirectoryInfoRequired( Form\Form $form ) {
		return $form->getValue( 'method' ) === 'configure';
	}

	public static function _isJsonRequired( Form\Form $form ) {
		return $form->getValue( 'method' ) === 'import';
	}
}
