<?php

declare(strict_types=1);

use pocketmine\utils\Git;

require dirname(__DIR__) . '/vendor/autoload.php';

/**
 * @param string[] $strings
 *
 * @return string[]
 */
function preg_quote_array(array $strings, string $delim) : array{
    return array_map(static function (string $str) use ($delim) : string {
        return preg_quote($str, $delim);
    }, $strings);
}

/**
 * @param string[] $includedPaths
 * @param mixed[]  $metadata
 * @phpstan-param array<string, mixed> $metadata
 *
 * @return \Generator|string[]
 */
function buildPhar(string $pharPath, string $basePath, array $includedPaths, array $metadata, string $stub, int $signatureAlgo = \Phar::SHA1, ?int $compression = null){
    $basePath = rtrim(str_replace("/", DIRECTORY_SEPARATOR, $basePath), DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR;
    $includedPaths = array_map(static function (string $path) : string {
        return rtrim(str_replace("/", DIRECTORY_SEPARATOR, $path), DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR;
    }, $includedPaths);

    yield "Creating output file $pharPath";
    if (file_exists($pharPath)) {
        yield "Phar file already exists, overwriting...";
        try {
            \Phar::unlinkArchive($pharPath);
        } catch (\PharException $e) {
            //unlinkArchive() doesn't like dodgy phars
            unlink($pharPath);
        }
    }

    yield "Adding files...";

    $start = microtime(true);
    $phar = new \Phar($pharPath);
    $phar->setMetadata($metadata);
    $phar->setStub($stub);
    $phar->setSignatureAlgorithm($signatureAlgo);
    $phar->startBuffering();

    // If paths contain any of these, they will be excluded
    $excludedSubstrings = preg_quote_array([
        realpath($pharPath), // Don't add the phar to itself
    ], '/');

    $folderPatterns = preg_quote_array([
        DIRECTORY_SEPARATOR . 'tests' . DIRECTORY_SEPARATOR,
        DIRECTORY_SEPARATOR . '.' //"Hidden" files, git dirs etc
    ], '/');

    // Only exclude these within the basedir, otherwise the project won't get built if it itself is in a directory that matches these patterns
    $basePattern = preg_quote(rtrim($basePath, DIRECTORY_SEPARATOR), '/');
    foreach ($folderPatterns as $p) {
        $excludedSubstrings[] = $basePattern . '.*' . $p;
    }

    $regex = sprintf('/^(?!.*(%s))^%s(%s).*/i',
        implode('|', $excludedSubstrings), // String may not contain any of these substrings
        preg_quote($basePath, '/'), // String must start with this path...
        implode('|', preg_quote_array($includedPaths, '/')) // ... and must be followed by one of these relative paths, if any were specified. If none, this will produce a null capturing group which will allow anything.
    );

    $directory = new \RecursiveDirectoryIterator($basePath, \FilesystemIterator::SKIP_DOTS | \FilesystemIterator::FOLLOW_SYMLINKS | \FilesystemIterator::CURRENT_AS_PATHNAME); // can't use fileinfo because of symlinks
    $iterator = new \RecursiveIteratorIterator($directory);
    $regexIterator = new \RegexIterator($iterator, $regex);

    $count = count($phar->buildFromIterator($regexIterator, $basePath));
    yield "Added $count files";

    if ($compression !== null) {
        yield "Compressing files...";
        $phar->compressFiles($compression);
        yield "Finished compression";
    }
    $phar->stopBuffering();

    // Set the read-only flag on the PHAR file
    chmod($pharPath, 0555);

    yield "Done in " . round(microtime(true) - $start, 3) . "s";
}

function main() : void {
    if (ini_get("phar.readonly") == 1) {
        echo "Set phar.readonly to 0 with -dphar.readonly=0" . PHP_EOL;
        exit(1);
    }

    if (file_exists(dirname(__DIR__) . '/vendor/phpunit')) {
        echo "Remove Composer dev dependencies before building (composer install --no-dev)" . PHP_EOL;
        exit(1);
    }

    $opts = getopt("", ["out:", "git:", "build:"]);
    if (isset($opts["git"])) {
        $gitHash = $opts["git"];
    } else {
        $gitHash = 'unknown';
        echo "Git hash set to 'unknown'" . PHP_EOL;
    }

    if (isset($opts["build"])) {
        $build = (int) $opts["build"];
    } else {
        $build = 0;
    }

    foreach (buildPhar(
                 $opts["out"] ?? getcwd() . DIRECTORY_SEPARATOR . "AntiBadPackets.phar",
                 dirname(__DIR__) . DIRECTORY_SEPARATOR,
                 [
                 ],
                 [
                     'git' => $gitHash,
                     'build' => $build,
                 ],
                 <<<'STUB'
<?php

__HALT_COMPILER();
STUB
                 ,
                 \Phar::SHA1,
                 \Phar::GZ
             ) as $line) {
        echo $line . PHP_EOL;
    }
}

main();