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

use SabaiApps\Directories\Application;
use SabaiApps\Directories\Component\Entity\Model\Bundle;
use SabaiApps\Directories\Component\Entity\Type\IEntity;
use SabaiApps\Directories\Component\Form\Form;
use SabaiApps\Directories\Component\Field\IField;
use SabaiApps\Directories\Component\Field\Type\IQueryable;
use SabaiApps\Directories\Component\Field\Type\ICopiable;
use SabaiApps\Directories\Component\Field\Type\IConditionable;
use SabaiApps\Directories\Component\Field\Type\IColumnable;
use SabaiApps\Directories\Component\Field\Query;
use SabaiApps\Directories\Component\Field\Type\AbstractType;

class ReferenceFieldType extends AbstractType implements IQueryable, ICopiable, IConditionable, IColumnable {

	protected function _fieldTypeInfo() {
		return array(
			'label'            => _x( 'Reference', 'reference field type', 'directories-pro' ),
			'icon'             => 'fas fa-arrows-rotate',
			'default_settings' => array(
				'bundle' => null,
			),
		);
	}

	public function fieldTypeSchema( array $settings = array() ) {
		return array(
			'columns' => array(
				'value' => array(
					'type'     => Application::COLUMN_INTEGER,
					'notnull'  => true,
					'unsigned' => true,
					'was'      => 'value',
					'default'  => 0,
				),
			),
		);
	}

	public function fieldTypeSettingsForm( $fieldType, Bundle $bundle, array $settings, array $parents = array(), array $rootParents = array() ) {
		$options = $sync_field_options = array();
		foreach ( $this->_application->Entity_Bundles() as $_bundle ) {
			if ( ! empty( $_bundle->info['parent'] )
				|| ! empty( $_bundle->info['parent_field'] )
				|| ! empty( $_bundle->info['is_taxonomy'] )
				|| empty( $_bundle->info['public'] )
				|| ! in_array( $_bundle->entitytype_name, array( 'post', 'user' ) )
			) {
				continue;
			}

			$options[ $_bundle->name ] = $_bundle->getGroupLabel() . ' - ' . $_bundle->getLabel( 'singular' );

			if ( $bundle->name !== $_bundle->name ) {
				$_bundle_fields = $this->_application->Entity_Field( $_bundle );
				foreach ( array_keys( $_bundle_fields ) as $field_name ) {
					$_bundle_field = $_bundle_fields[ $field_name ];
					if ( $_bundle_field->getFieldType() !== 'entity_reference' ) {
						continue;
					}

					$bundle_field_settings = $_bundle_field->getFieldSettings();
					if ( $bundle_field_settings['bundle'] !== $bundle->name ) {
						continue; // not referencing current bundle
					}

					$sync_field_options[ $_bundle->name ][ $field_name ] = $_bundle_field->getFieldLabel() . ' (' . $field_name . ')';
				}
			}
		}
		$ret = array(
			'bundle'  => array(
				'#type'          => 'select',
				'#title'         => __( 'Content type', 'directories-pro' ),
				'#options'       => $options,
				'#default_value' => $settings['bundle'] ?? null,
				'#required'      => true,
			),
			'#submit' => array(
				11 => array( array( array( $this, '_configureSync' ), array( $fieldType, $bundle, $parents, $sync_field_options ) ) ), // 11 is weight
			),
		);
		foreach ( $sync_field_options as $bundle_name => $bundle_fields ) {
			$ret['sync'][ $bundle_name ] = array(
				'#type'          => 'select',
				'#title'         => __( 'Synchronize with field', 'directories-pro' ),
				'#options'       => array( '' => '— ' . __( 'Select field', 'directories-pro' ) . ' —' ) + $bundle_fields,
				'#default_value' => isset( $settings['sync'][ $bundle_name ] ) ? $settings['sync'][ $bundle_name ] : null,
				'#states'        => array(
					'visible' => array(
						sprintf( 'select[name="%s"]', $this->_application->Form_Form_fieldName( array_merge( $parents, array( 'bundle' ) ) ) ) => array( 'value' => $bundle_name ),
					),
				),
			);
		}

		return $ret;
	}

	public function _configureSync( Form $form, $fieldType, Bundle $bundle, $parents, $syncFieldOptions ) {
		$settings = $form->getValue( $parents );
		if ( ( ! $ref_bundle_name = ( $settings['bundle'] ?? null ) )
			|| empty( $settings['sync'][ $ref_bundle_name ] )
		) {
			return;
		}

		$sync_field_name = $settings['sync'][ $ref_bundle_name ];
		if ( ! $sync_field = $this->_application->Entity_Field( $ref_bundle_name, $sync_field_name ) ) {
			return;
		}

		$sync_field_settings = $sync_field->getFieldSettings();
		if ( $sync_field_settings['bundle'] !== $bundle->name ) {
			return; // this shouldn't happen but just in case
		}

		if ( $fieldType instanceof IField ) {
			$field_name = $fieldType->getFieldName();
		} else {
			$field_name = $form->getValue( 'setting', 'name' );
		}
		if ( isset( $sync_field_settings['sync'][ $bundle->name ] )
			&& $sync_field_settings['sync'][ $bundle->name ] === $field_name
		) {
			return; // already configured
		}

		$sync_field_settings['sync'] = array( $bundle->name => $field_name );
		$sync_field->setFieldSettings( $sync_field_settings );
		$sync_field->FieldConfig->commit();
	}

	public function fieldTypeOnSaveValues( IField $field, array $values, ?array $currentValues = null, array &$extraArgs = array() ) {
		$ret = $ids = array();
		foreach ( $values as $weight => $value ) {
			if ( is_array( $value ) ) {  // autocomplete field widget
				foreach ( $value as $id ) {
					if ( empty( $id ) ) {
						continue;
					}
					$ids[ $id ] = $id;
				}
			} elseif ( ! empty( $value ) ) {
				$ids[ $value ] = $value;
			}
		}
		foreach ( $ids as $id ) {
			$ret[]['value'] = (int) $id;
		}
		return $ret;
	}

	public function fieldTypeOnLoadValues( IField $field, array &$values, IEntity $entity, array $allValues ) {
		$entities = array();
		foreach ( $values as $key => $value ) {
			$entities[ $value['value'] ] = $key;
		}
		$values = array();
		if ( ! $ref_bundle = $this->getReferenceFieldBundle( $field ) ) {
			return;
		}

		foreach ( $this->_application
			->Entity_Types_impl( $ref_bundle->entitytype_name )
			->entityTypeEntitiesByIds( array_keys( $entities ), $ref_bundle->name )
		as $entity ) {
			$key            = $entities[ $entity->getId() ];
			$values[ $key ] = $this->_getValueForLoad( $entity );
		}
		ksort( $values ); // re-order as it was saved
	}

	protected function _getValueForLoad( IEntity $entity ) {
		return $entity;
	}

	public function getReferenceFieldBundle( IField $field ) {
		$field_settings = $field->getFieldSettings();
		if ( empty( $field_settings['bundle'] )
			|| ( ! $bundle = $this->_application->Entity_Bundle( $field_settings['bundle'] ) )
		) {
			if ( empty( $field_settings['bundle_type'] )
				|| ( ! $bundle_group = $field->Bundle->group )
				|| ( ! $bundle = $this->_application->Entity_Bundle( $field_settings['bundle_type'], $bundle_group ) )
			) {
				return;
			}
		}
		return $bundle;
	}

	public function fieldTypeIsModified( IField $field, $valueToSave, $currentLoadedValue ) {
		$current_value = array();
		foreach ( $currentLoadedValue as $entity ) {
			$current_value[]['value'] = $entity->getId();
		}
		return $current_value !== $valueToSave;
	}

	public function fieldQueryableInfo( IField $field, $inAdmin = false ) {
		return array(
			'example' => '1,5,13,0',
			'tip'     => __( 'Enter IDs separated with commas. Enter 0 for no matching items.', 'directories-pro' ),
		);
	}

	public function fieldQueryableQuery( Query $query, $fieldName, $paramStr, Bundle $bundle ) {
		if ( ! $ids = $this->_queryableParams( $paramStr ) ) {
			return;
		}

		$ids = $this->_getReferenceEntityIds( $ids, $include_null );
		if ( ! empty( $ids ) ) {
			if ( $include_null ) {
				$query->startCriteriaGroup( 'OR' )
					->fieldIsIn( $fieldName, $ids )
					->fieldIsNull( $fieldName )
					->finishCriteriaGroup();
			} else {
				$query->fieldIsIn( $fieldName, $ids );
			}
		} elseif ( $include_null ) {
				$query->fieldIsNull( $fieldName );
		}
	}

	protected function _getReferenceEntityIds( array $ids, &$includeNull ) {
		$ret = array();
		foreach ( $ids as $id ) {
			if ( $id == 0 ) {
				$includeNull = true;
			} else {
				$ret[] = $id;
			}
		}
		return $ret;
	}

	public function fieldCopyValues( IField $field, array $values, array &$allValues, $lang = null ) {
		if ( ! empty( $lang ) ) {
			$field_settings = $field->getFieldSettings();
			if ( empty( $field_settings['bundle'] )
				|| ( ! $bundle = $this->_application->Entity_Bundle( $field_settings['bundle'] ) )
			) {
				$this->_application->logError( 'Failed fetching reference field bundle.' );
				return;
			}

			if ( $this->_application->getPlatform()->isTranslatable( $bundle->entitytype_name, $bundle->name ) ) {
				foreach ( array_keys( $values ) as $k ) {
					if ( empty( $values[ $k ]['value'] ) ) {
						continue;
					}

					$translation_id = (int) $this->_application->getPlatform()->getTranslatedId(
						$bundle->entitytype_name,
						$bundle->name,
						$values[ $k ]['value'],
						$lang
					);
					if ( empty( $translation_id ) ) {
						unset( $values[ $k ] );
					} else {
						$values[ $k ]['value'] = $translation_id;
					}
				}
			}
		}

		return $values;
	}

	public function fieldConditionableInfo( IField $field, $isServerSide = false ) {
		if ( ! $isServerSide ) {
			return;
		}

		return array(
			'' => array(
				'compare' => array( 'empty', 'filled' ),
			),
		);
	}

	public function fieldConditionableRule( IField $field, $compare, $value = null, $name = '' ) {
		switch ( $compare ) {
			case 'empty':
				return array(
					'type'  => 'checked',
					'value' => false,
				);
			case 'filled':
				return array(
					'type'  => 'unchecked',
					'value' => false,
				);
			default:
				return;
		}
	}

	public function fieldConditionableMatch( IField $field, array $rule, array $values, IEntity $entity ) {
		switch ( $rule['type'] ) {
			case 'checked':
			case 'unchecked':
				return $rule['type'] === 'unchecked' ? empty( $values ) === $rule['value'] : ! empty( $values ) === $rule['value'];
			default:
				return false;
		}
	}

	public function fieldColumnableInfo( IField $field ) {
		$field_settings = $field->getFieldSettings();
		if ( empty( $field_settings['bundle'] )
			|| ! $this->_application->Entity_Bundle( $field_settings['bundle'] )
		) {
			$this->_application->logError( 'Failed fetching reference field bundle.' );
			return;
		}

		return array(
			'' => array(
				'label' => $field->getFieldLabel(),
			),
		);
	}

	public function fieldColumnableColumn( IField $field, IEntity $entity, $column = '' ) {
		$field_settings = $field->getFieldSettings();
		if ( ( ! $values = $entity->getFieldValue( $field->getFieldName() ) )
			|| empty( $field_settings['bundle'] )
			|| ! $this->_application->Entity_Bundle( $field_settings['bundle'] )
		) {
			return;
		}

		$ret = array();
		foreach ( $values as $ref_entity ) {
			$title_html = $this->_application->H( $ref_entity->getTitle() );
			if ( $this->_application->getPlatform()->getName() === 'WordPress'
				&& $this->_application->getPlatform()->isAdmin()
			) {
				$title_html = '<a href="' . admin_url( 'edit.php?post_type=' . $entity->getBundleName() . '&drts_wp_entity_reference_field=' . $field->getFieldName() . '&drts_wp_entity_reference=' . $ref_entity->getId() ) . '">'
					. $title_html . '</a>';
			}
			$ret[] = $title_html;
		}
		return empty( $ret ) ? null : implode( ', ', $ret );
	}
}
