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

use SabaiApps\Directories\Application;
use SabaiApps\Directories\Exception;
use SabaiApps\Directories\Component\Entity\Model\Bundle;
use SabaiApps\Directories\Component\Entity\Type\IEntity;

class BundleHelper {

	public static $bundles = array(), $bundlesByType = array();

	public function help( Application $application, $entityOrBundle, $group = '', $throwException = false ) {
		if ( $entityOrBundle instanceof IEntity ) {
			$name = $entityOrBundle->getBundleName();
			$ret  = $this->_getBundleByName( $application, $name );
		} elseif ( is_string( $entityOrBundle ) ) {
			$name = $entityOrBundle;
			$ret  = strlen( $group ) ? $this->_getBundleByType( $application, $name, $group ) : $this->_getBundleByName( $application, $name );
		} elseif ( $entityOrBundle instanceof Bundle ) {
			$name = $entityOrBundle->name;
			$ret  = $this->isValid( $application, $entityOrBundle ) ? self::add( $entityOrBundle ) : null;
		}
		if ( empty( $ret ) ) {
			if ( $throwException ) {
				throw new Exception\RuntimeException( 'Invalid bundle: ' . $name );
			}
			$application->logError( 'Invalid bundle: ' . $name . ' (trace: ' . print_r( debug_backtrace( DEBUG_BACKTRACE_IGNORE_ARGS, 10 ), true ) . ')  ' );
			$ret = false;
		}

		return $ret;
	}

	public function isValid( Application $application, Bundle $bundle ) {
		if ( ( ! $bundle_type_component = $application->Entity_BundleTypes( $bundle->type ) )
			|| ! $application->isComponentLoaded( $bundle_type_component )
		) {
			return false;
		}

		if ( empty( $bundle->info['parent'] ) ) {
			return true;
		}

		// Make sure the parent bundle is also valid
		return $application->Entity_Bundle( $bundle->info['parent'] ) ? true : false;
	}

	protected function _getBundleByName( Application $application, $bundleName ) {
		if ( isset( self::$bundles[ $bundleName ] ) ) {
			return self::$bundles[ $bundleName ];
		}

		if ( ( $bundle = $application->getModel( 'Bundle', 'Entity' )->name_is( $bundleName )->fetchOne() )
			&& $this->isValid( $application, $bundle )
		) {
			return self::add( $bundle );
		}
	}

	protected function _getBundleByType( Application $application, $bundleType, $group ) {
		if ( ! isset( self::$bundlesByType[ $group ] ) ) {
			// Fetch all first and validate by top level bundles first to prevent additional queries by isValid() method.
			$bundles = array();
			foreach ( $application->getModel( 'Bundle', 'Entity' )->group_is( $group )->fetch() as $bundle ) {
				$bundles[ empty( $bundle->info['parent'] ) && empty( $bundle->info['parent_field'] ) ? 0 : 1 ][] = $bundle;
			}
			ksort( $bundles );
			foreach ( array_keys( $bundles ) as $k ) {
				foreach ( array_keys( $bundles[ $k ] ) as $j ) {
					if ( $this->isValid( $application, $bundles[ $k ][ $j ] ) ) {
						self::add( $bundles[ $k ][ $j ] );
					}
				}
			}
		} elseif ( ! isset( self::$bundlesByType[ $group ][ $bundleType ] ) ) {
			if ( $bundle = $application->getModel( 'Bundle', 'Entity' )->group_is( $group )->type_is( $bundleType )->fetchOne() ) {
				if ( $this->isValid( $application, $bundle ) ) {
					self::add( $bundle );
				}
			}
		}
		if ( isset( self::$bundlesByType[ $group ][ $bundleType ] ) ) {
			return self::$bundlesByType[ $group ][ $bundleType ];
		}
	}

	public static function add( Bundle $bundle ) {
		self::$bundles[ $bundle->name ] = self::$bundlesByType[ $bundle->group ][ $bundle->type ] = $bundle;

		return $bundle;
	}

	public static function remove( $bundleName ) {
		if ( isset( self::$bundles[ $bundleName ] ) ) {
			$bundle = self::$bundles[ $bundleName ];
			unset( self::$bundlesByType[ $bundle->group ][ $bundle->type ], self::$bundles[ $bundleName ] );
		}
	}

	public function adminLinks( Application $application, Bundle $bundle, $route ) {
		$_links = array(
			'edit' => array(
				'weight' => 0,
				'link'   => array(
					'path'    => '',
					'label'   => '',
					'options' => array(
						'icon' => 'fas fa-pen-to-square',
					),
					'atts'    => array(
						'title' => __( 'Edit', 'directories-pro' ),
					),
				),
			),
		);
		if ( $application->Entity_BundleTypeInfo( $bundle->type, 'addable' ) !== false ) {
			$_links['fields'] = array(
				'weight' => 1,
				'link'   => array(
					'path'  => '/fields',
					'label' => __( 'Manage Fields', 'directories-pro' ),
				),
			);
		}
		if ( $application->Entity_Displays( $bundle ) ) { // make sure displays exist)
			$_links['displays'] = array(
				'weight' => 10,
				'link'   => array(
					'path'  => '/displays',
					'label' => __( 'Manage Displays', 'directories-pro' ),
				),
			);
		}
		$_links = $application->Filter( 'entity_bundle_admin_links', $_links, array( $bundle ) );
		// Sort
		uasort(
			$_links,
			function ( $a, $b ) {
				return $a['weight'] < $b['weight'] ? -1 : 1;
			}
		);
		// Build links
		$links = array();
		$route = '/' . trim( $route, '/' );
		foreach ( array_keys( $_links ) as $key ) {
			$link = $_links[ $key ]['link'];
			if ( isset( $link[0] ) ) { // array of links
				$links[ $key ] = array();
				foreach ( $link as $_link ) {
					if ( is_array( $_link ) ) {
						$options         = isset( $_link['options'] ) ? $_link['options'] : array();
						$atts            = isset( $_link['atts'] ) ? $_link['atts'] : array();
						$links[ $key ][] = $application->LinkTo( $_link['label'], $route . $_link['path'], $options, $atts );
					} else {
						$links[ $key ][] = $_link;
					}
				}
			} else {
				$options       = isset( $link['options'] ) ? $link['options'] : array();
				$atts          = isset( $link['atts'] ) ? $link['atts'] : array();
				$links[ $key ] = $application->LinkTo( $link['label'], $route . $link['path'], $options, $atts );
			}
		}

		return $links;
	}

	public function path( Application $application, Bundle $bundle, $permalink = false, $lang = null ) {
		$path = '/' . $application->getComponent( $bundle->component )->getSlug( $bundle->group, $lang );
		if ( $permalink
			|| empty( $bundle->info['is_primary'] )
		) {
			if ( $_bundle = $bundle->isAlias() ) {
				$path .= '/' . $_bundle->info['slug'];
			} else {
				$path .= '/' . $bundle->info['slug'];
			}
		}
		return $path;
	}

	public function isTaxonomyAliased( Application $application, $checkBundleOrEntity, $bundleOrEntity = null, $returnAll = false ) {
		if ( ! $check_bundle = $this->help( $application, $checkBundleOrEntity ) ) {
			return false;
		}

		if ( ! isset( $bundleOrEntity ) ) {
			$bundles = $application->Entity_Bundles_byType( $check_bundle->type );
			if ( count( $bundles ) < 2 ) {
				return false;
			}
		} else {
			if ( ( ! $bundle = $this->help( $application, $bundleOrEntity ) )
				|| $check_bundle->group === $bundle->group
			) {
				return false;
			}

			// Bundles are from different groups
			// Make sure we are comparing the same bundle type
			if ( $check_bundle->type !== $bundle->type ) {
				if ( ! $bundle = $this->help( $application, $check_bundle->type, $bundle->group ) ) {
					return false;
				}
			}
			$bundles = array( $bundle->name => $bundle );
		}

		$ret = array();
		foreach ( $bundles as $bundle ) {
			if ( ( $_bundle = $bundle->isAlias() )
				&& $_bundle->name === $check_bundle->name
			) {
				if ( ! $returnAll ) {
					return $bundle;
				}
				$ret[] = $bundle;
			}
		}

		return empty( $ret ) ? false : $ret;
	}
}
