<?php
/**
 * QuadLayers WP Autoload
 *
 * @package   quadlayers/wp-autoload
 * @author    QuadLayers
 * @link      https://github.com/quadlayers/wp-autoload
 * @copyright Copyright (c) 2023
 * @license   GPL-3.0
 */

namespace QuadLayers\WP_Autoload;

use Composer\Composer;
use Composer\Autoload\AutoloadGenerator as ComposerAutoloadGenerator;
use Composer\IO\IOInterface;
use Composer\Util\Filesystem;

/**
 * Class FileAutoloadFilePackageCreate.
 *
 * @package quadlayers/wp-autoloader
 */
class FileAutoloadFilePackageCreate extends ComposerAutoloadGenerator {

	/**
	 * Composer object.
	 *
	 * @var Composer object.
	 */
	private $composer;

	/**
	 * IO object.
	 *
	 * @var IOInterface object.
	 */
	private $io;

	/**
	 * Filesystem object.
	 *
	 * @var FileSystem object.
	 */
	private $filesystem;

	/**
	 * Vendor path.
	 *
	 * @var string.
	 */
	private $vendorPath;

	/**
	 * Vendor path code.
	 *
	 * @var string.
	 */
	private $vendorPathCode;

	/**
	 * Project base path.
	 *
	 * @var string.
	 */
	private $basePath;

	/**
	 * Project base path code.
	 *
	 * @var string.
	 */
	private $basePathCode;

	/**
	 * Constructor.
	 *
	 * @param Composer    $composer object.
	 * @param IOInterface $io object.
	 * @param Filesystem  $filesystem object.
	 */
	public function __construct( Composer $composer, IOInterface $io, Filesystem $filesystem ) {
		$this->composer   = $composer;
		$this->io         = $io;
		$this->filesystem = $filesystem;
		/**
		 * Get the vendor directory.
		 */
		$vendorDir = $this->composer->getConfig()->get( 'vendor-dir' );
		/**
		 * Get the vendor path.
		 */
		$this->vendorPath = $this->filesystem->normalizePath( realpath( realpath( $vendorDir ) ) );
		/**
		 * Get the vendor path code.
		 * Example: __DIR__
		 */
		$this->vendorPathCode = $this->filesystem->findShortestPathCode( $this->vendorPath, $this->vendorPath, true );
		/**
		 * Get the project base path.
		 */
		$this->basePath = $this->filesystem->normalizePath( realpath( realpath( getcwd() ) ) );
		/**
		 * Get the project base path code.
		 * Example: dirname( __DIR__ )
		 */
		$this->basePathCode = str_replace( $this->vendorPathCode, '$vendorDir', $this->filesystem->findShortestPathCode( $this->vendorPath, $this->basePath, true ) );
		/**
		 * Ensure the vendor directory exists.
		 */
		$this->filesystem->ensureDirectoryExists( $this->vendorPath );
	}

	/**
	 * Creates the autoload file.
	 */
	public function create(): void {

		$fileContent = $this->getContent();

		if ( $this->filesystem->filePutContentsIfModified( $this->getFilePath(), $fileContent ) ) {
			$this->io->write( '<info>QuadLayers WP Autoload created.</info>' );
		}

	}

	/**
	 * Deletes the autoload file.
	 */
	public function delete(): void {
		if ( $this->filesystem->remove( $this->getFilePath() ) ) {
			$this->io->write( '<info>QuadLayers WP Autoload removed.</info>' );
		}
	}

	/**
	 * Create the autoloader file contents to write to vendor/wordpress-autoload.php.
	 *
	 * @return string
	 */
	protected function getContent(): string {

		$namespacesPaths = $this->getValidNamespaces();

		$autoloadFileContents = <<<FILEHEADER
<?php
/* QuadLayers WP Autoload @generated by quadlayers/wp-autoload */
FILEHEADER;
		/**
		 * Inject the file paths for the autoloader.
		 */
		$autoloadFileContents .= <<<AUTOLOADFILEPATHS

\$vendorDir = $this->vendorPathCode;
\$baseDir = $this->basePathCode;

AUTOLOADFILEPATHS;

		/**
		 * Include the rules for the autoloader.
		 */
		$autoloadFileContents .= <<<AUTOLOAD

\QuadLayers\WP_Autoload\AutoloaderFactory::registerFromRules(array(

AUTOLOAD;

		foreach ( $namespacesPaths as $namespace => $path ) {
			$autoloadFileContents .= sprintf(
				'    %s => array(%s),',
				var_export( $namespace, true ),
				$path
			) . PHP_EOL;
		}

		$autoloadFileContents .= "));\n";

		return $autoloadFileContents . "\n";
	}

	/**
	 * Creates the autoload file content.
	 *
	 * @return array
	 */
	private function getValidNamespaces(): array {

		$namespacesPaths = array();

		foreach ( $this->composer->getPackage()->getExtra()['quadlayers/wp-autoload'] as $namespace => $folder ) {

			/**
			 * Check if the folder is defined in the "classmap" autoload.
			 */
			if ( ! in_array( $folder, $this->composer->getPackage()->getAutoload()['classmap'] ) ) {
				$this->io->writeError( "\n<error>QuadLayers WP Autoload error:", true );
				$this->io->writeError( sprintf( 'The "%s" folder is not defined in the "classmap" autoload.</error>', $folder ), true );
				return false;
			}

			/**
			 * Convert the paths to be relative to the vendor/wp-autoload.php file.
			 */
			if ( ! is_dir( $this->basePath . '/' . $folder ) ) {
				$this->io->writeError( "\n<error>QuadLayers WP Autoload error:", true );
				$this->io->writeError( sprintf( 'Could not scan for classes inside "%s" which does not appear to be a valid folder.</error>', $folder ), true );
				exit();
			}

			$namespacesPaths[ $namespace ] = $this->getPathCode( $this->filesystem, $this->basePath, $this->vendorPath, $folder );

		}

		return $namespacesPaths;

	}

	/**
	 * Retrieve the file path for the autoloader file.
	 *
	 * @return string
	 */
	public function getFilePath(): string {
		return "{$this->vendorPath}/wp-autoload.php";
	}


}
