<?php
/**
 * Registers PSR-4/classmap autoload rules for manually installed vendor packages.
 *
 * Used when composer install fails due to local SSL issues but vendor/ was
 * populated via scripts/install-vendor-fallback.php.
 *
 * @package PhoenixWP\PriceLabels
 */

defined( 'ABSPATH' ) || exit;

if ( phoenix_wp_price_labels_needs_vendor_autoload_bridge() ) {
	$phoenix_wp_price_labels_composer_loader = class_exists( \Composer\Autoload\ClassLoader::class )
		? new \Composer\Autoload\ClassLoader()
		: null;

	if ( $phoenix_wp_price_labels_composer_loader instanceof \Composer\Autoload\ClassLoader ) {
		phoenix_wp_price_labels_register_vendor_autoload( $phoenix_wp_price_labels_composer_loader );
		$phoenix_wp_price_labels_composer_loader->register( true );
	}
}

/**
 * Whether vendor/composer autoload is incomplete (fallback install).
 */
function phoenix_wp_price_labels_needs_vendor_autoload_bridge(): bool {
	if ( class_exists( \Dompdf\Cpdf::class, false ) ) {
		return false;
	}

	$file = PHOENIX_PRICE_LABELS_FOR_WOOCOMMERCE_PATH . 'vendor/composer/autoload_psr4.php';

	if ( ! is_readable( $file ) ) {
		return true;
	}

	$contents = (string) file_get_contents( $file );

	return ! str_contains( $contents, 'Dompdf\\' );
}

/**
 * Adds autoload rules from each package composer.json under vendor/.
 *
 * @param \Composer\Autoload\ClassLoader $loader Composer class loader.
 */
function phoenix_wp_price_labels_register_vendor_autoload( \Composer\Autoload\ClassLoader $loader ): void {
	$vendor_root = PHOENIX_PRICE_LABELS_FOR_WOOCOMMERCE_PATH . 'vendor';

	if ( ! is_dir( $vendor_root ) ) {
		return;
	}

	$vendor_dirs = glob( $vendor_root . '/*/*', GLOB_ONLYDIR ) ?: array();

	foreach ( $vendor_dirs as $package_dir ) {
		if ( str_contains( $package_dir, DIRECTORY_SEPARATOR . 'composer' ) ) {
			continue;
		}

		$composer_file = $package_dir . '/composer.json';
		if ( ! is_readable( $composer_file ) ) {
			continue;
		}

		/** @var array<string, mixed>|null $meta */
		$meta     = json_decode( (string) file_get_contents( $composer_file ), true );
		$autoload = is_array( $meta ) ? ( $meta['autoload'] ?? array() ) : array();

		foreach ( (array) ( $autoload['psr-4'] ?? array() ) as $prefix => $paths ) {
			foreach ( (array) $paths as $path ) {
				$loader->addPsr4( (string) $prefix, $package_dir . '/' . ltrim( (string) $path, '/\\' ) );
			}
		}

		foreach ( (array) ( $autoload['psr-0'] ?? array() ) as $prefix => $paths ) {
			foreach ( (array) $paths as $path ) {
				$loader->addPsr0( (string) $prefix, $package_dir . '/' . ltrim( (string) $path, '/\\' ) );
			}
		}

		foreach ( (array) ( $autoload['classmap'] ?? array() ) as $path ) {
			$map_path = $package_dir . '/' . ltrim( (string) $path, '/\\' );
			if ( is_dir( $map_path ) ) {
				$loader->addClassMap( phoenix_wp_price_labels_build_classmap( $map_path ) );
			} elseif ( is_file( $map_path ) ) {
				foreach ( phoenix_wp_price_labels_extract_classes_from_file( $map_path ) as $class ) {
					$loader->addClassMap( array( $class => $map_path ) );
				}
			}
		}
	}
}

/**
 * Builds a classmap for PHP files in a directory tree.
 *
 * @param string $directory Absolute directory path.
 * @return array<class-string, string>
 */
function phoenix_wp_price_labels_build_classmap( string $directory ): array {
	$map = array();

	$iterator = new RecursiveIteratorIterator(
		new RecursiveDirectoryIterator( $directory, FilesystemIterator::SKIP_DOTS )
	);

	foreach ( $iterator as $file ) {
		if ( ! $file instanceof SplFileInfo || ! $file->isFile() || 'php' !== $file->getExtension() ) {
			continue;
		}

		$path = $file->getPathname();
		foreach ( phoenix_wp_price_labels_extract_classes_from_file( $path ) as $class ) {
			$map[ $class ] = $path;
		}
	}

	return $map;
}

/**
 * @return list<class-string>
 */
function phoenix_wp_price_labels_extract_classes_from_file( string $path ): array {
	$contents = @file_get_contents( $path );
	if ( false === $contents ) {
		return array();
	}

	$classes   = array();
	$namespace = '';
	$tokens    = token_get_all( $contents );
	$count     = count( $tokens );

	for ( $i = 0; $i < $count; $i++ ) {
		$token = $tokens[ $i ];

		if ( ! is_array( $token ) ) {
			continue;
		}

		if ( T_NAMESPACE === $token[0] ) {
			$namespace = phoenix_wp_price_labels_read_namespace( $tokens, $i + 1 );
			continue;
		}

		if ( ! in_array( $token[0], array( T_CLASS, T_INTERFACE, T_ENUM, T_TRAIT ), true ) ) {
			continue;
		}

		$name = phoenix_wp_price_labels_read_symbol_name( $tokens, $i + 1 );
		if ( '' === $name ) {
			continue;
		}

		$classes[] = '' !== $namespace ? $namespace . '\\' . $name : $name;
	}

	return $classes;
}

/**
 * @param array<int, array{0: int, 1: string, 2: int}|string> $tokens Token list.
 * @param int                                                  $start  Start index.
 */
function phoenix_wp_price_labels_read_namespace( array $tokens, int $start ): string {
	$parts = array();
	$count = count( $tokens );

	for ( $i = $start; $i < $count; $i++ ) {
		$token = $tokens[ $i ];

		if ( is_array( $token ) && T_WHITESPACE === $token[0] ) {
			continue;
		}

		if ( is_array( $token ) && T_NAME_QUALIFIED === $token[0] ) {
			return $token[1];
		}

		if ( is_array( $token ) && T_STRING === $token[0] ) {
			$parts[] = $token[1];
			continue;
		}

		if ( '{' === $token || ';' === $token ) {
			break;
		}

		break;
	}

	return implode( '\\', $parts );
}

/**
 * @param array<int, array{0: int, 1: string, 2: int}|string> $tokens Token list.
 * @param int                                                  $start  Start index.
 */
function phoenix_wp_price_labels_read_symbol_name( array $tokens, int $start ): string {
	$count = count( $tokens );

	for ( $i = $start; $i < $count; $i++ ) {
		$token = $tokens[ $i ];

		if ( is_array( $token ) && T_WHITESPACE === $token[0] ) {
			continue;
		}

		if ( is_array( $token ) && T_STRING === $token[0] ) {
			return $token[1];
		}

		break;
	}

	return '';
}
