<?php
namespace SabaiApps\Directories\Component\Fieldset\FieldType;

use SabaiApps\Directories\Application;
use SabaiApps\Directories\Component\Display\FormField\ElementsFormField;
use SabaiApps\Directories\Component\Field\Type\AbstractValueType;
use SabaiApps\Directories\Component\Field\IField;
use SabaiApps\Directories\Component\Entity\Model\Bundle;
use SabaiApps\Directories\Component\Entity\Type\IEntity;
use SabaiApps\Directories\Component\Field\Query;
use SabaiApps\Directories\Component\Field\Type\IColumnable;
use SabaiApps\Directories\Component\Field\Type\IQueryable;
use SabaiApps\Directories\Component\Form\Form;

class FieldsetFieldType extends AbstractValueType implements IColumnable, IQueryable {

	protected function _fieldTypeInfo() {
		return array(
			'label'                              => __( 'Fieldset', 'directories-pro' ),
			'icon'                               => 'fas fa-list-alt',
			'default_settings'                   => array(
				'bundle' => null,
			),
			'schema_type'                        => 'entity_reference',
			'containable'                        => true,
			'designable'                         => array( 'margin' ),
			'entity_form_display_element_type'   => 'utility',
			'entity_form_display_element_children_callback' => array( __CLASS__, 'displayFormElementChildren' ),
			'entity_field_display_element_limit' => 1,
		);
	}

	public function fieldTypeSettingsForm( $fieldType, Bundle $bundle, array $settings, array $parents = array(), array $rootParents = array() ) {
		return array(
			'bundle' => array(
				'#type'             => 'hidden',
				'#value'            => $settings['bundle'],
				'#element_validate' => array(
					function ( Form $form, &$value, $element ) use ( $bundle ) {
						$values = $form->getValue();
						$label  = $values['general']['settings']['label'];
						if ( ! empty( $value ) ) {
							if ( $fieldset_bundle = $this->_application->Entity_Bundle( $value ) ) {
								$fieldset_bundle->setInfo(
									array(
										'label'          => $label,
										'label_singular' => $label,
									)
								)->commit();
							}
							return;
						}

						$slug           = $this->_application->System_Text_slugify( $label, 4 );
						$possible_slugs = array( $slug );
						$slug           = substr( $slug, 0, 3 );
						for ( $i = 2; $i <= 9; ++$i ) {
							$possible_slugs[] = $slug . $i;
						}
						do {
							if ( ! empty( $possible_slugs ) ) {
								$slug = array_shift( $possible_slugs );
							} else {
								// Try auto-generated
								$slug = substr( str_shuffle( 'abcdefghijklmnopqrstuvwxyz' ), 0, 4 );
							}
							$value = $bundle->group . '_fs_' . $slug;
						} while ( $this->_application->getModel( 'Bundle', 'Entity' )->name_is( $value )->count() );

						// Add fieldset config
						$fieldsets                          = $this->_application->getComponent( 'Fieldset' )->getFieldsets();
						$fieldsets[ $slug ]['label']          = $label;
						$fieldsets[ $slug ]['label_singular'] = $label;
						$fieldsets[ $slug ]['slug']           = $this->_application->System_Text_slugify( $label );
						ksort( $fieldsets );
						$this->_application->System_Component_saveConfig( 'Fieldset', array( 'fieldsets' => $fieldsets ) );
						$this->_application->clearComponentInfoCache();
						$this->_application->Entity_BundleTypes_clearCache();

						// Create bundle
						$bundle_type = 'fieldset_' . $slug;
						$bundles     = $this->_application->Entity_Admin_createEntityBundles(
							array(
								$bundle_type => array(
									'name'           => $value,
									'label'          => $label,
									'label_singular' => $label,
								) + $this->_application->Entity_BundleTypeInfo( $bundle_type ),
							),
							$bundle->group
						);
						$this->_application->Field_Types_clearCache();
						$this->_application->Field_Type_clearCache();

						if ( $fieldset_bundle = array_pop( $bundles ) ) {
							// Create displays
							$this->_application->Display_Create( $fieldset_bundle, 'form', 'default', array( 'elements' => array() ) );
							$this->_application->Display_Create( $fieldset_bundle, 'entity', 'detailed', array() );
							$this->_application->Display_Create( $fieldset_bundle, 'entity', 'summary', array() );
						}
					},
				),
			),
		);
	}

	protected function _getFieldsetBundle( IField $field ) {
		$settings = $field->getFieldSettings();
		if ( empty( $settings['bundle'] ) ) {
			return;
		}

		return $this->_application->Entity_Bundle( $settings['bundle'] );
	}

	public function fieldTypeOnSaveValues( IField $field, array $values, ?array $currentValues = null, array &$extraArgs = array() ) {
		if ( ! $bundle = $this->_getFieldsetBundle( $field ) ) {
			return;
		}

		$ret = array();

		$current_fieldset_ids = array();
		if ( ! empty( $currentValues ) ) {
			foreach ( $currentValues as $current_value ) {
				if ( is_object( $current_value ) ) {
					if ( $current_value instanceof IEntity ) {
						$id = $current_value->getId();
					}
				} else {
					$id = $current_value;
				}
				if ( empty( $id ) ) {
					continue;
				}

				$current_fieldset_ids[ $id ] = (int) $id;
			}
		}

		if ( ! empty( $values ) ) {
			foreach ( $values as $value ) {
				if ( empty( $value ) ) {
					continue;
				}

				$fieldset_entity = null;
				if ( isset( $value['_fieldset_id'] ) ) {
					$id = intval( $value['_fieldset_id'] );
					unset( $value['_fieldset_id'] );
					if ( isset( $current_fieldset_ids[ $id ] ) ) {
						unset( $current_fieldset_ids[ $id ] );
						$fieldset_entity = $this->_application->Entity_Entity( 'fieldset', $id );
					}
				}
				if ( $fieldset_entity ) {
					try {
						$updated_entity = $this->_application->Entity_Save( $fieldset_entity, $value, array( 'skip_empty' => true ) );
					} catch ( \Exception $e ) {
						$this->_application->LogError( 'Failed updating fieldset(' . $bundle->name . '): ' . $e->getMessage() );
						continue;
					}
					if ( $updated_entity ) {
						$ret[]['value'] = (int) $updated_entity->getId();
					} else {
						// Delete this entity since values are empty
						$current_fieldset_ids[ $id ] = $id;
					}
				} else {
					try {
						$new_entity = $this->_application->Entity_Save( $bundle, $value, array( 'skip_empty' => true ) );
					} catch ( \Exception $e ) {
						$this->_application->LogError( 'Failed creating fieldset(' . $bundle->name . '): ' . $e->getMessage() );
						continue;
					}
					if ( $new_entity ) {
						$ret[]['value'] = (int) $new_entity->getId();
					}
				}
			}
		}

		// Delete removed fieldset entities
		if ( ! empty( $current_fieldset_ids ) ) {
			$fieldset_to_delete = array();
			foreach ( $current_fieldset_ids as $id ) {
				if ( ! $fieldset_entity = $this->_application->Entity_Entity( 'fieldset', $id ) ) {
					continue;
				}

				$fieldset_to_delete[ $id ] = $fieldset_entity;
			}
			if ( ! empty( $fieldset_to_delete ) ) {
				$this->_application->getComponent( 'Entity' )->deleteEntities( 'fieldset', $fieldset_to_delete );
			}
		}

		return $ret;
	}

	public function fieldTypeOnValuesSaved( IField $field, array $values, IEntity $entity, ?array $oldValues = null ) {
		if ( empty( $values ) ) {
			return;
		}

		$fieldset_ids = array();
		foreach ( $values as $value ) {
			$fieldset_ids[] = $value['value'];
		}
		foreach ( $this->_application->Entity_Entities( 'fieldset', $fieldset_ids, false ) as $fieldset ) {
			if ( ! $fieldset->getParentId() ) {
				$this->_application->Entity_Save( $fieldset, array( 'parent' => $entity->getId() ) );
			}
		}
	}

	public function fieldTypeOnRemoved( IField $field ) {
		$settings = $field->getFieldSettings();
		if ( empty( $settings['bundle'] )
			|| ( ! $bundle = $this->_application->Entity_Bundle( $settings['bundle'] ) )
		) {
			return;
		}

		$bundles_to_delete = array( $bundle->name => $bundle );
		$field_configs     = array();
		$this->_loadChildFieldsetBundles( $bundle, $bundles_to_delete, $field_configs );
		$this->_application->Entity_Admin_deleteEntityBundles( $bundles_to_delete, true );
		foreach ( $field_configs as $field_config ) {
			if ( ! count( $field_config->Fields ) ) {
				$field_config->markRemoved()->commit();
				$this->_application->getComponent( 'Entity' )->deleteFieldStorage( array( $field_config ) );
			}
		}

		$this->_application->Field_Types_clearCache();
		$this->_application->Field_Type_clearCache();
	}

	public function fieldTypeReadableInfo( IField $field ) {
		$info = parent::fieldTypeReadableInfo( $field );
		if ( ( $settings = $field->getFieldSettings() )
			&& ! empty( $settings['bundle'] )
			&& ( $bundle = $this->_application->Entity_Bundle( $settings['bundle'] ) )
		) {
			$info['bundle'] = array(
				'label' => __( 'Fieldset type', 'directories-pro' ),
				'value' => $bundle->getLabel() . ' (' . $bundle->name . ')',
			);
		}
		return $info;
	}

	protected function _loadChildFieldsetBundles( Bundle $bundle, array &$bundles, array &$fieldConfigs ) {
		foreach ( array_keys( $this->_application->Entity_Field_options( $bundle, array( 'type' => 'fieldset_fieldset' ) ) ) as $field_name ) {
			if ( ( ! $field = $this->_application->Entity_Field( $bundle, $field_name ) )
				|| ( ! $settings = $field->getFieldSettings() )
				|| empty( $settings['bundle'] )
				|| ( ! $_bundle = $this->_application->Entity_Bundle( $settings['bundle'] ) )
			) {
				continue;
			}

			$bundles[ $_bundle->name ]   = $_bundle;
			$fieldConfigs[ $field_name ] = $field->FieldConfig;
			$this->_loadChildFieldsetBundles( $_bundle, $bundles, $fieldConfigs );
		}
	}

	public static function displayFormElementChildren( Application $application, $name, array $element, $bundleName ) {
		if ( $element['display_type'] === 'form' ) {
			if ( empty( $element['settings']['field_name'] ) ) {
				return;
			}

			$field_name = $element['settings']['field_name'];
		} elseif ( $element['display_type'] === 'entity' ) {
			if ( empty( $element['name'] ) ) {
				return;
			}

			$field_name = substr( $element['name'], 13 );
		} else {
			return;
		}

		if ( ( ! $field = $application->Entity_Field( $bundleName, $field_name ) )
			|| ( ! $settings = $field->getFieldSettings() )
			|| empty( $settings['bundle'] )
			|| ( ! $display = $application->Display_Display( $settings['bundle'], $element['display'], $element['display_type'] ) )
		) {
			return;
		}

		$children = array();
		foreach ( $display['elements'] as $element ) {
			$children[] = ElementsFormField::getElementHtml( $application, $name, $element, $settings['bundle'], ! empty( $display['locked'] ) );
		}

		return sprintf(
			'<input type="hidden" name="%1$s[]" value="__FIELDSET_START__%2$d" />
    <div class="drts-display-element-wrapper" data-container-display-id="%2$d">
        %3$s
    </div>
    <input type="hidden" name="%1$s[]" value="__FIELDSET_END__" />',
			$name,
			$display['id'],
			empty( $children ) ? '' : implode( PHP_EOL, $children )
		);
	}

	public function fieldColumnableInfo( IField $field ) {
		if ( ! $bundle = $this->_getFieldsetBundle( $field ) ) {
			return;
		}

		return array(
			'' => array(
				'label' => $bundle->getLabel( 'singular' ),
			),
		);
	}

	public function fieldColumnableColumn( IField $field, IEntity $entity, $column = '' ) {
		if ( ! $values = $entity->getFieldValue( $field->getFieldName() ) ) {
			return 0;
		}

		$count = count( $values );
		if ( ( ! $bundle = $this->_getFieldsetBundle( $field ) )
			|| ( ! $url = $this->_application->getPlatform()->getEntityAdmninUrl( $bundle->entitytype_name, $bundle->name ) )
		) {
			return $count;
		}

		return '<a href="' . $this->_application->H( $url ) . '">' . $count . '</a>';
	}

	public function fieldQueryableInfo( IField $field, $inAdmin = false ) {
	}

	public function fieldQueryableQuery( Query $query, $fieldName, $paramStr, Bundle $bundle ) {
	}
}
