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

use SabaiApps\Directories\Application;
use SabaiApps\Framework\User\AbstractIdentity;

class BundlesHelper {

	public function help( Application $application, ?array $bundleNames = null, $group = null ) {
		$ret = array();
		if ( isset( $bundleNames ) ) {
			if ( isset( $group ) ) {
				// $bundleNames is bundle types
				foreach ( $bundleNames as $k => $bundle_type ) {
					if ( isset( BundleHelper::$bundlesByType[ $group ][ $bundle_type ] ) ) {
						$bundle               = BundleHelper::$bundlesByType[ $group ][ $bundle_type ];
						$ret[ $bundle->name ] = $bundle;
						unset( $bundleNames[ $k ] );
					}
				}
				if ( ! empty( $bundleNames ) ) {
					$ret += $this->byType( $application, $group );
				}
			} else {
				foreach ( $bundleNames as $k => $bundle_name ) {
					if ( isset( BundleHelper::$bundles[ $bundle_name ] ) ) {
						$ret[ $bundle_name ] = BundleHelper::$bundles[ $bundle_name ];
						unset( $bundleNames[ $k ] );
					}
				}
				if ( ! empty( $bundleNames ) ) {
					foreach ( $application->getModel( 'Bundle', 'Entity' )->name_in( $bundleNames )->fetch() as $bundle ) {
						if ( $application->Entity_Bundle_isValid( $bundle ) ) {
							$ret[ $bundle->name ] = BundleHelper::add( $bundle );
						}
					}
				}
			}
		} else {
			$model = $application->getModel( 'Bundle', 'Entity' );
			if ( isset( $group ) ) {
				$model->group_is( $group );
			}
			foreach ( $model->fetch() as $bundle ) {
				if ( $application->Entity_Bundle_isValid( $bundle ) ) {
					$ret[ $bundle->name ] = BundleHelper::add( $bundle );
				}
			}
		}

		return $ret;
	}

	public function byType( Application $application, $bundleType, $group = null ) {
		$ret = array();
		if ( $bundle_types = is_array( $bundleType ) ? $bundleType : array( $bundleType ) ) {
			$bundles = $application->getModel( 'Bundle', 'Entity' )->type_in( $bundle_types );
			if ( isset( $group ) ) {
				$bundles->group_is( $group );
			}
			foreach ( $bundles->fetch() as $bundle ) {
				if ( $application->Entity_Bundle_isValid( $bundle ) ) {
					$ret[ $bundle->name ] = BundleHelper::add( $bundle );
				}
			}
		}

		return $ret;
	}

	public function collection( Application $application, $bundleNames ) {
		$ret = array();
		foreach ( $this->help( $application, $bundleNames ) as $bundle ) {
			$ret[ $bundle->name ] = $bundle;
		}
		return $application->getModel( 'Bundle', 'Entity' )->createCollection( $ret );
	}

	protected $_addableBundles = array();

	public function addable( Application $application, $bundleType, ?AbstractIdentity $identity = null ) {
		$user_id = isset( $identity ) ? $identity->id : $application->getUser()->id;
		if ( ! isset( $this->_addableBundles[ $user_id ] ) ) {
			$this->_addableBundles[ $user_id ] = array();
			foreach ( $this->byType( $application, $bundleType ) as $bundle_name => $bundle ) {
				if ( ! empty( $bundle->info['parent'] )
					|| ! empty( $bundle->info['parent_field'] )
					|| empty( $bundle->info['public'] )
				) {
					continue;
				}

				if ( ! in_array( $bundle->entitytype_name, array( 'term', 'user' ) ) // term and user entity types do not have the create entity permission
					&& ! $application->HasPermission( 'entity_create_' . $bundle_name )
				) {
					continue;
				}

				$this->_addableBundles[ $user_id ][ $bundle_name ] = $bundle->getGroupLabel() . ' - ' . $bundle->getLabel( 'singular' );
			}
		}
		return $this->_addableBundles[ $user_id ];
	}

	public function sort( Application $application, ?array $bundles = null, $group = null ) {
		if ( ! isset( $bundles ) ) {
			$bundles = $this->help( $application, null, $group );
		}
		uasort(
			$bundles,
			function ( $a, $b ) {
				strnatcmp( $a->name, $b->name );
			}
		);
		$ret        = $top_bundles = $child_bundles = array();
		$sort_order = $application->Filter( 'entity_bundle_sort_order', array() );
		foreach ( $bundles as $bundle ) {
			if ( isset( $sort_order[ $bundle->type ] ) ) {
				$j = $sort_order[ $bundle->type ];
			} else {
				$j = empty( $bundle->info['parent'] ) && empty( $bundle->info['parent_field'] ) ? 0 : 1;
				if ( empty( $bundle->info['public'] ) ) {
					$j += 2;
				}
			}

			if ( empty( $bundle->info['parent'] ) ) {
				$i = empty( $bundle->info['is_taxonomy'] ) ? 0 : 1;
				$top_bundles[ $bundle->group ][ $i ][ $j ][ $bundle->name ] = $bundle->name;
			} else {
				$child_bundles[ $bundle->info['parent'] ][ $j ][ $bundle->name ] = $bundle->name;
			}
		}
		foreach ( array_keys( $top_bundles ) as $group_name ) {
			$ret[ $group_name ] = array();
			ksort( $top_bundles[ $group_name ] );
			foreach ( array_keys( $top_bundles[ $group_name ] ) as $i ) {
				ksort( $top_bundles[ $group_name ][ $i ] );
				foreach ( array_keys( $top_bundles[ $group_name ][ $i ] ) as $j ) {
					ksort( $top_bundles[ $group_name ][ $i ][ $j ] );
					foreach ( array_keys( $top_bundles[ $group_name ][ $i ][ $j ] ) as $bundle_name ) {
						$ret[ $group_name ][ $bundle_name ] = $bundles[ $bundle_name ];
						if ( ! empty( $child_bundles[ $bundle_name ] ) ) {
							ksort( $child_bundles[ $bundle_name ] );
							foreach ( array_keys( $child_bundles[ $bundle_name ] ) as $j2 ) {
								ksort( $child_bundles[ $bundle_name ][ $j2 ] );
								foreach ( $child_bundles[ $bundle_name ][ $j2 ] as $child_bundle_name ) {
									$ret[ $group_name ][ $child_bundle_name ] = $bundles[ $child_bundle_name ];
								}
							}
						}
					}
				}
			}
		}

		if ( isset( $group ) ) {
			return isset( $ret[ $group ] ) ? $ret[ $group ] : array();
		}

		return $ret;
	}

	public function referencing( Application $application, $bundleName ) {
		$bundles = array();
		foreach ( $this->help( $application ) as $bundle ) {
			foreach ( $application->Entity_Field( $bundle ) as $field ) {
				if ( $field->getFieldType() !== 'entity_reference' ) {
					continue;
				}

				$field_settings = $field->getFieldSettings();
				if ( empty( $field_settings['bundle'] ) ) {
					continue;
				}

				$bundles[ $field_settings['bundle'] ][ $field->getFieldName() ][] = $field->bundle_name;
			}
		}

		return isset( $bundles[ $bundleName ] ) ? $bundles[ $bundleName ] : array();
	}

	public function byFeature( Application $application, $features ) {
		foreach ( $this->help( $application ) as $bundle ) {
			foreach ( (array) $features as $feature ) {
				if ( empty( $bundle->info[ $feature ] ) ) {
					continue 2; // skip this bundle
				}
			}
			$ret[ $bundle->name ] = $bundle;
		}
		return $ret;
	}
}
