<?php

namespace Monorepo;

use Composer\Util\Filesystem;
use PHPUnit\Framework\TestCase;

class BuildTest extends TestCase
{
    public function testLoadPackagesSimpleExampleProject()
    {
        $build = new Build();
        $packages = $build->loadPackages(__DIR__ . '/../_fixtures/example-simple');

        $packageNames = array_keys($packages);
        sort($packageNames);
        $this->assertEquals(array('PSR4', 'bar', 'foo'), $packageNames);
    }

    public function testLoadPackagesComposerExampleProject()
    {
        $build = new Build();
        $packages = $build->loadPackages(__DIR__ . '/../_fixtures/example-composer');

        $packageNames = array_keys($packages);
        $this->assertEquals(array('vendor/foo/bar', 'vendor/foo/baz'), $packageNames);
    }

    public function testBuildSimpleExampleProject()
    {
        $baseDir = dirname(__DIR__);

        $build = new Build();
        $build->build(__DIR__ . '/../_fixtures/example-simple');

        $barNamespaces = include(__DIR__ . '/../_fixtures/example-simple/bar/vendor/composer/autoload_namespaces.php');
        $this->assertCount(1, $barNamespaces);
        $this->assertEquals(array('Bar\\'), array_keys($barNamespaces));

        // InstalledVersions.php only exists in subcomponent when
        // composer-runtime-api is present in root composer.json
        $this->assertFileDoesNotExists(__DIR__ . '/../_fixtures/example-simple/bar/vendor/composer/InstalledVersions.php');

        $fooNamespaces = include(__DIR__ . '/../_fixtures/example-simple/foo/vendor/composer/autoload_namespaces.php');
        $this->assertCount(2, $fooNamespaces);
        $this->assertEquals(array('Foo\\', 'Bar\\'), array_keys($fooNamespaces));

        $psr4Namespaces = include(__DIR__ . '/../_fixtures/example-simple/PSR4/vendor/composer/autoload_psr4.php');
        $this->assertCount(1, $psr4Namespaces);
        $this->assertEquals(array('PSR4\\' => array($baseDir.'/PSR4/src')), $psr4Namespaces);
    }

    public function testBuildReplaceExampleProject()
    {
        $build = new Build();
        $build->build(__DIR__ . '/../_fixtures/example-replace');

        $bazNamespaces = include(__DIR__ . '/../_fixtures/example-replace/baz/vendor/composer/autoload_namespaces.php');
        $this->assertCount(2, $bazNamespaces);
        $this->assertEquals(array('Baz\\', 'Bar\\'), array_keys($bazNamespaces));
    }

    public function testBuildReplaceWithinPackageExampleProject()
    {
        $build = new Build();
        $build->build(__DIR__ . '/../_fixtures/example-replace-within-package');

        $bazNamespaces = include(__DIR__ . '/../_fixtures/example-replace-within-package/bar/vendor/composer/autoload_namespaces.php');
        $this->assertCount(2, $bazNamespaces);
        $this->assertEquals(array('Baz\\', 'Bar\\'), array_keys($bazNamespaces));
    }

    public function testBuildProvideExampleProject()
    {
        $build = new Build();
        $build->build(__DIR__ . '/../_fixtures/example-provide');

        $bazNamespaces = include(__DIR__ . '/../_fixtures/example-provide/baz/vendor/composer/autoload_namespaces.php');
        $this->assertCount(2, $bazNamespaces);
        $this->assertEquals(array('Baz\\', 'Bar\\'), array_keys($bazNamespaces));
    }

    public function testBuildWithAdvancedExampleProject()
    {
        $build = new Build();
        $build->build(__DIR__ . '/../_fixtures/example-advanced');

        $barAutoloadReal = file_get_contents(__DIR__ . '/../_fixtures/example-advanced/bar/vendor/composer/autoload_files.php');
        $barIncludeFiles = include(__DIR__ . '/../_fixtures/example-advanced/bar/vendor/composer/autoload_files.php');

        $this->assertEquals(array(realpath(__DIR__ . '/../../') . '/vendor/foo/baz/bin/baz'), array_values($barIncludeFiles));
        $this->assertTrue(strpos($barAutoloadReal, md5('foo/baz:bin/baz')) !== false);
    }

    public function testBuildWithVendorDirExampleProject()
    {
        $build = new Build();
        $build->build(__DIR__ . '/../_fixtures/example-vendordir');

        $barAutoloadReal = file_get_contents(__DIR__ . '/../_fixtures/example-vendordir/bar/vendor/composer/autoload_files.php');
        $barIncludeFiles = include(__DIR__ . '/../_fixtures/example-vendordir/bar/vendor/composer/autoload_files.php');

        $this->assertEquals(array(realpath(__DIR__ . '/../../') . '/different/folder/foo/baz/bin/baz'), array_values($barIncludeFiles));
        $this->assertTrue(strpos($barAutoloadReal, md5('foo/baz:bin/baz')) !== false);
    }

    public function testBuildWithRelativeBinExampleProject()
    {
        $build = new Build();
        $build->build(__DIR__ . '/../_fixtures/example-relbin');

        $link = __DIR__ . '/../_fixtures/example-relbin/baz/vendor/bin/usefulbin';
        $this->assertFileExists($link);
        $this->assertEquals('../../../vendor/foo/bar/bin/usefulbin', readlink($link));
    }

    public function testBuildWithNoDevExampleProject()
    {
        $build = new Build();
        $build->build(__DIR__ . '/../_fixtures/example-nodev', true, true);

        $fooNamespaces = include(__DIR__ . '/../_fixtures/example-nodev/foo/vendor/composer/autoload_namespaces.php');
        $this->assertCount(2, $fooNamespaces);
        $this->assertEquals(array('Foo\\', 'Baz\\'), array_keys($fooNamespaces));
    }

    public function testBuildWithNoDevClassmapAuthoritativeExampleProject()
    {
        $build = new Build();
        $build->build(__DIR__ . '/../_fixtures/example-nodev-classmap-authoritative', true, true, true);

        $fooNamespaces = include(__DIR__ . '/../_fixtures/example-nodev-classmap-authoritative/foo/vendor/composer/autoload_namespaces.php');
        $this->assertCount(2, $fooNamespaces);
        $this->assertEquals(array('Foo\\', 'Baz\\'), array_keys($fooNamespaces));

        $loader = file_get_contents(__DIR__ . '/../_fixtures/example-nodev-classmap-authoritative/foo/vendor/composer/autoload_real.php');
        $this->assertStringContainsString('$loader->setClassMapAuthoritative(true);', $loader);
    }

    public function testBuildWithIncludePathExampleProject()
    {
        $build = new Build();
        $build->build(__DIR__ . '/../_fixtures/example-include-path');

        $includePaths = include(__DIR__ . '/../_fixtures/example-include-path/bar/vendor/composer/include_paths.php');
        $this->assertContains(realpath(__DIR__ . '/../../') . '/bar/lib', $includePaths);
    }

    public function testBuildWithComposerRuntimeApiGeneratesInstalledVersions()
    {
        $build = new Build();
        $build->build(__DIR__ . '/../_fixtures/example-composer-runtime-api');

        $this->assertFileExists(__DIR__ . '/../_fixtures/example-composer-runtime-api/foo/vendor/composer/InstalledVersions.php');
        $this->assertFileExists(__DIR__ . '/../_fixtures/example-composer-runtime-api/foo/vendor/composer/installed.php');
    }

    public function __call($method, $args)
    {
        if ($method === 'assertStringContainsString') {
            call_user_func_array([$this, 'assertContains'], $args);
        }
    }

    /** @after */
    protected function tearDownStuff()
    {
        $fs = new Filesystem();
        $dirs = glob(__DIR__ . '/../_fixtures/*/*/vendor');
        foreach ($dirs as $dir) {
            $fs->removeDirectoryPhp($dir);
        }
    }
}
