<?php
/**
 * Copyright 2022 Adobe
 * All Rights Reserved.
 *
 * NOTICE: Adobe permits you to use, modify, and distribute this file in
 * accordance with the terms of the Adobe license agreement accompanying
 * it.
 */
namespace Magento\Mray\Composer;

use Composer\Composer;
use Composer\Factory;
use Composer\Filter\PlatformRequirementFilter\IgnoreAllPlatformRequirementFilter;
use Composer\Installer;
use Composer\IO\ConsoleIO;
use Composer\Package\BasePackage;
use Composer\Package\PackageInterface;
use Composer\Util\Loop;
use Exception;
use React\Promise\PromiseInterface;

class SinglePackageDownloader
{
    /**
     * Downloads and installs a single package in a synchronous way,
     * executing all the required steps and waits for promises to complete
     *
     * @param  Composer    $composer
     * @param  BasePackage $package
     * @param  string      $path
     * @param  bool        $installDependencies
     * @param  ConsoleIO   $io
     * @throws Exception
     */
    public function execute(
        Composer         $composer,
        PackageInterface $package,
        string           $path,
        bool             $installDependencies,
        ConsoleIO        $io
    ) {
        $loop = $composer->getLoop();
        $downloader = $composer->getDownloadManager()->getDownloader('zip');
        $oldPath = getcwd();
        try {
            $this->await($loop, $downloader->download($package, $path));
            $this->await($loop, $downloader->prepare('install', $package, $path));
            $this->await($loop, $downloader->install($package, $path));
            if ($installDependencies) {
                chdir($path);
                $installer = Installer::create($io, Factory::create($io, null, true));
                $installer
                    ->setDumpAutoloader(false)
                    ->setOptimizeAutoloader(false)
                    ->setClassMapAuthoritative(false)
                    ->setApcuAutoloader(false)
                    ->setPlatformRequirementFilter(new IgnoreAllPlatformRequirementFilter())
                    ->disablePlugins()
                    ->run();
            }
        } catch (Exception $e) {
            $this->await($loop, $downloader->cleanup('install', $package, $path));
            throw $e;
        } finally {
            chdir($oldPath);
        }

        $this->await($loop, $downloader->cleanup('install', $package, $path));
    }

    /**
     * Waits for a promise to resolve
     *
     * @param Loop                  $loop    Loop instance which you can get from $composer->getLoop()
     * @param PromiseInterface|null $promise
     *
     * @return void
     */
    private function await(Loop $loop, PromiseInterface $promise = null)
    {
        if ($promise) {
            $loop->wait([$promise]);
        }
    }
}
